Search Apps Documentation Source Content File Folder Download Copy Actions Download

doc.gno

10.69 Kb · 318 lines
  1// Package version_manager provides a runtime version management system for dynamic
  2// implementation switching without data migration. It implements the Strategy Pattern
  3// combined with Plugin Architecture to enable hot-swapping between different versioned
  4// implementations of the same domain.
  5//
  6// ## Overview
  7//
  8// Version Manager enables seamless upgrades by allowing multiple versioned implementations
  9// (v1, v2, v3) to coexist and share a unified storage layer. The domain
 10// (proxy) realm owns that storage; the manager records version initializers and
 11// swaps the active implementation reference without granting implementation
 12// realms direct write permission.
 13//
 14// Key components of this package include:
 15//
 16//  1. **VersionManager Interface**: Defines the contract for managing multiple versioned
 17//     implementations of a domain.
 18//  2. **versionManager Implementation**: Concrete implementation that manages version
 19//     registration and active implementation switching.
 20//  3. **Realm-Threaded Validation**: Validates live realm tokens and restricts
 21//     registration to packages under the configured domain path.
 22//  4. **Domain-Scoped Security**: Ensures only authorized packages within the domain
 23//     path can register implementations.
 24//
 25// ## Key Features
 26//
 27//   - **Zero-Downtime Upgrades**: Switch implementations at runtime without service
 28//     interruption or data migration.
 29//   - **Unified Storage**: All versions share a single KVStore owned by the
 30//     domain (proxy) realm, eliminating migration overhead.
 31//   - **Hot-Swapping**: Instant version switching through dynamic strategy replacement
 32//     with explicit realm validation.
 33//   - **Domain-Scoped Security**: Only packages under the authorized domain path can
 34//     register implementations, preventing unauthorized access.
 35//   - **Backward Compatibility**: Previous versions remain registered for
 36//     gradual migration and rollback support.
 37//   - **Strategy Pattern**: Enables runtime algorithm swapping without code changes.
 38//
 39// ## Architecture Pattern
 40//
 41// The package implements two complementary design patterns:
 42//
 43//   - **Strategy Pattern**: Enables runtime selection of implementation strategies
 44//   - **Plugin Architecture**: Supports dynamic loading and registration of versions
 45//
 46// ## Workflow
 47//
 48// Typical usage of the version_manager package includes the following steps:
 49//
 50//  1. **Initialization**: Create a version manager for the domain using NewVersionManager.
 51//  2. **Version Registration**: Each version (v1, v2, v3) calls RegisterInitializer during
 52//     its init(cur realm) function to register its implementation.
 53//  3. **Active Implementation**: The first registered version becomes the active implementation;
 54//     subsequent versions are retained for later switching.
 55//  4. **Version Switching**: Use ChangeImplementation to hot-swap to a different version
 56//     while storage ownership stays with the domain KVStore.
 57//
 58// ## Example Usage
 59//
 60// ### Step 1: Define Domain Interface
 61//
 62// ```gno
 63// // protocol_fee/types.gno
 64// package protocol_fee
 65//
 66//	type ProtocolFee interface {
 67//	    SetFeeRatio(ratio uint64) error
 68//	    GetFeeRatio() uint64
 69//	}
 70//
 71// ```
 72//
 73// ### Step 2: Create Version Manager
 74//
 75// ```gno
 76// // protocol_fee/protocol_fee.gno
 77// package protocol_fee
 78//
 79// import (
 80//
 81//	"gno.land/p/gnoswap/version_manager"
 82//	"gno.land/p/gnoswap/store"
 83//
 84// )
 85//
 86//	var manager version_manager.VersionManager
 87//
 88//	func init(cur realm) {
 89//	    kvStore := store.NewKVStore(cur.Address())
 90//
 91//	    manager = version_manager.NewVersionManager(
 92//	        cur.PkgPath(),
 93//	        kvStore,
 94//	        func(_ int, rlm realm, kv store.KVStore) any {
 95//	            return NewProtocolFeeStore(kv)
 96//	        },
 97//	    )
 98//	}
 99//
