Search Apps Documentation Source Content File Folder Download Copy Actions Download

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