doc.gno
11.04 Kb · 334 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 system manages permissions
10// dynamically, granting write access only to the active implementation while keeping
11// previous versions available in read-only mode for backward compatibility.
12//
13// Key components of this package include:
14//
15// 1. **VersionManager Interface**: Defines the contract for managing multiple versioned
16// implementations of a domain.
17// 2. **versionManager Implementation**: Concrete implementation that manages version
18// registration, permission control, and implementation switching.
19// 3. **Permission-Based Access Control**: Dynamically manages read/write permissions
20// for each version through the shared KVStore.
21// 4. **Domain-Scoped Security**: Ensures only authorized packages within the domain
22// path can register implementations.
23//
24// ## Key Features
25//
26// - **Zero-Downtime Upgrades**: Switch implementations at runtime without service
27// interruption or data migration.
28// - **Unified Storage**: All versions share a single KVStore with permission-based
29// access control, eliminating migration overhead.
30// - **Hot-Swapping**: Instant version switching through dynamic strategy replacement
31// with automatic permission management.
32// - **Domain-Scoped Security**: Only packages under the authorized domain path can
33// register implementations, preventing unauthorized access.
34// - **Backward Compatibility**: Previous versions remain accessible in read-only mode
35// for gradual migration and rollback support.
36// - **Strategy Pattern**: Enables runtime algorithm swapping without code changes.
37//
38// ## Architecture Pattern
39//
40// The package implements two complementary design patterns:
41//
42// - **Strategy Pattern**: Enables runtime selection of implementation strategies
43// - **Plugin Architecture**: Supports dynamic loading and registration of versions
44//
45// ## Workflow
46//
47// Typical usage of the version_manager package includes the following steps:
48//
49// 1. **Initialization**: Create a version manager for the domain using NewVersionManager.
50// 2. **Version Registration**: Each version (v1, v2, v3) calls RegisterInitializer during
51// their init() function to register their implementation.
52// 3. **Active Implementation**: The first registered version becomes the active implementation
53// with write access; subsequent versions are granted read-only access.
54// 4. **Version Switching**: Use ChangeImplementation to hot-swap to a different version,
55// which automatically updates permissions.
56//
57// ## Example Usage
58//
59// ### Step 1: Define Domain Interface
60//
61// ```gno
62// // protocol_fee/types.gno
63// package protocol_fee
64//
65// type ProtocolFee interface {
66// SetFeeRatio(ratio uint64) error
67// GetFeeRatio() uint64
68// }
69//
70// ```
71//
72// ### Step 2: Create Version Manager
73//
74// ```gno
75// // protocol_fee/protocol_fee.gno
76// package protocol_fee
77//
78// import (
79//
80// "gno.land/p/gnoswap/version_manager"
81// "gno.land/p/gnoswap/store"
82//
83// )
84//
85// var manager version_manager.VersionManager
86//
87// func init() {
88// kvStore := store.NewKVStore("protocol_fee")
89//
90// manager = version_manager.NewVersionManager(
91// "gno.land/r/gnoswap/protocol_fee",
92// kvStore,
93// func(kv store.KVStore) any {
94// return NewProtocolFeeStore(kv)
95// },
96// )
97// }
98//
99// func GetManager() version_manager.VersionManager {
100// return manager
101// }
102//
103// ```
104//
105// ### Step 3: Implement Version 1
106//
107// ```gno
108// // protocol_fee/v1/v1.gno
109// package v1
110//
111// import "gno.land/r/gnoswap/protocol_fee"
112//
113// type protocolFeeV1 struct {
114//
115// store any
116//
117// }
118//
119// func init() {
120// // Register this version during package initialization
121// manager := protocol_fee.GetManager()
122// manager.RegisterInitializer(func(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() {
154// // Register v2 - will be read-only until explicitly activated
155// manager := protocol_fee.GetManager()
156// manager.RegisterInitializer(func(store any) any {
157// return &protocolFeeV2{store: store}
158// })
159// }
160//
161// func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
162// // v2 improved implementation
163// return nil
164// }
165//
166// func (pf *protocolFeeV2) GetFeeRatio() uint64 {
167// // v2 improved implementation
168// return 0
169// }
170//
171// ```
172//
173// ### Step 5: Use Active Implementation
174//
175// ```gno
176// // client code
177// import "gno.land/r/gnoswap/protocol_fee"
178//
179// func UseFee() {
180// manager := protocol_fee.GetManager()
181// impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)
182//
183// ratio := impl.GetFeeRatio()
184// // Use the active version's implementation
185// }
186//
187// ```
188//
189// ### Step 6: Switch Versions at Runtime
190//
191// ```gno
192// // governance or admin function
193//
194// func UpgradeToV2() error {
195// manager := protocol_fee.GetManager()
196//
197// // Hot-swap to v2 - zero downtime
198// err := manager.ChangeImplementation("gno.land/r/gnoswap/protocol_fee/v2")
199// if err != nil {
200// return err
201// }
202//
203// // v2 now has write access
204// // v1 automatically becomes read-only
205// return nil
206// }
207//
208// ```
209//
210// ## Registration Flow
211//
212// The version registration process follows this sequence:
213//
214// 1. Domain package initializes version manager with KVStore
215// ↓
216// 2. v1 package calls RegisterInitializer during init()
217// → Becomes active implementation (write access)
218// ↓
219// 3. v2 package calls RegisterInitializer during init()
220// → Registered but read-only
221// ↓
222// 4. v3 package calls RegisterInitializer during init()
223// → Registered but read-only
224//
225// ## Version Switching Flow
226//
227// When switching versions, the following steps occur:
228//
229// 1. Admin calls ChangeImplementation("path/to/v2")
230// ↓
231// 2. Version Manager retrieves v2's initializer
232// ↓
233// 3. Executes v2 initializer with shared KVStore
234// ↓
235// 4. Updates permissions:
236// - v1: Write → ReadOnly
237// - v2: ReadOnly → Write
238// - v3: ReadOnly (unchanged)
239// ↓
240// 5. v2 is now active with write access
241//
242// ## Permission Model
243//
244// The version manager enforces a strict permission model:
245//
246// - **Write Access**: Only the active implementation can modify storage. Granted
247// automatically to the first registered version or during ChangeImplementation.
248// - **Read-Only Access**: Inactive versions can read storage but cannot modify it.
249// All non-active versions are automatically downgraded to read-only.
250// - **Automatic Transition**: Permission changes are handled automatically by the
251// manager during version switching.
252// - **Domain Isolation**: Permissions are scoped to the domain's KVStore.
253//
254// ## Security
255//
256// Domain-scoped security ensures that only authorized packages can register:
257//
258// - **Path Validation**: Caller's package path must start with the domain path + "/"
259// - **Realm Verification**: Only realm (contract) code can register, not user calls
260// - **Example**: For domain "gno.land/r/gnoswap/protocol_fee":
261// - Valid: "gno.land/r/gnoswap/protocol_fee/v1", "gno.land/r/gnoswap/protocol_fee/v2"
262// - Invalid: "gno.land/r/gnoswap/other", "gno.land/r/attacker/malicious"
263//
264// ## Error Handling
265//
266// The package returns errors for:
267//
268// - Unauthorized caller attempting to register (not in domain path)
269// - Duplicate registration of the same package path
270// - Attempting to switch to an unregistered version
271// - Permission update failures during version switching
272// - Invalid initializer function type
273//
274// ## Best Practices
275//
276// 1. **Version Registration**: All versions should register during init() to ensure
277// they're available before any runtime operations.
278// 2. **Interface Compliance**: Ensure all versions implement the same domain interface
279// for seamless switching.
280// 3. **Storage Compatibility**: Design storage schema to be forward and backward
281// compatible across versions to prevent data corruption.
282// 4. **Testing**: Thoroughly test version switching in a staging environment before
283// production use.
284// 5. **Rollback Support**: Keep previous versions registered to enable quick rollback
285// if issues are detected in new versions.
286// 6. **Type Assertions**: Always check type assertions when retrieving the current
287// implementation to prevent runtime panics.
288//
289// ## Use Cases
290//
291// ### Protocol Upgrades
292//
293// Upgrade DeFi protocol logic without disrupting active users:
294//
295// manager.ChangeImplementation("gno.land/r/gnoswap/protocol_fee/v2")
296//
297// ### A/B Testing
298//
299// Test new implementations before full rollout:
300//
301// // Switch to experimental version
302// manager.ChangeImplementation("gno.land/r/gnoswap/protocol_fee/experimental")
303//
304// // Rollback if issues detected
305// manager.ChangeImplementation("gno.land/r/gnoswap/protocol_fee/v1")
306//
307// ### Emergency Response
308//
309// Quickly switch to a patched version during security incidents:
310//
311// manager.ChangeImplementation("gno.land/r/gnoswap/protocol_fee/v1_hotfix")
312//
313// ## Limitations and Considerations
314//
315// - **Type Safety**: Requires runtime type assertion to domain interface. No compile-time
316// type checking for implementation compatibility.
317// - **Atomic Switching**: Permission updates during ChangeImplementation are not
318// transactionally rolled back on partial failure. Manual recovery may be required.
319// - **Storage Schema**: Requires careful schema design for cross-version compatibility.
320// Breaking schema changes require migration or careful version ordering.
321// - **Registration Order**: The first registered version automatically becomes the
322// active implementation with write access. Plan your deployment order carefully.
323// - **No Unregistration**: Once registered, a version cannot be unregistered. Plan
324// version lifecycles accordingly.
325//
326// ## Related Packages
327//
328// - gno.land/p/gnoswap/store: Provides KVStore with permission-based access control
329// - gno.land/p/nt/avl: AVL tree used for efficient initializer storage and lookup
330// - gno.land/p/nt/avl/rotree: Read-only tree wrapper for safe external access
331//
332// Package version_manager is intended for use in Gno smart contracts requiring
333// dynamic, upgradeable implementations with zero-downtime version switching.
334package version_manager