Skip to content

feat(escrow): harden upgradeability, version visibility, and onboarding interop#246

Merged
Agbeleshe merged 1 commit into
Hub-of-Evolution:mainfrom
davedumto:feat/upgradeability-and-interop-hardening
Apr 29, 2026
Merged

feat(escrow): harden upgradeability, version visibility, and onboarding interop#246
Agbeleshe merged 1 commit into
Hub-of-Evolution:mainfrom
davedumto:feat/upgradeability-and-interop-hardening

Conversation

@davedumto
Copy link
Copy Markdown
Contributor

Summary

Hardens four cross-cutting upgradeability and interoperability concerns in craft-nexus-contract/src/lib.rs:

  • [UPGRADEABILITY] WasmUpgradeProposal Unvalidated Upgrade Path #230WasmUpgradeProposal is now validated end-to-end: zero-hash rejection, explicit cancel-before-replace, execute_upgrade(expected_wasm_hash) re-check, proposer/timestamp metadata, and lifecycle events (UPG_PROP / UPG_CANC / UPG_EXEC).
  • [MAINTAINABILITY] ContractVersion Missing Migration Visibility #241 — Adds a bounded UpgradeHistory log (FIFO-capped at 32) plus get_upgrade_history / get_version_info queries, and documents the ContractVersion semantics on get_version.
  • [INTEROPERABILITY] OnboardingContractAddress Unverified Cross-Contract Call #243 — Cross-contract calls to the onboarding contract now route through safe_update_reputation / safe_update_user_metrics, which wrap try_invoke_contract so a malicious or version-skewed onboarding contract cannot trap escrow flows. set_onboarding_contract rejects self-pointers; get_onboarding_contract / has_onboarding_contract / clear_onboarding_contract are public; failed cross-contract calls emit an OB_FAIL warning event.
  • [UPGRADEABILITY] FeeTokenIndex Single-Value Upgrade Constraint #239 — Introduces per-token FeeTokenInfo (active / custom_fee_bps / accumulated) at DataKey::FeeTokenConfig(Address), paired with get_fee_token_config / get_fee_tokens / set_fee_token_config / migrate_fee_token_configs. accumulated is contract-owned — admin can flip active or override bps but cannot rewrite historical fee accounting. The legacy FeeTokenIndex Vec remains the canonical enumeration source for backward compatibility.

Behavior changes worth flagging on review

  • execute_upgrade() signature gained an expected_wasm_hash: BytesN<32> argument. SDK callers and any deploy scripts must be updated. This is intentional defense-in-depth against signing-tool spoofing where the operator sees a different payload than what is stored on-chain.
  • propose_upgrade_wasm no longer silently overwrites a pending proposal — callers receive UpgradeProposalExists and must explicitly cancel_upgrade_wasm first. This prevents a compromised admin from resetting the cooldown clock without an audit trail.
  • cancel_upgrade_wasm now returns NoUpgradeProposed when there is nothing to cancel, instead of silently succeeding.
  • New error variants: InvalidUpgradeHash, UpgradeProposalExists, OnboardingContractNotSet.
  • custom_fee_bps on FeeTokenInfo is storage-only in this PR — calculate_fee does not yet consult it. A follow-up issue can wire it into the fee calculation once the storage shape stabilizes in production.

Out of scope

The pre-existing lib build errors on main (Escrow initializers missing batch_id / funded fields, stake_data scope error) are unrelated to these four issues and intentionally untouched here.

Test plan

  • cargo check --lib shows the same 3 pre-existing errors and no new ones (verified locally).
  • Test suite has pre-existing breakage on main (~80 errors before this PR); follow-up needed to refresh setup_test for the new execute_upgrade signature and to define the missing onboarding_contract test binding.
  • Manual review of the four issue acceptance criteria against the changes summarized above.

closes #230
closes #241
closes #243
closes #239

…ng interop

Addresses four hardening issues filed against the escrow contract.

Hub-of-Evolution#230 — WasmUpgradeProposal validation
- Reject zero-hash proposals up front with InvalidUpgradeHash so admins
  cannot accidentally propose the default BytesN<32> sentinel.
- Refuse to overwrite a pending proposal silently; admin must cancel
  first, preventing a compromised admin from resetting the cooldown
  clock without a visible audit trail.
- execute_upgrade now takes an expected_wasm_hash and re-checks it
  against the stored proposal as defense-in-depth against signing-tool
  spoofing.
- Capture proposed_by / proposed_at on every proposal and emit
  UpgradeProposalEvent on propose / cancel / execute.

Hub-of-Evolution#241 — ContractVersion migration visibility
- New UpgradeRecord + bounded UpgradeHistory log (FIFO cap of 32) so
  every successful upgrade is queryable on-chain alongside its
  wasm_hash, executing admin, and timestamp.
- Add get_upgrade_history and get_version_info query methods.
- Document ContractVersion semantics on get_version: starts at 1,
  increments by exactly 1 per execute_upgrade, decoupled from the
  wasm_hash which is captured per-record in history.

Hub-of-Evolution#243 — Onboarding cross-contract call hardening
- Cross-contract calls now route through safe_update_reputation /
  safe_update_user_metrics, which use try_invoke_contract so a
  malicious or version-skewed onboarding contract cannot trap the
  escrow flow.
- Reject set_onboarding_contract pointing at the escrow itself to
  remove a re-entrancy hazard if the OnboardingInterface ever expands.
- Emit config_updated events on set / clear; expose
  get_onboarding_contract / has_onboarding_contract / clear_onboarding_contract.
- Emit OB_FAIL warning event with the failing method name when a safe
  cross-contract call falls through; reputation events (from Hub-of-Evolution#211) keep
  off-chain reconstructions intact.

Hub-of-Evolution#239 — FeeTokenIndex flexibility
- Introduce FeeTokenInfo (active / custom_fee_bps / accumulated) keyed
  per token via DataKey::FeeTokenConfig(Address). Mirrors the running
  fee total alongside the legacy FeeTokenIndex Vec, giving future
  multi-token fee logic a per-token slot without a contract upgrade.
- get_fee_token_config / get_fee_tokens / set_fee_token_config /
  migrate_fee_token_configs admin functions added for tuning and
  one-shot backfill of pre-existing tokens. accumulated remains
  contract-owned (admin can flip active or override bps but cannot
  rewrite historical fee accounting).

Pre-existing lib build errors (Escrow batch_id/funded missing fields,
stake_data scope) are out of scope for these issues and unchanged.

closes Hub-of-Evolution#230
closes Hub-of-Evolution#241
closes Hub-of-Evolution#243
closes Hub-of-Evolution#239
@Agbeleshe Agbeleshe merged commit 2020338 into Hub-of-Evolution:main Apr 29, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants