Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

version_manager source pure

Package version\_manager provides a runtime version management system for dynamic implementation switching without da...

Overview

Package version_manager provides a runtime version management system for dynamic implementation switching without data migration. It implements the Strategy Pattern combined with Plugin Architecture to enable hot-swapping between different versioned implementations of the same domain.

## Overview

Version Manager enables seamless upgrades by allowing multiple versioned implementations (v1, v2, v3) to coexist and share a unified storage layer. The domain (proxy) realm owns that storage; the manager records version initializers and swaps the active implementation reference without granting implementation realms direct write permission.

Key components of this package include:

  1. **VersionManager Interface**: Defines the contract for managing multiple versioned implementations of a domain.
  2. **versionManager Implementation**: Concrete implementation that manages version registration and active implementation switching.
  3. **Realm-Threaded Validation**: Validates live realm tokens and restricts registration to packages under the configured domain path.
  4. **Domain-Scoped Security**: Ensures only authorized packages within the domain path can register implementations.

## Key Features

  • **Zero-Downtime Upgrades**: Switch implementations at runtime without service interruption or data migration.
  • **Unified Storage**: All versions share a single KVStore owned by the domain (proxy) realm, eliminating migration overhead.
  • **Hot-Swapping**: Instant version switching through dynamic strategy replacement with explicit realm validation.
  • **Domain-Scoped Security**: Only packages under the authorized domain path can register implementations, preventing unauthorized access.
  • **Backward Compatibility**: Previous versions remain registered for gradual migration and rollback support.
  • **Strategy Pattern**: Enables runtime algorithm swapping without code changes.

## Architecture Pattern

The package implements two complementary design patterns:

  • **Strategy Pattern**: Enables runtime selection of implementation strategies
  • **Plugin Architecture**: Supports dynamic loading and registration of versions

## Workflow

Typical usage of the version_manager package includes the following steps:

  1. **Initialization**: Create a version manager for the domain using NewVersionManager.
  2. **Version Registration**: Each version (v1, v2, v3) calls RegisterInitializer during its init(cur realm) function to register its implementation.
  3. **Active Implementation**: The first registered version becomes the active implementation; subsequent versions are retained for later switching.
  4. **Version Switching**: Use ChangeImplementation to hot-swap to a different version while storage ownership stays with the domain KVStore.

## Example Usage

### Step 1: Define Domain Interface

```gno // protocol_fee/types.gno package protocol_fee

Example
1type ProtocolFee interface {
2    SetFeeRatio(ratio uint64) error
3    GetFeeRatio() uint64
4}

```

### Step 2: Create Version Manager

```gno // protocol_fee/protocol_fee.gno package protocol_fee

import (

Example
1"gno.land/p/gnoswap/version_manager"
2"gno.land/p/gnoswap/store"

)

Example
 1var manager version_manager.VersionManager
 2
 3func init(cur realm) {
 4    kvStore := store.NewKVStore(cur.Address())
 5
 6    manager = version_manager.NewVersionManager(
 7        cur.PkgPath(),
 8        kvStore,
 9        func(_ int, rlm realm, kv store.KVStore) any {
10            return NewProtocolFeeStore(kv)
11        },
12    )
13}
14
15func GetManager() version_manager.VersionManager {
16    return manager
17}

```

### Step 3: Implement Version 1

```gno // protocol_fee/v1/v1.gno package v1

import "gno.land/r/gnoswap/protocol_fee"

type protocolFeeV1 struct {

Example
1store any

}

Example
 1func init(cur realm) {
 2    // Register this version during package initialization.
 3    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
 4        return &protocolFeeV1{store: store}
 5    })
 6}
 7
 8func (pf *protocolFeeV1) SetFeeRatio(ratio uint64) error {
 9    // v1 implementation
10    return nil
11}
12
13func (pf *protocolFeeV1) GetFeeRatio() uint64 {
14    // v1 implementation
15    return 0
16}

```

### Step 4: Implement Version 2

```gno // protocol_fee/v2/v2.gno package v2

import "gno.land/r/gnoswap/protocol_fee"

type protocolFeeV2 struct {

Example
1store any

}

Example
 1func init(cur realm) {
 2    // Register v2 - inactive until explicitly activated.
 3    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
 4        return &protocolFeeV2{store: store}
 5    })
 6}
 7
 8func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
 9    // v2 improved implementation
10    return nil
11}
12
13func (pf *protocolFeeV2) GetFeeRatio() uint64 {
14    // v2 improved implementation
15    return 0
16}

```

