Search Apps Documentation Source Content File Folder Download Copy Actions Download

doc.gno

6.86 Kb · 169 lines
  1// Package store provides a domain-specific key-value storage system with
  2// permission-based write access control for Gno smart contracts.
  3//
  4// ## Overview
  5//
  6// Each domain (e.g., pool, position, router, staker) creates its own KVStore
  7// instance bound to a unique domain address. That domain address is the
  8// primary authority over the store: it can write, manage the authorized-writer
  9// set, and is itself implicitly authorized as a writer.
 10//
 11// Key components of this package:
 12//
 13//  1. **KVStore Interface** - The contract for domain-specific storage operations.
 14//  2. **kvStore Implementation** - Concrete implementation with permission
 15//     management and data storage.
 16//  3. **Write-only Permission System** - Reads are public within the package;
 17//     only writes (Set, Delete, ACL changes) are gated.
 18//  4. **Type-Safe Getters** - Typed getter methods with runtime type casting
 19//     and validation.
 20//
 21// ## Key Features
 22//
 23//   - **Domain Isolation**: Each domain has its own isolated storage space
 24//     identified by its domain address. Keys are internally prefixed with that
 25//     address so domains cannot collide.
 26//   - **Per-Realm Write ACL**: The store maintains an explicit set of realms
 27//     authorized to write. The domain owner is in the set by default.
 28//   - **Type-Safe Operations**: Specialized getters (`GetInt64`, `GetString`,
 29//     `GetAddress`, ...) with automatic casting and structured errors.
 30//   - **Realm-Frame Auth Checks**: Every write call accepts a `realm` value
 31//     representing the live crossing frame; the store verifies that frame
 32//     before consulting the ACL, defending against replayed or captured
 33//     `realm` values.
 34//
 35// ## Permission Model
 36//
 37// There is exactly one assignable permission level:
 38//
 39//   - **Write**: Allowed to call `Set` and `Delete`.
 40//
 41// The zero value of `Permission` is reserved and rejected by registration APIs.
 42// The domain address is always treated as authorized and cannot be removed.
 43//
 44// ## Realm Threading
 45//
 46// Every mutating method takes two leading parameters:
 47//
 48//	func (k *kvStore) Set(_ int, rlm realm, key string, value any) error
 49//
 50// The leading `_ int` is a deliberate sentinel: at the call site it shows up
 51// as a `0`, making it visually obvious that a `realm` value is being threaded
 52// through. `rlm` is the live crossing frame, and `rlm.IsCurrent()` is checked
 53// first in every mutating method to reject stale or spoofed tokens.
 54//
 55// The v1 -> v2 mapping for the two realm-identity expressions is straightforward:
 56//
 57//   - `runtime.CurrentRealm().Address()` -> `rlm.Address()` (the current realm)
 58//   - `runtime.PreviousRealm().Address()` -> `rlm.Previous().Address()` (the caller)
 59//
 60// Which one a given method checks is a per-method domain decision, not a v2
 61// framework rule. The store currently checks:
 62//
 63//   - `Set` / `Delete`: `rlm.Previous().Address()` (the caller) against the
 64//     write ACL. Set additionally bypasses the ACL when `rlm.IsCode()` is
 65//     false so EOA / user-realm callers (deploy, tests) can populate state
 66//     without prior registration; Delete has no such bypass.
 67//   - `AddAuthorizedCaller`, `UpdateAuthorizedCaller`,
 68//     `RemoveAuthorizedCaller`: `rlm.Address()` (the current realm) against
 69//     the domain address.
 70//
 71// ## Workflow
 72//
 73//  1. **Initialization**: Create a store with `NewKVStore(domainAddress)`.
 74//  2. **Data Operations**: Use `Set` / `Get` (and typed getters) for I/O.
 75//  3. **Permission Management**: From the domain realm, call
 76//     `AddAuthorizedCaller` to grant write access to additional realms.
 77//  4. **Type-Safe Retrieval**: Prefer typed getters (`GetInt64`, `GetString`,
 78//     etc.) over raw `Get` whenever the stored type is known.
 79//
 80// ## Example Usage
 81//
 82// ```gno
 83// package examplerealm
 84//
 85// import (
 86//
 87//	"gno.land/p/gnoswap/store"
 88//
 89// )
 90//
 91//	func Configure(cur realm, routerAddr address) error {
 92//	    // Create a KVStore owned by the current (pool) realm.
 93//	    kv := store.NewKVStore(cur.Address())
 94//
 95//	    // Persist some values. `0, cur` threads the live realm frame through.
 96//	    if err := kv.Set(0, cur, "totalLiquidity", uint64(1_000_000)); err != nil {
 97//	        return err
 98//	    }
 99//	    if err := kv.Set(0, cur, "poolName", "ETH-USDC"); err != nil {
