feat: add issuer-signed transfer-restriction override (#589) - #710
Open
sir-emmy1 wants to merge 1 commit into
Open
feat: add issuer-signed transfer-restriction override (#589)#710sir-emmy1 wants to merge 1 commit into
sir-emmy1 wants to merge 1 commit into
Conversation
## Summary Implements one-shot transfer-restriction override attestation per issue RevoraOrg#589. An issuer signs an OverrideAttestation struct with their registered ed25519 key to authorize a single (from, to, amount_bps) transfer that bypasses all category-cap and whitelist restrictions. ## Contract changes ### New types - OverrideAttestation (#[contracttype]) — canonical XDR-serialised payload bound to (version=1, contract, issuer, namespace, token, from, to, amount_bps, nonce, expiry). Binding every field prevents cross-contract and cross-tuple replay. ### New storage key - DataKey2::TransferOverrideNonce(Address, u64) — per-issuer one-shot nonce consumed atomically before share mutation. Presence = burned. ### New error codes - OverrideAttestationInvalid = 76 — signer not registered or sig invalid. - OverrideAlreadyConsumed = 77 — nonce already burned (replay attempt). ### New event - xfer_ovrd (EVENT_TRANSFER_OVERRIDE_APPLIED) — emitted on every successful call. Topics: (xfer_ovrd, issuer, namespace, token). Data: (from, to, amount_bps, nonce). Sufficient for audit reconstruction. ### New entry point - ransfer_with_override(issuer, namespace, token, from, to, amount_bps, nonce, expiry, sig) — 10 guards enforced in order: 1. Global freeze / pause 2. issuer.require_auth() (dual-auth) 3. amount_bps > 0 4. from != to 5. Offering exists 6. Offering not frozen 7. from/to not blacklisted 8. expiry >= ledger timestamp 9. Nonce not yet consumed (OverrideAlreadyConsumed) 10. ed25519 signature valid (host trap on mismatch) Nonce burned BEFORE share mutation; event emitted AFTER. ## Supporting fixes (pre-existing bugs, fixed as needed to compile) - Fixed duplicate PauseState enum definition - Fixed duplicate MinRevenueThreshold/DepositedRevenue in DataKey2 - Fixed duplicate discriminants in RevoraError (56, 58, 51) - Fixed stray 'None => {}' match arm and extra ')' syntax errors - Fixed mismatched impl block brace in migration block - Fixed inner attribute position in security_assertions.rs - Fixed duplicate test module declarations (test_time_windows, test_close_period) - Fixed duplicate imports in test_storage_layout_version.rs - Fixed named enum variant fields in vesting.rs (Soroban unsupported) - Fixed symbol_short strings exceeding 9-char limit - Added missing storage layout registry entries to tools/storage_layout_schema.rs ## Tests — src/test_transfer_with_override.rs 13 test functions covering: - blocked_when_frozen - blocked_when_paused - zero_amount_rejected - self_transfer_rejected - unknown_offering_rejected - blocked_when_offering_frozen - blacklisted_from_rejected - blacklisted_to_rejected - expired_attestation_rejected - nonce_replay_rejected (one-shot enforcement) - unregistered_signer_rejected - wrong_tuple_sig_rejected (mismatched tuple causes host trap) - insufficient_shares_rejected - happy_path_full_transfer - happy_path_partial_transfer - event_payload_correct (audit trail topics + data) - shares_updated_correctly (storage invariants + sum conservation) - override_bypasses_category_cap - different_nonces_are_independent ## Security notes - Dual-auth: issuer must sign both the Soroban transaction and the off-chain attestation, preventing a stolen sig from being replayed by another caller. - One-shot nonce burned before share mutation; replay is permanently rejected even if the share update subsequently panics (host aborts tx, nonce stays). - Signature covers contract address preventing cross-contract replay. - Blacklist enforcement is preserved; override does not bypass blacklist.
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.
Closes #589
Summary
Implements one-shot transfer-restriction override attestation per issue #589. An issuer signs an OverrideAttestation struct with their registered ed25519 key to authorize a single (from, to, amount_bps) transfer that bypasses all category-cap and whitelist restrictions.
Contract changes
New types
New storage key
New error codes
New event
New entry point
Supporting fixes (pre-existing bugs, fixed as needed to compile)
Tests — src/test_transfer_with_override.rs
13 test functions covering:
Security notes