|
| 1 | +# Dusk Contract Standards Audit Packet |
| 2 | + |
| 3 | +This document is the auditor-facing entry point for the Dusk-native standards |
| 4 | +layer. It captures the review scope, security assumptions, invariant matrix, |
| 5 | +validation commands, and known residual risks for this branch. |
| 6 | + |
| 7 | +## Scope |
| 8 | + |
| 9 | +In scope: |
| 10 | + |
| 11 | +- `standards/dusk-contract-standards/src/**` |
| 12 | +- `standards/examples/authorization_counter` |
| 13 | +- `standards/examples/drc20_roles_pausable` |
| 14 | +- `standards/examples/drc721_collection` |
| 15 | +- `standards/examples/multisig_controller` |
| 16 | +- `standards/examples/proxy_counter` |
| 17 | +- `scripts/dusk-contract-standards-local-smoke.sh` |
| 18 | +- `standards/dusk-contract-standards/tests/**` |
| 19 | + |
| 20 | +Out of scope: |
| 21 | + |
| 22 | +- Dusk protocol consensus, wallet, node, prover, and VM internals. |
| 23 | +- Forge macro implementation correctness, except where the generated |
| 24 | + data-driver and Wasm artifacts are exercised through this repository's tests. |
| 25 | +- Economic policy choices such as exact upgrade delays, role assignment |
| 26 | + procedures, and marketplace royalty enforcement. |
| 27 | +- Compatibility with Ethereum/OpenZeppelin ABIs where Dusk-native semantics |
| 28 | + deliberately diverge. |
| 29 | + |
| 30 | +## Security Model |
| 31 | + |
| 32 | +The standards layer assumes: |
| 33 | + |
| 34 | +- `CallContext::current()` correctly reports observed Moonlight root callers |
| 35 | + through `public_sender`, including normal transfer-contract entrypoint |
| 36 | + execution, and reports immediate contract callers for nested calls. |
| 37 | +- Phoenix calls do not expose a stable caller identity. Phoenix authorization |
| 38 | + must therefore be an explicit Schnorr signature over an `AuthorizedAction`. |
| 39 | +- Dusk signature primitives verify according to their upstream implementations. |
| 40 | +- `abi::self_id()`, `abi::block_height()`, `abi::emit`, and contract-call |
| 41 | + routing are provided correctly by the host. |
| 42 | +- Client encoders use the same schema and byte serialization as the generated |
| 43 | + Forge data-drivers. |
| 44 | + |
| 45 | +The layer does not assume: |
| 46 | + |
| 47 | +- an Ethereum-like `msg.sender` for Phoenix; |
| 48 | +- delegatecall-style proxy storage behavior; |
| 49 | +- off-chain signatures are safe without chain/contract/domain/action/payload |
| 50 | + binding; |
| 51 | +- zero principals are valid owners, recipients, spenders, operators, or admins. |
| 52 | + |
| 53 | +## Invariant Matrix |
| 54 | + |
| 55 | +| ID | Invariant | Primary Coverage | |
| 56 | +| --- | --- | --- | |
| 57 | +| AUTH-1 | Signed actions bind chain id, contract, domain, action id, payload hash, nonce, principal, and expiry before nonce/replay consumption. | `tests/primitives.rs`, `tests/properties.rs`, VM test, local-node smoke | |
| 58 | +| AUTH-2 | Rejected envelope, signature, expiry, role, owner, admin, and replay-key cases do not advance nonce/replay state. | property negative matrices, VM test, local-node smoke | |
| 59 | +| AUTH-3 | Phoenix authorization proves control of a Schnorr key principal and never relies on observed caller identity. | primitives, properties, signed auth example, local-node smoke | |
| 60 | +| AUTH-4 | Moonlight owners can authorize by observed root caller or signed BLS action; contract owners only by observed immediate contract caller. Transfer-contract entrypoint calls map to `public_sender`, while nested calls do not. | primitives, reference contracts, VM test, `core::context` unit tests | |
| 61 | +| MULTISIG-1 | Threshold multisig requires distinct owner quorum and rejects duplicate signers before nonce/replay consumption. | primitives | |
| 62 | +| MULTISIG-2 | Observed Moonlight/contract owners can count toward quorum; Phoenix owners require signed action approvals. | primitives | |
| 63 | +| MULTISIG-3 | Multisig owner and threshold maintenance requires current quorum and rejected changes leave state unchanged. | primitives | |
| 64 | +| MULTISIG-4 | Standalone controller proposal/confirmation requires distinct owners, rejects non-owners and duplicate confirmations without nonce movement, expires stale proposals, tombstones executed ids, and clears stale pending operations after authority-removing changes. | primitives, properties, VM test | |
| 65 | +| MULTISIG-5 | A contract-owned proxy/admin path can be controlled by a 2-of-3 standalone multisig controller through observed inter-contract caller context, while direct owner calls to the governed target fail. | VM test, local-node smoke | |
| 66 | +| ACCESS-1 | Role grants/revokes are admin-gated, reject zero accounts, and emit typed events when state changes. | primitives, DRC20 reference, data-driver event decoding | |
| 67 | +| ACCESS-2 | Owner and owner-set initialization reject zero principals atomically. | primitives, properties | |
| 68 | +| PAUSE-1 | Reference pausable DRC20/DRC721 pause all balance-changing operations. | primitives, VM test, local-node smoke | |
| 69 | +| PAUSE-2 | Approvals remain available while paused and do not move balances or ownership. | VM test, local-node smoke | |
| 70 | +| TOKEN20-1 | DRC20 total supply equals the modeled sum of balances after arbitrary operation sequences. | property state-machine model | |
| 71 | +| TOKEN20-2 | DRC20 transfer, transfer-from, allowance, mint, burn, cap, and vote checkpoint failures are atomic. | primitives, properties | |
| 72 | +| TOKEN721-1 | DRC721 owner, balance, approval, operator, enumerable, and total-supply views remain internally consistent. | property state-machine model | |
| 73 | +| TOKEN721-2 | DRC721 mint, transfer, burn, approval, operator, and royalty failures are atomic. | primitives, properties | |
| 74 | +| ROYALTY-1 | Royalty receivers cannot be zero, basis points cannot exceed 10,000, and overflow-prone quotes do not mutate state. | primitives, properties | |
| 75 | +| NONCE-1 | Nonce domains are independent, monotonic, import/export safe, and atomic on rejection. | primitives, properties | |
| 76 | +| REPLAY-1 | Replay keys cannot be reused for the same principal and import/export is monotonic. | primitives, properties | |
| 77 | +| PROXY-1 | Upgrade admin rejects zero admin/implementation and binds signed admin actions to exact payloads. | primitives, VM test, local-node smoke | |
| 78 | +| PROXY-2 | Prepare, activate, cancel, rollback, and rollback-finalization obey delay/window state transitions and emit events. | primitives, properties, data-driver event decoding | |
| 79 | +| TIMELOCK-1 | Timelock scheduling/execution/cancellation follows delay and predecessor constraints. | primitives, properties | |
| 80 | +| TIMELOCK-2 | Controller minimum-delay changes are self-governed through scheduled operations, not direct arbitrary admin mutation. | primitives, properties | |
| 81 | +| REENTRANCY-1 | Scoped guard rejects nested entry and resets after panics. | primitives | |
| 82 | +| ABI-1 | Forge data-driver schemas load for every reference contract. | `tests/data_driver_fuzz.rs` | |
| 83 | +| ABI-2 | Function inputs JSON-encode, decode, and re-encode consistently; malformed and mutated payloads do not crash. | `tests/data_driver_fuzz.rs` | |
| 84 | +| ABI-3 | Function outputs and typed events decode from rkyv and mutated output/event payloads do not crash readers. | `tests/data_driver_fuzz.rs` | |
| 85 | +| NODE-1 | Real local Rusk deployment accepts valid signed calls and rejects replay, bad payload, expired action, pause, and proxy replay cases while preserving expected state. | `scripts/dusk-contract-standards-local-smoke.sh` | |
| 86 | + |
| 87 | +## Reproducible Validation |
| 88 | + |
| 89 | +Run the audit-grade validation pass: |
| 90 | + |
| 91 | +```sh |
| 92 | +./scripts/dusk-contract-standards-audit-grade.sh |
| 93 | +``` |
| 94 | + |
| 95 | +Default intensity: |
| 96 | + |
| 97 | +- `STANDARDS_PROPTEST_CASES=8192` |
| 98 | +- `STANDARDS_PROPTEST_MAX_SHRINK_ITERS=16384` |
| 99 | +- `STANDARDS_DATA_DRIVER_FUZZ_CASES=4096` |
| 100 | +- `STANDARDS_DATA_DRIVER_FUZZ_SHRINK_ITERS=8192` |
| 101 | + |
| 102 | +For a funded local-node run, start Rusk and provide wallet/node settings: |
| 103 | + |
| 104 | +```sh |
| 105 | +RUN_LOCAL_NODE_SMOKE=1 \ |
| 106 | +RUSK_URL=http://127.0.0.1:18080 \ |
| 107 | +RUSK_WALLET_BIN=/path/to/rusk-wallet \ |
| 108 | +WALLET_DIR=target/rusk-local-smoke/wallet \ |
| 109 | +WALLET_RESTORE_FILE=/path/to/wallet.dat \ |
| 110 | +./scripts/dusk-contract-standards-audit-grade.sh |
| 111 | +``` |
| 112 | + |
| 113 | +For dependency advisory scanning, install `cargo-audit` and opt in: |
| 114 | + |
| 115 | +```sh |
| 116 | +RUN_CARGO_AUDIT=1 ./scripts/dusk-contract-standards-audit-grade.sh |
| 117 | +``` |
| 118 | + |
| 119 | +## Focused Reviews |
| 120 | + |
| 121 | +- `docs/dusk-contract-standards-multisig-controller-review.md` |
| 122 | + |
| 123 | +## Auditor Checklist |
| 124 | + |
| 125 | +Reviewers should focus on: |
| 126 | + |
| 127 | +- whether the Dusk principal model is the right abstraction for Moonlight, |
| 128 | + Phoenix, and contract callers; |
| 129 | +- whether every public signed path uses action-bound helpers before nonce |
| 130 | + consumption; |
| 131 | +- whether single-owner admin paths should be replaced with |
| 132 | + `ThresholdMultisig` composition or standalone `MultisigController` |
| 133 | + ownership before production deployment; |
| 134 | +- whether payload-hash construction is unambiguous and domain separated enough |
| 135 | + for downstream wallets; |
| 136 | +- whether pause semantics match product/security expectations; |
| 137 | +- whether upgrade and timelock policies are sufficient for production systems; |
| 138 | +- whether any event is missing for an indexer-relevant state change; |
| 139 | +- whether generated data-driver schemas are acceptable as the client/wallet |
| 140 | + ABI source of truth. |
| 141 | + |
| 142 | +## Residual Risks |
| 143 | + |
| 144 | +The branch is audit-ready, not audit-complete. Remaining risk areas: |
| 145 | + |
| 146 | +- independent client encoders may still disagree with Forge data-drivers unless |
| 147 | + they are tested differentially; |
| 148 | +- Dusk runtime host-function behavior is assumed rather than proven here; |
| 149 | +- no formal verification is included for arithmetic or state machines; |
| 150 | +- dependency advisory scanning is optional unless `cargo-audit` is installed; |
| 151 | +- local-node smoke is deterministic and adversarial for defined invariants, but |
| 152 | + it is not a long-running network chaos test. |
0 commit comments