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
README.mdPreviewRaw
# Version Manager

Runtime version management system for dynamic implementation switching without data migration.

## Overview

Version Manager implements a Strategy Pattern-based system that enables hot-swapping between different versioned implementations of the same domain (e.g., v1, v2, v3) while maintaining a unified storage layer. This approach allows seamless upgrades without downtime or migration overhead.

## Features

- **Zero-Downtime Upgrades**: Switch implementations at runtime without service interruption
- **Unified Storage**: All versions share a single KVStore owned by the domain (proxy) realm
- **Domain-Scoped Security**: Only authorized packages within the domain path can register
- **Hot-Swapping**: Instant version switching through dynamic strategy replacement
- **Secure by Design**: Implementation realms cannot directly modify storage (see Storage Access Model below)

**Pattern**: Strategy + Plugin Architecture

## Usage

### Step 1: Define Domain Interface

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

type ProtocolFee interface {
    SetFeeRatio(ratio uint64) error
    GetFeeRatio() uint64
}
```

### Step 2: Create Version Manager

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

import (
    "gno.land/p/gnoswap/store/v1"
    "gno.land/p/gnoswap/version_manager/v1"
)

var manager version_manager.VersionManager

func init(cur realm) {
    kvStore := store.NewKVStore(cur.Address())

    manager = version_manager.NewVersionManager(
        cur.PkgPath(),
        kvStore,
        // initializeDomainStoreFn carries the v2 interrealm marker (`_ int, rlm realm`):
        // the leading 0 surfaces realm-threading at the call site.
        func(_ int, rlm realm, kv store.KVStore) any {
            return NewProtocolFeeStore(kv)
        },
    )
}

func GetManager() version_manager.VersionManager {
    return manager
}

// RegisterInitializer is the crossing entry point each version package calls.
// `cur` is the live crossing-frame realm token; it is threaded straight into the
// version manager (the leading 0 is the v2 sentinel) so the manager can reject
// spoofed/stale tokens via rlm.IsCurrent() and identify the caller via rlm.Previous().
func RegisterInitializer(cur realm, initializer func(_ int, rlm realm, store any) any) {
    if err := manager.RegisterInitializer(0, cur, initializer); err != nil {
        panic(err)
    }
}

// UpgradeImpl switches the active version. Authorization (admin / governance) is
// enforced here in the /r/ realm; version_manager only rejects spoofed tokens.
func UpgradeImpl(cur realm, packagePath string) {
    if err := manager.ChangeImplementation(0, cur, packagePath); err != nil {
        panic(err)
    }
}
```

### Step 3: Implement Versions

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

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

type protocolFeeV1 struct {
    store any
}

func init(cur realm) {
    // Register this version during package initialization.
    // `cross(cur)` invokes the domain's crossing entry point, which threads the
    // live realm token into the version manager.
    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
        return &protocolFeeV1{store: store}
    })
}

func (pf *protocolFeeV1) SetFeeRatio(ratio uint64) error {
    // v1 implementation
}

func (pf *protocolFeeV1) GetFeeRatio() uint64 {
    // v1 implementation
}
```

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

type protocolFeeV2 struct {
    store any
}

func init(cur realm) {
    // Register v2 — inactive until explicitly activated.
    protocol_fee.RegisterInitializer(cross(cur), func(_ int, rlm realm, store any) any {
        return &protocolFeeV2{store: store}
    })
}

func (pf *protocolFeeV2) SetFeeRatio(ratio uint64) error {
    // v2 improved implementation
}

func (pf *protocolFeeV2) GetFeeRatio() uint64 {
    // v2 improved implementation
}
```

### Step 4: Use Active Implementation

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

func UseFee() {
    manager := protocol_fee.GetManager()
    impl := manager.GetCurrentImplementation().(protocol_fee.ProtocolFee)

    ratio := impl.GetFeeRatio()
    // Use the active version's implementation
}
```

### Step 5: Switch Versions at Runtime

```go
// governance or admin entry point
func UpgradeToV2(cur realm) {
    // Hot-swap to v2 — zero downtime. `cross(cur)` enters UpgradeImpl's crossing
    // frame; UpgradeImpl threads the realm token into the version manager.
    protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
}
```

## Workflow

### Registration Flow

```
1. Domain package initializes version manager with KVStore
   ↓
