Skip to content

feat(contract): #18 admin-only upgrade entrypoint with version tracking - #19

Merged
CHASE3020 merged 2 commits into
mainfrom
feat/issue-18-contract-upgrade
Nov 6, 2026
Merged

CHASE3020 merged 2 commits into
mainfrom
feat/issue-18-contract-upgrade

Conversation

@CHASE3020

Copy link
Copy Markdown
Collaborator

Summary

Resolves #18.

Implements admin-only contract upgradability for YieldStreamDistributor via Soroban's env.deployer().update_current_contract_wasm with monotonic version tracking, complete storage key preservation, a get_version() public view, and a detailed operational runbook and trust analysis in docs/UPGRADING.md.

Changes Made

  • Contract Interface (contracts/distributor/src/lib.rs):
    • Added upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError>:
      • Enforces admin authorization via admin.require_auth().
      • Increments the contract version (storage::set_version) with overflow checks.
      • Preserves and bumps instance TTL (storage::bump_instance).
      • Executes in-place WASM executable replacement via env.deployer().update_current_contract_wasm(new_wasm_hash).
      • Emits structured audit event contract_upgraded.
    • Added public view get_version(env: Env) -> u32.
    • Initialized version = 1 in initialize.
  • State Storage (contracts/distributor/src/storage.rs & types.rs):
    • Added DataKey::Version to instance storage.
    • Implemented get_version (defaulting to 1 for unmigrated v1 contracts) and set_version.
    • All existing keys (DataKey::Admin, DataKey::DistributionCount, DataKey::Distribution(id), DataKey::Claimed(id, index)) remain preserved and unaltered across upgrades.
  • Audit Events (contracts/distributor/src/events.rs):
    • Added contract_upgraded(&env, old_version, new_version, &new_wasm_hash) with topics ("upgrade", old_version, new_version) and new hash payload.
  • Integration & Unit Tests (contracts/distributor/src/test/upgrade.rs):
    • test_upgrade_contract_success_and_preserves_state: Initializes v1, creates campaign, claims leaf 0 under v1, uploads v2 WASM via env.deployer().upload_contract_wasm, invokes upgrade, verifies version bumps from 1 to 2, verifies upgrade event, verifies that v1 state (campaign metadata, admin, counts) remains intact, claims leaf 1 under v2, and verifies double-claim protection persists.
    • test_upgrade_auth_matrix: Verifies authorization required specifically matches the admin address.
    • test_upgrade_without_admin_auth_fails: Verifies that unauthorized callers cannot invoke upgrade.
    • test_upgrade_uninitialized_fails: Verifies that an uninitialized contract returns ContractError::NotInitialized.
    • test_upgrade_consecutive_versions: Verifies sequential version bumps (v1 -> v2 -> v3).
  • Runbook & Security Analysis (docs/UPGRADING.md):
    • Step-by-step CLI commands for deterministic build, WASM install, upgrade invocation, post-upgrade validation, and disaster recovery rollback.
    • Written analysis of the trust trade-off (admin code replacement privileges vs immutability) and mitigations (multisig, timelocks, ossification).
    • Explicit composition model with the emergency pause switch (Contract: admin emergency pause switch #9).

Acceptance Verification

  • Test deploys a v2 WASM in testutils and claims with v1 state intact
  • Auth matrix locked down (admin.require_auth())
  • Written analysis of trust trade-off and composition with emergency pause switch (Contract: admin emergency pause switch #9) in docs/UPGRADING.md
  • Passes cargo fmt --check
  • Passes cargo clippy --all-targets -- -D warnings
  • All 26 unit and integration tests passing

@CHASE3020
CHASE3020 merged commit e3fbd27 into main Nov 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lifecycle: contract upgradability (admin-only upgrade entrypoint)

1 participant