100//	        return err
101//	    }
102//
103//	    // Grant write access to the router realm.
104//	    if err := kv.AddAuthorizedCaller(0, cur, routerAddr, store.Write); err != nil {
105//	        return err
106//	    }
107//
108//	    // Reads are public -- no realm needed.
109//	    liquidity, err := kv.GetUint64("totalLiquidity")
110//	    if err != nil {
111//	        return err
112//	    }
113//	    _ = liquidity
114//
115//	    return nil
116//	}
117//
118// ```
119//
120// ## Permission Checks at a Glance
121//
122//   - **Read Operations** (`Get`, `GetInt64`, `GetString`, ...): no permission
123//     check; reads are public within the package.
124//   - **Write Operations** (`Set`, `Delete`): require the caller --
125//     `rlm.Previous().Address()` -- to be in the write ACL. Set additionally
126//     bypasses the ACL when the immediate caller is an EOA / user realm
127//     (`!rlm.IsCode()`); Delete has no such bypass.
128//   - **Management Operations** (`AddAuthorizedCaller`,
129//     `UpdateAuthorizedCaller`, `RemoveAuthorizedCaller`): require the
130//     current realm -- `rlm.Address()` -- to equal the domain address.
131//
132// ## Key Prefixing
133//
134// Internally, all keys are prefixed with the domain address to keep domains
135// isolated:
136//
137//	Stored key: "{domainAddress}:{userKey}"
138//
139// This prevents key collisions between domains that share a runtime.
140//
141// ## Errors
142//
143//   - `ErrKeyNotFound` - Requested key does not exist.
144//   - `ErrWritePermissionDenied` - the caller (`rlm.Previous().Address()`) is
145//     not in the write ACL. For Set, this only fires when `rlm.IsCode()` is
146//     true; the EOA / user-realm path bypasses the ACL.
147//   - `ErrUpdatePermissionDenied` - ACL change attempted from a current realm
148//     (`rlm.Address()`) other than the domain address.
149//   - `ErrAuthorizedCallerAlreadyRegistered` - Adding a caller that already
150//     exists.
151//   - `ErrAuthorizedCallerNotFound` - Updating or removing a caller that was
152//     never registered.
153//   - `ErrInvalidPermission` - Permission value other than `Write` was passed.
154//   - `ErrFailedCast` - Typed getter saw a value whose runtime type does not
155//     match.
156//   - `errSpoofedRealm` - The supplied `realm` is not the live crossing frame.
157//
158// ## Limitations and Considerations
159//
160//   - All stored values are of type `any`, so retrieval requires either a
161//     typed getter or a cast.
162//   - ACL management can only be performed by the domain realm itself.
163//   - The domain address is implicitly authorized and cannot be removed.
164//   - Keys are automatically prefixed; callers should not encode the domain
165//     address into their own keys.
166//
167// Package store is intended for use in Gno smart contracts that need
168// isolated, write-gated storage for distinct protocol components.
169package store