2. v1 package calls RegisterInitializer (via the domain's crossing wrapper) during `init(cur realm)`
   → Manager validates the realm token (rlm.IsCurrent()) and caller domain path
   → Becomes active implementation
   ↓
3. v2 package calls RegisterInitializer during `init(cur realm)`
   → Registered for later activation
   ↓
4. v3 package calls RegisterInitializer during `init(cur realm)`
   → Registered
```

### Version Switching Flow

```
1. Admin/governance calls ChangeImplementation (via the domain's UpgradeImpl wrapper)
   → Authorization is enforced in the /r/ wrapper
   ↓
2. Version Manager validates the realm token (rlm.IsCurrent()), rejecting spoofed/stale tokens
   ↓
3. Version Manager retrieves v2's initializer
   ↓
4. Executes v2 initializer with shared KVStore
   ↓
5. Updates currentImplementation pointer to v2
   ↓
6. v2 is now the active implementation
```

### Storage Access Model

- **Domain Ownership**: The domain (proxy) realm owns the KVStore and has write permission
- **Explicit Realm Threading**: Registration/upgrade calls thread the live crossing-frame token (`rlm`) into the manager instead of relying on `runtime.CurrentRealm()`. The manager validates it with `rlm.IsCurrent()` (rejecting spoofed/stale tokens) and identifies the registering version package via `rlm.Previous()`
- **No Direct Permission Grants**: Implementation realms do not receive storage permissions directly; the proxy realm drives all storage access
- **Security by Design**: External callers cannot invoke implementation realms to modify storage

### Best Practices

1. **Version Registration**: All versions should register during `init(cur realm)`
2. **Interface Compliance**: Ensure all versions implement the same domain interface
3. **Storage Compatibility**: Design storage schema to be forward/backward compatible
4. **Testing**: Test version switching thoroughly before production use
5. **Rollback Support**: Keep previous versions registered for quick rollback capability

## Error Handling

The package returns errors for:

- A spoofed or stale realm token (`rlm.IsCurrent() == false` → `ErrSpoofedRealm`)
- Unauthorized caller attempting to register (not in domain path)
- Duplicate registration of the same package path
- Attempting to switch to an unregistered version
- A nil initializer in the registered map (`ChangeImplementation`'s internal
  invalid-state check). The initializer function signature is checked at
  compile time by the typed API.

## Use Cases

### Protocol Upgrades

Upgrade DeFi protocol logic without disrupting active users. The target version
must already be deployed/loaded and must have registered its initializer during
package initialization; `UpgradeImpl` only activates registered paths:

```go
// The protocol_fee/v2 package has already registered this path during init.
protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")
```

### A/B Testing

Test a new implementation before full rollout. Deploy/load the package and let
its `init` call `RegisterInitializer` before switching:

```go
// v2 was deployed and registered before this call.
protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v2")

// Roll back to another path that was also registered during initialization.
protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v1")
```

### Emergency Response

Quickly switch to a patched version during security incidents. The hotfix
package must be deployed/loaded and registered before activation:

```go
// v1_hotfix was deployed and registered during its package init.
protocol_fee.UpgradeImpl(cross(cur), "gno.land/r/gnoswap/protocol_fee/v1_hotfix")
```

## Implementation Notes

- Built on Strategy Pattern for runtime algorithm swapping
- Uses Plugin Architecture for explicit version registration
- Storage access is driven by the proxy realm; the live realm token is threaded explicitly (the v2 `_ int, rlm realm` marker) and validated via `rlm.IsCurrent()`
- No data migration required - all versions share the same storage
- Type assertions required when retrieving current implementation
- Initializers are registered by version packages; the manager does not load or deploy packages itself

## Limitations

- **Type Safety**: Requires runtime type assertion to domain interface
- **Storage Schema**: Requires careful schema design for cross-version compatibility
- **Registration Order**: First registered version becomes the initial active implementation
- **Domain Call Requirement**: Implementation functions must be called through domain proxy for storage access

## Related Packages

- `gno.land/p/gnoswap/store/v1`: KVStore with permission-based access control

Functions

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

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