Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a9741cf
feat: add geo-resto package
recule556688 Oct 21, 2025
d6ea2b8
fix: add missing newline at end of files in geo-resto package
recule556688 Oct 21, 2025
4099c86
style: format geo-resto package with gno fmt
recule556688 Oct 21, 2025
96aa0ce
feat: add new event types and update event participation method names
recule556688 Oct 23, 2025
a286517
refactor: replace std.Address with address type across geo-resto package
recule556688 Oct 23, 2025
fc682d3
test: add unit tests for transaction link generation in geo-resto pac…
recule556688 Oct 29, 2025
0ef3949
feat: add string parsing helpers and transaction link generation for …
recule556688 Oct 29, 2025
e3df41a
feat: add quick action links for adding locations and checking in to …
recule556688 Oct 29, 2025
3297a28
refactor: remove obsolete transaction link tests from geo-resto package
recule556688 Nov 11, 2025
3004e91
refactor: streamline AuthManager by replacing maps with AVL trees for…
recule556688 Nov 11, 2025
7c7c006
refactor: replace maps with AVL trees in EventManager
recule556688 Nov 11, 2025
29a47eb
refactor: replace maps with AVL trees in LocationManager
recule556688 Nov 11, 2025
10e7201
refactor: replace maps with AVL trees in VisitManager
recule556688 Nov 11, 2025
50c4472
refactor: simplify event creation by removing redundant password para…
recule556688 Nov 11, 2025
d436ed4
refactor: remove password parameter from event-related functions for …
recule556688 Nov 11, 2025
44fa137
refactor: remove password check from event joining test for simplific…
recule556688 Nov 11, 2025
7f49c46
refactor: adjust formatting
recule556688 Nov 11, 2025
17e1263
refactor: enhance error handling in location and event functions; add…
recule556688 Nov 13, 2025
4b3368c
refactor: implement trusted verifier management; add functions to add…
recule556688 Nov 16, 2025
6350c6c
test: add unit tests for trusted verifier management functionality
recule556688 Nov 17, 2025
6cd9afc
refactor: enforce owner-only access checks in AddTrustedVerifier and …
recule556688 Nov 17, 2025
865cb24
refactor: add owner-only actions for managing trusted verifiers in re…
recule556688 Nov 17, 2025
7981ebd
fix: fixing the coding style
recule556688 Nov 19, 2025
0175a2d
refactor: remove obsolete 'zkproof is hard' documentation file
recule556688 Nov 19, 2025
92d9303
refactor: replace address truncation with name resolution for user vi…
recule556688 Nov 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/r/karma1337/geo-resto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 🌍 Geo-Resto

A Gno-based realm for creating a decentralized, on-chain record of real-world places and visits.

## Core Features

- **Location Management**: Add and verify geographic locations, creating a permanent, user-owned registry.
- **Proof of Presence**: Check-in at locations using a secure challenge-response mechanism to prove you were there.
- **Event System**: Organize location-based events like meetups, airdrops, or community gatherings.
- **QR Code Verification**: A robust system for event organizers to securely verify attendee presence in real-time.
- **On-Chain History**: All data is immutable, timestamped, and verifiable on the Gno blockchain.

## How It Works

The realm is organized into modules that handle specific tasks:

- `geo_resto.gno`: The main entry point and public API.
- `location.gno`: Manages location data.
- `visit.gno`: Handles check-ins and visit history.
- `event.gno`: Manages the event lifecycle.
- `auth.gno`: Secures the system with rate-limiting, access control, and verification logic.
- `renderer.gno`: Renders the web interface.

## Quick Start

### Add a Location
```gno
AddLocation("Eiffel Tower", "Iconic tower in Paris", 48.8584, 2.2945, "landmark")
```

### Check-In at a Location
1. **Get the challenge:**
```gno
GetLocationChallenge("loc_1")
```
2. **Submit the proof (challenge response):**
```gno
CheckIn("loc_1", "proof_from_challenge")
```

### Create an Event
```gno
CreateEvent("loc_1", "Tech Meetup", "Weekly discussion", "", 1, startTime, endTime)
```

## Event QR Code Verification

A secure system for proving event attendance.

- **Organizers**: Create an event to get a QR code. At the event, generate temporary verification codes for attendees using `GenerateAttendeeCode("event_1")`.
- **Attendees**: Get verified by the organizer.
- **Stats**: Organizers can track attendance with `GetVerifiedAttendees("event_1")` and `GetEventVerificationStats("event_1")`.

