Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions 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