### Step 5: Use Active Implementation

```gno // client code import "gno.land/r/gnoswap/protocol_fee"

Example
1func UseFee() {
2    manager := protocol_fee.GetManager()
3    impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)
4
5    ratio := impl.GetFeeRatio()
6    // Use the active version's implementation
7}

```

### Step 6: Switch Versions at Runtime

```gno // governance or admin function

Example
1func UpgradeToV2(cur realm) {
2    // Hot-swap to v2 - zero downtime.
3    protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
4}

```

## Registration Flow

The version registration process follows this sequence:

  1. Domain package initializes version manager with KVStore ↓
  2. v1 package calls the domain registration wrapper during init(cur realm) → Becomes the active implementation ↓
  3. v2 package calls the domain registration wrapper during init(cur realm) → Registered for later activation ↓
  4. v3 package calls the domain registration wrapper during init(cur realm) → Registered for later activation

## Version Switching Flow

When switching versions, the following steps occur:

  1. Admin/governance calls the domain upgrade wrapper ↓
  2. The domain wrapper enforces authorization and calls ChangeImplementation(0, cur, ...) ↓
  3. Version Manager validates the live realm token and retrieves v2's initializer ↓
  4. Executes v2 initializer with the shared KVStore ↓
  5. Updates currentPackagePath and currentImplementation

## Storage Access Model

The version manager keeps storage ownership with the domain (proxy) realm:

  • **Domain Ownership**: The domain realm owns the KVStore and drives writes.
  • **No Direct Grants**: Implementation realms do not receive direct storage permissions from version_manager.
  • **Explicit Realm Threading**: Registration and switching calls validate the live realm token with `rlm.IsCurrent()` and identify the caller with `rlm.Previous()`.
  • **Domain Isolation**: Registration is scoped to packages under the domain path.

## Security

Domain-scoped security ensures that only authorized packages can register:

  • **Path Validation**: Caller's package path must start with the domain path + "/"
  • **Realm Verification**: Only realm (contract) code can register, not user calls
  • **Example**: For domain "gno.land/r/gnoswap/protocol_fee":
  • Valid: "gno.land/r/gnoswap/protocol_fee/v1", "gno.land/r/gnoswap/protocol_fee/v2"
  • Invalid: "gno.land/r/gnoswap/other", "gno.land/r/attacker/malicious"

## Error Handling

The package returns errors for:

  • Unauthorized caller attempting to register (not in domain path)
  • Duplicate registration of the same package path
  • Attempting to switch to an unregistered version
  • Invalid initializer function type

## Best Practices

  1. **Version Registration**: All versions should register during init(cur realm) to ensure they're available before any runtime operations.
  2. **Interface Compliance**: Ensure all versions implement the same domain interface for seamless switching.
  3. **Storage Compatibility**: Design storage schema to be forward and backward compatible across versions to prevent data corruption.
  4. **Testing**: Thoroughly test version switching in a staging environment before production use.
  5. **Rollback Support**: Keep previous versions registered to enable quick rollback if issues are detected in new versions.
  6. **Type Assertions**: Always check type assertions when retrieving the current implementation to prevent runtime panics.

## Use Cases

### Protocol Upgrades

Upgrade DeFi protocol logic without disrupting active users:

Example
1manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v2")

### A/B Testing

Test new implementations before full rollout:

Example
1// Switch to experimental version
2manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/experimental")
3
4// Rollback if issues detected
5manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1")

### Emergency Response

Quickly switch to a patched version during security incidents:

Example
1manager.ChangeImplementation(0, cur, "gno.land/r/gnoswap/protocol_fee/v1_hotfix")

## Limitations and Considerations

  • **Type Safety**: Requires runtime type assertion to domain interface. No compile-time type checking for implementation compatibility.
  • **Atomic Switching**: Initializer side effects during ChangeImplementation are not transactionally rolled back on partial failure. Manual recovery may be required.
  • **Storage Schema**: Requires careful schema design for cross-version compatibility. Breaking schema changes require migration or careful version ordering.
  • **Registration Order**: The first registered version automatically becomes the active implementation. Plan your deployment order carefully.
  • **No Unregistration**: Once registered, a version cannot be unregistered. Plan version lifecycles accordingly.

## Related Packages

  • gno.land/p/gnoswap/store: Provides KVStore with permission-based access control

