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