100//	func GetManager() version_manager.VersionManager {
101//	    return manager
102//	}
103//
104// ```
105//
106// ### Step 3: Implement Version 1
107//
108// ```gno
109// // protocol_fee/v1/v1.gno
110// package v1
111//
112// import "gno.land/r/gnoswap/protocol_fee"
113//
114// type protocolFeeV1 struct {
115//
116//	store any
117//
118// }
119//
120//	func init(cur realm) {
121//	    // Register this version during package initialization.
122//	    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
123//	        return &protocolFeeV1{store: store}
124//	    })
125//	}
126//
127//	func (pf *protocolFeeV1) SetFeeRatio(ratio uint64) error {
128//	    // v1 implementation
129//	    return nil
130//	}
131//
132//	func (pf *protocolFeeV1) GetFeeRatio() uint64 {
133//	    // v1 implementation
134//	    return 0
135//	}
136//
137// ```
138//
139// ### Step 4: Implement Version 2
140//
141// ```gno
142// // protocol_fee/v2/v2.gno
143// package v2
144//
145// import "gno.land/r/gnoswap/protocol_fee"
146//
147// type protocolFeeV2 struct {
148//
149//	store any
150//
151// }
152//
153//	func init(cur realm) {
154//	    // Register v2 - inactive until explicitly activated.
155//	    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
156//	        return &protocolFeeV2{store: store}
157//	    })
158//	}
159//
160//	func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
161//	    // v2 improved implementation
162//	    return nil
163//	}
164//
165//	func (pf *protocolFeeV2) GetFeeRatio() uint64 {
166//	    // v2 improved implementation
167//	    return 0
168//	}
169//
170// ```
171//
172// ### Step 5: Use Active Implementation
173//
174// ```gno
175// // client code
176// import "gno.land/r/gnoswap/protocol_fee"
177//
178//	func UseFee() {
179//	    manager := protocol_fee.GetManager()
180//	    impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)
181//
182//	    ratio := impl.GetFeeRatio()
183//	    // Use the active version's implementation
184//	}
185//
186// ```
187//
188// ### Step 6: Switch Versions at Runtime
189//
190// ```gno
191// // governance or admin function
192//
193//	func UpgradeToV2(cur realm) {
194//	    // Hot-swap to v2 - zero downtime.
195//	    protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
196//	}
197//
198// ```
199//
200// ## Registration Flow
201//
202// The version registration process follows this sequence:
203//
204//  1. Domain package initializes version manager with KVStore
205//     ↓
206//  2. v1 package calls the domain registration wrapper during init(cur realm)
207//     → Becomes the active implementation
208//     ↓
209//  3. v2 package calls the domain registration wrapper during init(cur realm)
210//     → Registered for later activation
211//     ↓
212//  4. v3 package calls the domain registration wrapper during init(cur realm)
213//     → Registered for later activation
214//
215// ## Version Switching Flow
216//
217// When switching versions, the following steps occur:
218//
219//  1. Admin/governance calls the domain upgrade wrapper
220//     ↓
221//  2. The domain wrapper enforces authorization and calls ChangeImplementation(0, cur, ...)
222//     ↓
223//  3. Version Manager validates the live realm token and retrieves v2's initializer
224//     ↓
225//  4. Executes v2 initializer with the shared KVStore
226//     ↓
227//  5. Updates currentPackagePath and currentImplementation
228//
229// ## Storage Access Model
230//
231// The version manager keeps storage ownership with the domain (proxy) realm:
232//
233//   - **Domain Ownership**: The domain realm owns the KVStore and drives writes.
234//   - **No Direct Grants**: Implementation realms do not receive direct storage
235//     permissions from version_manager.
236//   - **Explicit Realm Threading**: Registration and switching calls validate the
237//     live realm token with `rlm.IsCurrent()` and identify the caller with
238//     `rlm.Previous()`.
239//   - **Domain Isolation**: Registration is scoped to packages under the domain path.
240//
241// ## Security
242//
243// Domain-scoped security ensures that only authorized packages can register:
244//
245//   - **Path Validation**: Caller's package path must start with the domain path + "/"
246//   - **Realm Verification**: Only realm (contract) code can register, not user calls
247//   - **Example**: For domain "gno.land/r/gnoswap/protocol_fee":
248//   - Valid: "gno.land/r/gnoswap/protocol_fee/v1", "gno.land/r/gnoswap/protocol_fee/v2"
249//   - Invalid: "gno.land/r/gnoswap/other", "gno.land/r/attacker/malicious"
250//
251// ## Error Handling
252//
253// The package returns errors for:
254//
255//   - Unauthorized caller attempting to register (not in domain path)
256//   - Duplicate registration of the same package path
257//   - Attempting to switch to an unregistered version
258//   - Invalid initializer function type
259//
260// ## Best Practices
261//
262//  1. **Version Registration**: All versions should register during init(cur realm) to ensure
263//     they're available before any runtime operations.
264//  2. **Interface Compliance**: Ensure all versions implement the same domain interface
265//     for seamless switching.
266//  3. **Storage Compatibility**: Design storage schema to be forward and backward
267//     compatible across versions to prevent data corruption.
268//  4. **Testing**: Thoroughly test version switching in a staging environment before
269//     production use.
270//  5. **Rollback Support**: Keep previous versions registered to enable quick rollback
271//     if issues are detected in new versions.
272//  6. **Type Assertions**: Always check type assertions when retrieving the current
273//     implementation to prevent runtime panics.
274//
275// ## Use Cases
276//
277// ### Protocol Upgrades
278//
279// Upgrade DeFi protocol logic without disrupting active users:
280//
281//	manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v2")
282//
283// ### A/B Testing
284//
285// Test new implementations before full rollout:
286//
287//	// Switch to experimental version
288//	manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/experimental")
289//
290//	// Rollback if issues detected
291//	manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1")
292//
293// ### Emergency Response
294//
295// Quickly switch to a patched version during security incidents:
296//
297//	manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1_hotfix")
298//
299// ## Limitations and Considerations
300//
301//   - **Type Safety**: Requires runtime type assertion to domain interface. No compile-time
302//     type checking for implementation compatibility.
303//   - **Atomic Switching**: Initializer side effects during ChangeImplementation are not
304//     transactionally rolled back on partial failure. Manual recovery may be required.
305//   - **Storage Schema**: Requires careful schema design for cross-version compatibility.
306//     Breaking schema changes require migration or careful version ordering.
307//   - **Registration Order**: The first registered version automatically becomes the
308//     active implementation. Plan your deployment order carefully.
309//   - **No Unregistration**: Once registered, a version cannot be unregistered. Plan
310//     version lifecycles accordingly.
311//
312// ## Related Packages
313//
314//   - gno.land/p/gnoswap/store: Provides KVStore with permission-based access control
315//
316// Package version_manager is intended for use in Gno smart contracts requiring
317// dynamic, upgradeable implementations with zero-downtime version switching.
318package version_manager