Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

doc.gno

7.42 Kb · 180 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//   - **Authorized Caller Management**: Dynamic addition, update, and removal
 35//     of authorized callers with specific permissions.
 36//
 37// ## Permission Model
 38//
 39// The package defines one assignable permission level:
 40//
 41//   - **Write (1)**: Allowed to call `Set` and `Delete`.
 42//
 43// The zero value of `Permission` is reserved and rejected by registration APIs.
 44// Code callers not in the authorized-caller table have no write access. Current
 45// user/EOA realms bypass the ACL for Set and Delete after the live-frame check.
 46// Read methods do not enforce caller permissions; realm-level APIs must gate
 47// sensitive reads themselves. The domain address (owner) is always treated as
 48// authorized and cannot be removed.
 49//
 50// ## Realm Threading
 51//
 52// Every mutating method takes two leading parameters:
 53//
 54//	func (k *kvStore) Set(_ int, rlm realm, key string, value any) error
 55//
 56// The leading `_ int` is a deliberate sentinel: at the call site it shows up
 57// as a `0`, making it visually obvious that a `realm` value is being threaded
 58// through. `rlm` is the live crossing frame, and `rlm.IsCurrent()` is checked
 59// first in every mutating method to reject stale or spoofed tokens.
 60//
 61// The v1 -> v2 mapping for the two realm-identity expressions is straightforward:
 62//
 63//   - `runtime.CurrentRealm().Address()` -> `rlm.Address()` (the current realm)
 64//   - `runtime.PreviousRealm().Address()` -> `rlm.Previous().Address()` (the caller)
 65//
 66// Which one a given method checks is a per-method domain decision, not a v2
 67// framework rule. The store currently checks:
 68//
 69//   - `Set` / `Delete`: `rlm.Address()` (the realm performing the write, i.e.
 70//     the domain realm calling into the store) against the write ACL for code
 71//     callers. Current user/EOA realms (`!rlm.IsCode()`) bypass the ACL after
 72//     `rlm.IsCurrent()` validation, so they can populate or delete state.
 73//   - `AddAuthorizedCaller`, `UpdateAuthorizedCaller`,
 74//     `RemoveAuthorizedCaller`: `rlm.Address()` (the current realm) against
 75//     the domain address.
 76//
 77// ## Workflow
 78//
 79//  1. **Initialization**: Create a store with `NewKVStore(domainAddress)`.
 80//  2. **Data Operations**: Use `Set` / `Get` (and typed getters) for I/O.
 81//  3. **Permission Management**: From the domain realm, call
 82//     `AddAuthorizedCaller` to grant write access to additional realms.
 83//  4. **Type-Safe Retrieval**: Prefer typed getters (`GetInt64`, `GetString`,
 84//     etc.) over raw `Get` whenever the stored type is known.
 85//
 86// ## Example Usage
 87//
 88// ```gno
 89// package examplerealm
 90//
 91// import (
 92//
 93//	"gno.land/p/gnoswap/store/v1"
 94//
 95// )
 96//
 97//	func Configure(cur realm, routerAddr address) error {
 98//	    // Create a KVStore owned by the current (pool) realm.
 99//	    kv := store.NewKVStore(cur.Address())
100//
101//	    // Persist some values. `0, cur` threads the live realm frame through.
102//	    if err := kv.Set(0, cur, "totalLiquidity", uint64(1_000_000)); err != nil {
103//	        return err
104//	    }
105//	    if err := kv.Set(0, cur, "poolName", "ETH-USDC"); err != nil {
106//	        return err
107//	    }
108//
109//	    // Grant write access to the router realm.
110//	    if err := kv.AddAuthorizedCaller(0, cur, routerAddr, store.Write); err != nil {
111//	        return err
112//	    }
113//
114//	    // Reads are public -- no realm needed.
115//	    liquidity, err := kv.GetUint64("totalLiquidity")
116//	    if err != nil {
117//	        return err
118//	    }
119//	    _ = liquidity
120//
121//	    return nil
122//	}
123//
124// ```
125//
126// ## Permission Checks at a Glance
127//
128// The store automatically verifies permissions during operations:
129//
130//   - **Read Operations** (`Get`, `GetInt64`, `GetString`, ...): no permission
131//     check; reads are public within the package.
132//   - **Write Operations** (`Set`, `Delete`): require the calling realm --
133//     `rlm.Address()` -- to be in the write ACL when the caller is code.
134//     Current user/EOA realms (`!rlm.IsCode()`) bypass the ACL after the live
135//     frame check.
136//   - **Management Operations** (`AddAuthorizedCaller`,
137//     `UpdateAuthorizedCaller`, `RemoveAuthorizedCaller`): require the
138//     current realm -- `rlm.Address()` -- to equal the domain address.
139//
140// ## Key Prefixing
141//
142// Internally, all keys are prefixed with the domain address to keep domains
143// isolated:
144//
145//	Stored key: "{domainAddress}:{userKey}"
146//
147// This prevents key collisions between domains that share a runtime.
148//
149// ## Errors
150//
151// The package defines several error types:
152//
153//   - `ErrWritePermissionDenied` - the calling code realm (`rlm.Address()`) is
154//     not in the write ACL. Current user/EOA realms bypass the ACL for Set and
155//     Delete after `rlm.IsCurrent()` validation.
156//   - `ErrUpdatePermissionDenied` - ACL change attempted from a current realm
157//     (`rlm.Address()`) other than the domain address.
158//   - `ErrAuthorizedCallerAlreadyRegistered` - Adding a caller that already
159//     exists.
160//   - `ErrAuthorizedCallerNotFound` - Updating or removing a caller that was
161//     never registered.
162//   - `ErrInvalidPermission` - Permission value other than `Write` was passed.
163//   - `ErrFailedCast` - Typed getter saw a value whose runtime type does not
164//     match.
165//   - `ErrSpoofedRealm` - The supplied `realm` is not the live crossing frame.
166//
167// ## Limitations and Considerations
168//
169//   - All stored values are of type `any`, so retrieval requires either a
170//     typed getter or a cast.
171//   - Read methods are not permission-gated. Add realm-level checks for
172//     sensitive reads.
173//   - ACL management can only be performed by the domain realm itself.
174//   - The domain address is implicitly authorized and cannot be removed.
175//   - Keys are automatically prefixed; callers should not encode the domain
176//     address into their own keys.
177//
178// Package store is intended for use in Gno smart contracts that need
179// isolated, write-gated storage for distinct protocol components.
180package store