PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/gnoswap/version_manager/v1

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v1
Namespace
gnoswap / version_manager
Files
5 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/gnoswap/version_manager/v1
gno
0.9

Files (5)

  • README.mdmarkdown
  • gnomod.tomltoml
  • doc.gnogno
  • types.gnogno
  • version_manager.gnogno
version_manager.gnogno
1// Package version_manager implements a runtime version management system using the Strategy Pattern.2// It enables dynamic switching between different implementation versions of the same domain (e.g., v1, v2, v3)3// while maintaining a unified storage layer. This approach allows for seamless upgrades without migration overhead.4//5// Key Features:6//   - Dynamic implementation registration and switching7//   - Domain-scoped security (only authorized packages can register)8//   - Zero-downtime upgrades through hot-swapping9//10// Architecture Pattern: Strategy + Plugin Architecture11package version_manager1213import (14	"chain"15	"errors"16	"strings"1718	"gno.land/p/gnoswap/store/v1"19)2021// ErrSpoofedRealm is returned when the supplied realm token does not match the22// live crossing frame (rlm.IsCurrent() == false). It signals a stale or23// spoofed token captured in an earlier frame.24const ErrSpoofedRealm = "rlm does not match the current crossing frame"2526// versionManager is the concrete implementation of VersionManager interface.27// It manages multiple versioned implementations of a domain (e.g., protocol_fee/v1, protocol_fee/v2).28//29// Storage Access Model:30// Implementation realms do NOT receive direct storage permissions. Instead, when calls flow31// from the domain proxy to the implementation, the proxy realm (which has write permission32// to the KVStore) is the one that drives the storage. This design prevents external callers33// from directly invoking implementation realms to modify storage.34type versionManager struct {35	// initializers stores registered initializer functions keyed by package path36	// Each initializer bootstraps a specific version's implementation37	initializers map[string]func(_ int, rlm realm, store any) any3839	// domainKVStore is the shared storage layer accessible by all versions40	// The domain (proxy) realm is the owner and has write permission41	domainKVStore store.KVStore4243	// initializeDomainStoreFn wraps the KVStore into domain-specific storage interface44	// This abstraction decouples the version manager from domain-specific storage implementations45	initializeDomainStoreFn func(_ int, rlm realm, kvStore store.KVStore) any4647	// domainPath defines the base path for this domain (e.g., "gno.land/r/gnoswap/protocol_fee")48	// Used for security validation to ensure only authorized packages can register49	domainPath string5051	// currentPackagePath holds the package path of the active implementation52	// (e.g., "gno.land/r/gnoswap/protocol_fee/v2")53	currentPackagePath string5455	// currentImplementation is the active version's instance56	currentImplementation any57}5859// RegisterInitializer registers a new version implementation for the domain.60// This method must be called by each version package (e.g., v1, v2) during initialization.61//62// The registration process:63//  1. Validates the realm token is the live crossing frame (rejects spoofed tokens)64//  2. Validates the caller is within the authorized domain path65//  3. Stores the initializer function for later version switching66//67// Parameters:68//   - _: Interrealm-call discriminator; callers pass 0.69//   - rlm: Propagated current realm context from the domain wrapper; this implementation validates rlm.IsCurrent() and inspects rlm.Previous() to identify and authorize the registering version package.70//   - initializer: Callback receiving the discriminator, current realm context, and domain-specific storage wrapper, and returning the version implementation instance.71//72// Returns:73//   - error: nil when the initializer is registered (and the first one is activated); otherwise an error for a spoofed realm, nil initializer, user caller, caller outside the domain path, or duplicate package path.74//75// Security: Only packages under the domainPath prefix can register (enforced by isContainDomainPath).76func (vm *versionManager) RegisterInitializer(_ int, rlm realm, initializer func(_ int, rlm realm, store any) any) error {77	if !rlm.IsCurrent() {78		return errors.New(ErrSpoofedRealm)79	}8081	// Validate initializer is not nil to prevent panic during initialization82	if initializer == nil {83		return errors.New("version_manager: initializer cannot be nil")84	}8586	// Ensure the caller is within the domain path (e.g., protocol_fee/v1, protocol_fee/v2).87	// rlm.Previous() corresponds to v1's runtime.PreviousRealm().88	previousRealm := rlm.Previous()89	if previousRealm.IsUser() {90		return errors.New("version_manager: caller cannot be user")91	}9293	targetPackagePath := previousRealm.PkgPath()94	if !vm.isContainDomainPath(targetPackagePath) {95		return errors.New("version_manager: caller is not in the domain path")96	}9798	// Check if this package path has already been registered99	if _, ok := vm.initializers[targetPackagePath]; ok {100		return errors.New("version_manager: initializer already registered")101	}102103	// Register the initializer function for this package path104	vm.initializers[targetPackagePath] = initializer105106	chain.Emit(107		"RegisterInitializer",108		"domainPath", vm.domainPath,109		"registeredPackagePath", targetPackagePath,110	)111112	// Initialize the current implementation if it hasn't been done yet113	if vm.currentPackagePath == "" || vm.currentImplementation == nil {114		vm.currentPackagePath = targetPackagePath115		vm.currentImplementation = initializer(0, rlm, vm.initializeDomainStoreFn(0, rlm, vm.domainKVStore))116117		chain.Emit(118			"InitializeImplementation",119			"domainPath", vm.domainPath,120			"newPackagePath", targetPackagePath,121		)122	}123124	return nil125}126127// ChangeImplementation performs a hot-swap to a different version implementation.128// This enables zero-downtime upgrades by switching the active implementation at runtime.129//130// The switching process:131//  1. Validates the realm token is the live crossing frame (rejects spoofed tokens)132//  2. Validates the target version has been registered via RegisterInitializer133//  3. Retrieves and executes the target version's initializer134//135// Authorization is the caller realm's responsibility — version_manager only rejects136// spoofed realm tokens. Upgrade ACLs (admin / governance) live in the wrapping /r/ realm137// (see each module's upgrade.gno).138//139// Parameters:140//   - _: Interrealm-call discriminator; callers pass 0.141//   - rlm: Propagated current realm context from the domain wrapper; this implementation validates rlm.IsCurrent() before switching.142//   - packagePath: Full package path of the target version; it must be a key in the registered initializer map.143//144// Returns:145//   - error: nil when packagePath becomes active; otherwise ErrSpoofedRealm, an unknown-package error, or an invalid-initializer error.146func (vm *versionManager) ChangeImplementation(_ int, rlm realm, packagePath string) error {147	if !rlm.IsCurrent() {148		return errors.New(ErrSpoofedRealm)149	}150151	// Retrieve the registered initializer function152	initializer, ok := vm.initializers[packagePath]153	if !ok {154		return errors.New("version_manager: initializer not found for package path:" + packagePath)155	}156157	if initializer == nil {158		return errors.New("version_manager: initializer is not a function")159	}160161	prevPackagePath := vm.currentPackagePath162	vm.currentPackagePath = packagePath163	vm.currentImplementation = initializer(0, rlm, vm.initializeDomainStoreFn(0, rlm, vm.domainKVStore))164165	chain.Emit(166		"ChangeImplementation",167		"domainPath", vm.domainPath,168		"previousPackagePath", prevPackagePath,169		"newPackagePath", packagePath,170	)171172	return nil173}174175// GetDomainPath returns the base domain path for this version manager.176// Example: "gno.land/r/gnoswap/protocol_fee"177//178// Returns:179//   - string: Base package path used to scope registered version implementations.180func (vm *versionManager) GetDomainPath() string {181	return vm.domainPath182}183184// GetInitializers returns the map containing all registered initializer functions.185// Keys are package paths, values are initializer functions.186// Useful for inspecting which versions are available.187//188// Returns:189//   - map[string]func(_ int, rlm realm, store any) any: Current registry mapping version package paths to initializer callbacks.190func (vm *versionManager) GetInitializers() map[string]func(_ int, rlm realm, store any) any {191	return vm.initializers192}193194// GetCurrentPackagePath returns the package path of the currently active implementation.195//196// Returns:197//   - string: Active implementation's package path, or the empty string before registration.198func (vm *versionManager) GetCurrentPackagePath() string {199	return vm.currentPackagePath200}201202// GetCurrentImplementation returns the instance of the currently active version.203// The returned value should be type-asserted to the domain-specific interface.204//205// Returns:206//   - any: Active version implementation instance, or nil before the first initializer is registered.207func (vm *versionManager) GetCurrentImplementation() any {208	return vm.currentImplementation209}210211// isContainDomainPath checks if the calling contract is within the authorized domain path.212// This is a critical security check that prevents unauthorized external contracts from213// registering implementations.214//215// Validation rules:216//   - Package path must start with domainPath + "/"217//218// Example:219//   - domainPath: "gno.land/r/gnoswap/protocol_fee"220//   - Valid callers: "gno.land/r/gnoswap/protocol_fee/v1", "gno.land/r/gnoswap/protocol_fee/v2"221//   - Invalid callers: "gno.land/r/gnoswap/other", "gno.land/r/attacker/malicious"222func (vm *versionManager) isContainDomainPath(targetPackagePath string) bool {223	// `domainPath` is set via the current realm's PkgPath in each contract.224	// Therefore, there is no need for a separate trailing slash check,225	// and the prefix is determined by directly appending `/` for version detection.226	prefix := vm.domainPath + "/"227228	return strings.HasPrefix(targetPackagePath, prefix)229}230231// NewVersionManager creates a new version manager instance for a specific domain.232// This should be called once per domain during system initialization.233//234// Parameters:235//236//   - domainPath: The base package path for the domain (e.g., "gno.land/r/gnoswap/protocol_fee")237//     Used for access control to ensure only authorized packages can register238//239//   - kvStore: The shared key-value store that all versions will access240//     The domain realm (proxy) is the owner and has write permission to this store241//242//   - initializeDomainStoreFn: A factory function that wraps the KVStore into a domain-specific storage interface243//     This abstraction allows each version to work with a familiar storage API244//     Example: func(_ int, rlm realm, kvStore store.KVStore) any { return NewProtocolFeeStore(kvStore) }245//246// Returns:247//   - VersionManager: An initialized version manager ready to accept implementation registrations248//249// Usage Pattern:250//  1. Create version manager in parent domain package251//  2. Each version (v1, v2, v3) calls RegisterInitializer during their init()252//  3. Use ChangeImplementation to switch between versions at runtime253func NewVersionManager(254	domainPath string,255	kvStore store.KVStore,256	initializeDomainStoreFn func(_ int, rlm realm, kvStore store.KVStore) any,257) VersionManager {258	return &versionManager{259		domainPath:              domainPath,260		domainKVStore:           kvStore,261		initializeDomainStoreFn: initializeDomainStoreFn,262		initializers:            make(map[string]func(_ int, rlm realm, store any) any),263		currentPackagePath:      "",264		currentImplementation:   nil,265	}266}267

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.