security: harden upgrade path with two-step WASM hash verification - #79
Merged
arisu6804 merged 1 commit intoAug 27, 2026
Conversation
- Add Error::WasmHashMismatch (code 10) for atomic rejection of unrecognized artifacts - Add DataKey::ExpectedWasmHash instance-storage entry so the admin can stage an approved hash before applying an upgrade - Add storage::set/get/clear_expected_wasm_hash helpers - Add set_expected_wasm_hash entrypoint (admin-only) to record the approved artifact on-chain - Rewrite upgrade() to compare new_wasm_hash against the staged value before calling update_current_contract_wasm; any mismatch returns WasmHashMismatch and the transaction rolls back atomically without touching Wasm, storage, or events - Add admin address to upgrade event topics for indexer attribution - Add six new tests covering: no-staged-hash rejection, hash mismatch rejection, state preservation after mismatch, happy-path upgrade, unauthorized set_expected_wasm_hash, and event payload shape - Unblock and fix previously ignored test_upgrade_event_payload Closes YieldVault-Org#78
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #78
Hardens the contract upgrade path with a two-step, admin-authorized WASM hash verification gate. An upgrade now requires the admin to first stage an approved hash on-chain; only a call that presents the exact same hash can then apply the upgrade. Any mismatch is rejected atomically — no Wasm swap, no storage mutation, no event.
Trust model
The upgrade authority is the configured vault admin (the same account that controls pause, yield accrual, and admin rotation). Governance can transfer that role via
set_adminbefore initiating an upgrade.The two-step flow is:
set_expected_wasm_hash(hash)— records the approved artifact on-chain (instance storage).upgrade(hash)— verifieshash == stored expected hashbefore invokingupdate_current_contract_wasm. On success the staged entry is cleared and an auditable event is emitted.This means a typo, a supply-chain substitution, or a race-condition attempt with a different hash is always rejected before any state is touched.
Changes
src/error.rsWasmHashMismatch = 10— stable numeric code for artifact rejection.src/types.rsDataKey::ExpectedWasmHash— instance-storage key for the staged approved hash.src/storage.rsget/set/clear_expected_wasm_hashhelpers. The clear fires only on a successful upgrade; a mismatch leaves the staged value intact so the admin can retry with the correct artifact.src/lib.rsset_expected_wasm_hash(expected_hash)entrypoint (admin-only, auth-guarded) to stage the approved hash before an upgrade.upgrade(): checks staged hash == supplied hash before calling the deployer; returnsWasmHashMismatchotherwise. Clears the staged entry and emits the upgrade event only on success.src/events.rsupgradeevent now includesadminas a second topic so indexers can attribute every upgrade to an authority without scanning the data payload.Artifact verification
WASM hash verification works at the Soroban protocol layer:
update_current_contract_wasmaccepts only a hash that corresponds to a code entry already uploaded to the ledger. TheExpectedWasmHashstaging adds a second, application-layer gate — both must pass for the upgrade to proceed.Compatibility impact
The
upgradeentrypoint signature is unchanged (BytesN<32>→Result<(), Error>). Callers must now callset_expected_wasm_hashfirst; an upgrade attempt without a staged hash returnsWasmHashMismatch(code 10). The new entrypoint and error code are additive.The upgrade event gains a second topic (admin address). Off-chain indexers that subscribed to
(Symbol("upgrade"),)should update their filter to(Symbol("upgrade"), admin).Test evidence
Six new tests, all passing (
cargo test— 52 passed, 0 failed, 3 pre-existing ignores untouched):test_upgrade_requires_expected_hash_to_be_stagedWasmHashMismatchtest_upgrade_mismatch_is_rejectedWasmHashMismatchtest_upgrade_state_preserved_on_mismatchtest_upgrade_succeeds_with_matching_hashtest_set_expected_wasm_hash_without_auth_failstest_upgrade_event_payload(upgrade, admin)with hash as data (previously ignored, now active)Existing
test_upgrade_without_auth_failscontinues to pass. No unrelated tests were modified or skipped.