## Future Ideas

- **Zero-Knowledge Proofs**: For privacy-preserving check-ins.
- **Community Governance**: Location ratings, reviews, and decentralized moderation.
- **Enhanced Visuals**: Richer map interfaces and data visualizations.

## Testing

Run the full test suite with:
```bash
gno test
```

264 changes: 264 additions & 0 deletions packages/r/karma1337/geo-resto/auth.gno
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file and functionality already exists in packages like p/nt/ownable & gno.land/p/nt/ownable/exts/authorizable. Please remove this code and use existing libraries. This will shorten your code, make it more readable, and increase the reusability value and trust of your code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still unresolved

Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
package georesto

import (
"crypto/sha256"
"encoding/hex"
"math"
"strconv"
"time"
)

// AuthManager handles access control and proof verification
type AuthManager struct {
trustedVerifiers map[string]bool // Trusted verifier addresses
adminAddresses map[string]bool // Admin addresses
rateLimitTracker map[string]int64 // Tracks last action time for a user and action
accessTokens map[string]int64 // token -> expiration time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not use map in Gno. Please switch to avl.Tree

}

// NewAuthManager creates a new authentication manager instance
func NewAuthManager() *AuthManager {
return &AuthManager{
trustedVerifiers: make(map[string]bool),
adminAddresses: make(map[string]bool),
rateLimitTracker: make(map[string]int64),
accessTokens: make(map[string]int64),
}
}

// AddTrustedVerifier adds a trusted verifier address
func (am *AuthManager) AddTrustedVerifier(verifierAddress string, admin address) bool {
if !am.IsAdmin(admin.String()) {
return false
}

am.trustedVerifiers[verifierAddress] = true
return true
}

// RemoveTrustedVerifier removes a trusted verifier address
func (am *AuthManager) RemoveTrustedVerifier(verifierAddress string, admin address) bool {
if !am.IsAdmin(admin.String()) {
return false
}

delete(am.trustedVerifiers, verifierAddress)
return true
}

// IsTrustedVerifier checks if an address is a trusted verifier
func (am *AuthManager) IsTrustedVerifier(address string) bool {
return am.trustedVerifiers[address]
}

// AddAdmin adds an admin address
func (am *AuthManager) AddAdmin(adminAddress string, currentAdmin address) bool {
// Only existing admins can add new admins
if len(am.adminAddresses) > 0 && !am.IsAdmin(currentAdmin.String()) {
return false
}

am.adminAddresses[adminAddress] = true
return true
}

// IsAdmin checks if an address is an admin
func (am *AuthManager) IsAdmin(address string) bool {
return am.adminAddresses[address]
}

// VerifyLocationProof verifies a cryptographic proof for location check-in
func (am *AuthManager) VerifyLocationProof(userAddress, locationID, proof string, timestamp int64) bool {
// In a real implementation, this would involve more complex verification.
// For now, we'll check if the proof is a valid challenge response.
return am.VerifyLocationChallenge(locationID, proof)
}

// GenerateLocationProof generates a proof for location check-in
func (am *AuthManager) GenerateLocationProof(userAddress, locationID string, timestamp int64) string {
return am.generateLocationProof(userAddress, locationID, timestamp)
}

// VerifyEventAccess verifies access to a password-protected event
func (am *AuthManager) VerifyEventAccess(eventID, password string, user address) bool {
event := eventManager.GetEvent(eventID)
if event == nil {
return false
}

// If no password required, access is granted
if event.Password == "" {
return true
}

// Verify password
hashedPassword := am.hashString(password)
return hashedPassword == event.Password
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in the password like this will make it available to everyone as transaction data is public. Please figure out a different way to do this.


// CanModifyLocation checks if a user can modify a location
func (am *AuthManager) CanModifyLocation(locationID string, user address) bool {
location := locationManager.GetLocation(locationID)
if location == nil {
return false
}

userAddr := user.String()

// Location creator can always modify
if location.Creator.String() == userAddr {
return true
}

// Admins can modify any location
if am.IsAdmin(userAddr) {
return true
}

// Trusted verifiers can modify for verification purposes
if am.IsTrustedVerifier(userAddr) {
return true
}

return false
}

// CanModifyEvent checks if a user can modify an event
func (am *AuthManager) CanModifyEvent(eventID string, user address) bool {
event := eventManager.GetEvent(eventID)
if event == nil {
return false
}

userAddr := user.String()

// Event creator can always modify
if event.Creator.String() == userAddr {
return true
}

// Admins can modify any event
if am.IsAdmin(userAddr) {
return true
}

return false
}

// ValidateCoordinates validates GPS coordinates
func (am *AuthManager) ValidateCoordinates(latitude, longitude float64) bool {
return latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180
}

// ValidateProximity checks if user is within acceptable range of location
func (am *AuthManager) ValidateProximity(userLat, userLon, locationLat, locationLon, maxDistanceKm float64) bool {
distance := haversine(userLat, userLon, locationLat, locationLon)
return distance <= maxDistanceKm
}

// GenerateAccessToken generates a temporary access token for API access
func (am *AuthManager) GenerateAccessToken(userAddress string, expirationTime int64) string {
data := userAddress + ":" + strconv.FormatInt(expirationTime, 10)
hash := sha256.Sum256([]byte(data))
token := hex.EncodeToString(hash[:])
am.accessTokens[token] = expirationTime
return token
}

// VerifyAccessToken verifies an access token
func (am *AuthManager) VerifyAccessToken(token, userAddress string, currentTime int64) bool {
expirationTime, exists := am.accessTokens[token]
if !exists {
return false // Token does not exist
}

if currentTime > expirationTime {
delete(am.accessTokens, token) // Clean up expired token
return false // Token has expired
}

// Optional: Verify that the token was generated for the correct userAddress.
// This would require storing the userAddress with the token or regenerating
// the token to check for a match. For now, we'll keep it simple.

return true
}

// CheckRateLimit implements basic rate limiting for API calls
func (am *AuthManager) CheckRateLimit(userAddress string, action string, cooldownSeconds int64) bool {
key := userAddress + ":" + action
lastActionTime, exists := am.rateLimitTracker[key]

currentTime := time.Now().Unix()

if exists && (currentTime-lastActionTime) < cooldownSeconds {
return false // Rate limit exceeded
}

am.rateLimitTracker[key] = currentTime
return true
}

// Helper methods

func (am *AuthManager) generateLocationProof(userAddress, locationID string, timestamp int64) string {
data := userAddress + ":" + locationID + ":" + strconv.FormatInt(timestamp, 10)
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:])
}

func (am *AuthManager) hashString(input string) string {
hash := sha256.Sum256([]byte(input))
return hex.EncodeToString(hash[:])
}

// VerifyZKProof verifies a zero-knowledge proof (placeholder for future implementation)
func (am *AuthManager) VerifyZKProof(proof string, publicInputs []string) bool {
// Placeholder for ZK proof verification
// In a real implementation, this would verify a zk-SNARK or zk-STARK proof
// allowing users to prove they were at a location without revealing exact coordinates
return len(proof) > 0 && len(publicInputs) > 0
}

// GenerateLocationChallenge generates a challenge for location verification
func (am *AuthManager) GenerateLocationChallenge(locationID string) string {
// Generate a time-based challenge that can be solved by someone physically present
timestamp := time.Now().Unix()
data := locationID + ":" + strconv.FormatInt(timestamp/300, 10) // 5-minute windows
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:8]) // Return first 8 bytes as challenge
}

// VerifyLocationChallenge verifies a location challenge response
func (am *AuthManager) VerifyLocationChallenge(locationID, response string) bool {
// Check if response matches any of the last few time windows (for clock drift tolerance)
currentTime := time.Now().Unix()
for i := 0; i < 3; i++ { // Check current and previous 2 windows
timeWindow := (currentTime / 300) - int64(i)
data := locationID + ":" + strconv.FormatInt(timeWindow, 10)
hash := sha256.Sum256([]byte(data))
expectedResponse := hex.EncodeToString(hash[:8])

if response == expectedResponse {
return true
}
}

return false
}

// haversine calculates the distance between two points on Earth.
func haversine(lat1, lon1, lat2, lon2 float64) float64 {
const R = 6371 // Earth radius in kilometers
dLat := (lat2 - lat1) * (math.Pi / 180.0)
dLon := (lon2 - lon1) * (math.Pi / 180.0)
lat1Rad := lat1 * (math.Pi / 180.0)
lat2Rad := lat2 * (math.Pi / 180.0)

a := math.Sin(dLat/2)*math.Sin(dLat/2) +
math.Cos(lat1Rad)*math.Cos(lat2Rad)*
math.Sin(dLon/2)*math.Sin(dLon/2)
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))

return R * c
}
Loading
Loading