sec(vault): nonce-bound replay protection on set_authorized_caller (#419)#469
Merged
greatest0fallt1me merged 1 commit intoJun 26, 2026
Conversation
Adds a monotonic u64 nonce (StorageKey::AuthorizedCallerNonce) that the owner must supply on every set_authorized_caller call. A stale or replayed nonce is rejected with VaultError::StaleNonce, preventing a captured owner signature from being re-broadcast to reinstate a stale authorized_caller. Also adds VaultError::StaleNonce (code 30), a get_authorized_caller_nonce view function, the missing vault-self-address guard, and 8 new tests covering: default nonce, first rotation, stale/future nonce rejection, sequential increments, u64::MAX wrap, no-advance on failure, and event nonce emission. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@abore9769 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
3 tasks
Contributor
|
nonce-binding set_authorized_caller against replay is a real hardening win. i bumped your StaleNonce discriminant to 32 to avoid colliding with variants that landed in parallel — merged 👍 |
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 #419
VaultError::StaleNonce(code 30) — new error returned when the supplied nonce doesn't match the stored nonceStorageKey::AuthorizedCallerNonce— monotonicu64stored in instance storage, incremented on every successful rotationset_authorized_caller(new_caller, expected_nonce)— signature extended withexpected_nonce: u64; also adds the missing vault-self-address guardget_authorized_caller_nonce()— new view function returning the current nonce (defaults to0)set_authorized_callernow carries(old_caller, new_caller, consumed_nonce)How replay protection works
get_authorized_caller_nonce()(or assumes0on first use).set_authorized_caller(new_caller, expected_nonce).expected_nonce == stored_nonce; returnsStaleNonceif not.wrapping_add(1).A captured owner signature is permanently invalidated after one successful rotation because the stored nonce has advanced.
Test plan
set_authorized_caller_nonce_default_is_zero— nonce is0before first useset_authorized_caller_first_rotation_with_nonce_zero— nonce 0 succeeds, stored nonce becomes 1set_authorized_caller_stale_nonce_rejected— replay of consumed nonce returnsStaleNonceset_authorized_caller_future_nonce_rejected— future nonce returnsStaleNonceset_authorized_caller_nonce_increments_sequentially— three rotations increment correctlyset_authorized_caller_nonce_wraps_at_u64_max— wraps to0afteru64::MAXset_authorized_caller_failed_rotation_does_not_advance_nonce— failed call leaves nonce unchanged; correct nonce still works afterset_authorized_caller_event_emits_nonce— event data includes consumed nonceset_authorized_caller_vault_address_fails— vault self-address guard (converted from broken#[should_panic]totry_+ error assertion; now passes)🤖 Generated with Claude Code