Package version_manager is intended for use in Gno smart contracts requiring dynamic, upgradeable implementations with zero-downtime version switching.

Package version_manager implements a runtime version management system using the Strategy Pattern. It enables dynamic switching between different implementation versions of the same domain (e.g., v1, v2, v3) while maintaining a unified storage layer. This approach allows for seamless upgrades without migration overhead.

Key Features:

  • Dynamic implementation registration and switching
  • Domain-scoped security (only authorized packages can register)
  • Zero-downtime upgrades through hot-swapping

Architecture Pattern: Strategy + Plugin Architecture

Constants 1

const ErrSpoofedRealm

1const ErrSpoofedRealm = "rlm does not match the current crossing frame"
source

ErrSpoofedRealm is returned when the supplied realm token does not match the live crossing frame (rlm.IsCurrent() == false). It signals a stale or spoofed token captured in an earlier frame.

Functions 1

func NewVersionManager

1func NewVersionManager(
2	domainPath string,
3	kvStore store.KVStore,
4	initializeDomainStoreFn func(_ int, rlm realm, kvStore store.KVStore) any,
5) VersionManager
source

NewVersionManager creates a new version manager instance for a specific domain. This should be called once per domain during system initialization.

Parameters:

  • domainPath: The base package path for the domain (e.g., "gno.land/r/gnoswap/protocol_fee") Used for access control to ensure only authorized packages can register

  • kvStore: The shared key-value store that all versions will access The domain realm (proxy) is the owner and has write permission to this store

  • initializeDomainStoreFn: A factory function that wraps the KVStore into a domain-specific storage interface This abstraction allows each version to work with a familiar storage API Example: func(_ int, rlm realm, kvStore store.KVStore) any { return NewProtocolFeeStore(kvStore) }

Returns:

  • VersionManager: An initialized version manager ready to accept implementation registrations

Usage Pattern:

  1. Create version manager in parent domain package
  2. Each version (v1, v2, v3) calls RegisterInitializer during their init()
  3. Use ChangeImplementation to switch between versions at runtime

Types 1

type VersionManager

interface
 1type VersionManager interface {
 2	// RegisterInitializer registers a version's implementation.
 3	// Must be called by each version package during initialization.
 4	// First registration becomes the active implementation.
 5	// Subsequent registrations are retained for later switching.
 6	//
 7	// The leading `_ int, rlm realm` is the v2 interrealm-spec marker pattern:
 8	// the `0` sentinel surfaces at every call site so the realm threading is
 9	// visible to the reader, and rlm is the live crossing-frame token that
10	// the implementation validates via rlm.IsCurrent() and reads via
11	// rlm.Previous() to identify the version package.
12	RegisterInitializer(_ int, rlm realm, initializer func(_ int, rlm realm, store any) any) error
13
14	// ChangeImplementation switches the active version at runtime.
15	// This enables hot-swapping without downtime or data migration while storage
16	// ownership stays with the domain KVStore.
17	//
18	// The leading `_ int, rlm realm` is the v2 interrealm-spec marker pattern;
19	// rlm is validated via rlm.IsCurrent() to reject spoofed/stale tokens.
20	// Upgrade ACLs live in the wrapping /r/ realm, not here.
21	ChangeImplementation(_ int, rlm realm, packagePath string) error
22
23	// GetDomainPath returns the base domain path (e.g., "gno.land/r/gnoswap/protocol_fee")
24	GetDomainPath() string
25
26	// GetInitializers returns all registered version initializers
27	GetInitializers() map[string]func(_ int, rlm realm, store any) any
28
29	// GetCurrentPackagePath returns the package path of the active implementation
30	GetCurrentPackagePath() string
31
32	// GetCurrentImplementation returns the active version instance
33	// The caller should type-assert this to the domain-specific interface
34	GetCurrentImplementation() any
35}
source

VersionManager defines the interface for managing multiple versioned implementations of a domain. It enables the Strategy Pattern at the package level, allowing runtime switching between different versions (v1, v2, v3, etc.) without requiring data migration.

Design Goals:

  • Enable zero-downtime upgrades through hot-swapping implementations
  • Maintain a single source of truth for storage across all versions
  • Enforce security through domain-scoped registration
  • Support backward compatibility by keeping old versions registered for later activation

Implementation Note: The actual implementations of each version must satisfy a common domain interface defined by the specific domain (e.g., ProtocolFee interface for protocol_fee domain).

Imports 4

Source Files 4