test(security): chaos failure-injection lifecycle test (#468) + large-history treasury load test (#467) - #514
Merged
misrasamuelisiguzor-oss merged 2 commits intoAug 31, 2026
Conversation
…lement-workflow) Several feature PRs landed on main with code that references symbols that were never added (or were dropped in a later merge), leaving three of the four contract crates unable to compile. This restores the missing pieces so the workspace builds again, which is a prerequisite for adding any new tests under contracts/treasury/tests/ (issues WHEELBACK#467 / WHEELBACK#468). crates/multisig: - Append the three TreasuryError variants that landed code already depends on but the enum never gained (append-only, per scripts/check-enum-ordering.sh): WithdrawalLimitExceeded = 35 (WHEELBACK#455, referenced by the withdrawal velocity limit) InvalidSplitRatio = 36 (WHEELBACK#456, panicked by resolve_dispute_split) ForceCancelNotAllowed = 37 (WHEELBACK#457, panicked by force_cancel_settlement) contracts/compliance: - Add DataKey::LastBulkAllow / LastBulkBlock, keyed per admin, which the bulk-op cooldown code (WHEELBACK#454) already reads and writes. contracts/treasury: - deposits.rs: fix DataKey::Balance(..) call in deposit_one that passed one key component instead of (holder, token_contract), and add the missing enforce_withdrawal_limit helper (no-op when unset/<=0, rolling per-address window otherwise) that withdraw_all already calls. - disputes.rs: resolve_dispute was missing its trailing Ok(()); add it. resolve_dispute_split transferred the split but never marked the dispute ResolvedSplit / recorded claimant_share_bps / emitted dispute_resolved_split / released the settlement hold - complete it to match its own doc comment so a split resolution does not leave the settlement stuck OnHold forever. contracts/settlement-workflow: - Merge the two conflicting `enum DataKey` definitions (ComplianceId / TreasuryId from WHEELBACK#364, ExecutedSettlements from WHEELBACK#373) into one. - Re-add the `multisig` dependency (dropped in a merge) that src/lib.rs needs for `use multisig::TreasuryError`; it carries no #[contractimpl] so it links no foreign wasm exports. tests (fallout of the enum change): - multisig_version_lock_test.rs: pin the new discriminants (34..=37) and add the new variants to the exhaustive match. - multisig_quorum_property_test.rs: proptest's prop_assert_eq! message arg does not support inline format captures; pass them positionally. cargo build --workspace, cargo clippy --workspace -- -D warnings, and the full treasury test suite pass. Pre-existing breakage NOT addressed here (unrelated to WHEELBACK#467/WHEELBACK#468): settlement-workflow's two test files, a couple of stale treasury/integration tests, and repo-wide `cargo fmt --check` drift across 26 files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D1wdxpKibGJsVzbB4EEnMg
… chaos test (WHEELBACK#468) WHEELBACK#467 - contracts/treasury/tests/large_history_load_test.rs Builds a treasury instance carrying 1000+ historical settlements and records measured instruction costs for the key read/scan entrypoints at that scale, complementing WHEELBACK#97's pagination-correctness coverage (whose largest real corpus is 50 settlements): - get_pending_settlements_page stays correct at 1000+ entries: first page, deep mid-history page, window overrunning the end, start past the end, and executed entries interspersed and skipped. - Cost vs. history size: full-history scan for a 50-entry tail page measured at 500 and 1000 settlements; asserts a loose runaway- regression bound and prints the numbers. - Finding recorded (not asserted away): the page scan early-breaks once the page is full, so a deep offset costs ~O(start+limit) and approaches a full-history scan - the same "cost scales with total accumulated history" shape as resolve_dispute_dos_test.rs. A compacted pending index would remove it; filed as a follow-up rather than fixed here. - resolve_dispute cost measured with 1000+ settlements as background load (the dimension the signer-rotation race issue cares about). WHEELBACK#468 - contracts/treasury/tests/lifecycle_chaos_test.rs Replays WHEELBACK#83's full lifecycle (create_invoice -> mark_paid -> propose_settlement -> approve_settlement -> compliance.is_allowed -> execute_settlement -> release_escrow) once per cross-contract call boundary, forcing exactly that boundary to fail each time: ComplianceGate, ExecuteThresholdNotMet, ExecuteTokenNotAllowed, ExecuteSettlementOnHold, ReleaseEscrow, plus a None control. After every run it asserts the multi-contract system is left consistent: token conservation, all-or-nothing payout, merchant paid iff settlement Executed, settlement status never a limbo value, invoice never wedged (always Paid or Released), and escrow released only when the settlement executed. The release_escrow case also proves recovery: unpause + retry completes the lifecycle. No inconsistent state was found. cargo test -p comebackhere-treasury passes (both files: 12 tests). cargo clippy --workspace -- -D warnings passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D1wdxpKibGJsVzbB4EEnMg
|
@Tisan1000 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! 🚀 |
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 #467. Closes #468.
What this delivers
#468 — chaos-style failure-injection test across the full settlement lifecycle
contracts/treasury/tests/lifecycle_chaos_test.rsReplays issue #83's full end-to-end lifecycle
(
create_invoice → mark_paid → propose_settlement → approve_settlement → compliance.is_allowed → execute_settlement → release_escrow) once percross-contract call boundary, deliberately forcing exactly that one boundary to
fail each time, parameterized by a
FailurePointenum rather than rewriting theflow:
ComplianceGateis_allowedreturns falseExecuteThresholdNotMetexecute_settlement→ThresholdNotMetExecuteTokenNotAllowedTokenNotAllowedExecuteSettlementOnHoldexecute_settlement→SettlementOnHoldReleaseEscrowrelease_escrow→ContractPausedNoneAfter every run it asserts the multi-contract system is left consistent,
whichever boundary failed:
treasury_balance + merchant_balance == minted totalExecutedPending/OnHold/Executed— never a limbo valuePaidorReleased— a failedrelease_escrowleaves it retryable, not wedgedThe
ReleaseEscrowcase additionally proves recovery: unpause + retry completesthe lifecycle. No inconsistent state was found at any boundary.
#467 — large-scale load test: treasury with 1000+ historical settlements
contracts/treasury/tests/large_history_load_test.rs#97 (
settlement_pagination_test.rs) covers pagination correctness, but itslargest real corpus is 50 settlements. This builds a treasury with 1000+
settlements and records measured instruction costs for the key read/scan
entrypoints at that scale:
get_pending_settlements_pagestays correct at 1000+ entries — first page,deep mid-history page, window overrunning the end, start past the end,
executed entries interspersed and skipped.
measured at 500 and 1000 settlements (
~2xdata,~3xcost on the testhost — linear scan with per-read overhead that itself grows slowly; a loose
runaway-regression bound, plus the numbers are printed with
--nocapture).resolve_disputecost measured with 1000+ settlements as background load.Finding (documented, not silently patched)
get_pending_settlements_pageearly-breaks once the requested page is full, soa shallow page is cheap (~854K instr.) while a deep page over a 1000-entry
history costs ~O(
start + limit) and approaches a full scan (~102M instr.,~120× the shallow cost). Paginating a UI to the end of a large settlement
history is therefore not cheap — the same "cost scales with total accumulated
history" shape as the
resolve_disputeconcern inresolve_dispute_dos_test.rs.page_scan_cost_grows_with_offset_depthpins the current behaviour.Suggested follow-up: maintain a compacted pending-settlement index so
get_pending_settlements_pageis O(limit) regardless of offset.Prerequisite repair (first commit)
maindid not compile — three of the four contract crates referencedsymbols that were never added or were dropped in a merge. The first commit
restores them so tests under
contracts/treasury/tests/can build:TreasuryError::WithdrawalLimitExceeded = 35(Add a treasury withdrawal-limit feature: maximum amount withdrawable per time window #455),InvalidSplitRatio = 36(Add support for partial dispute resolution (splitting a disputed amount between claimant and counterparty) #456),ForceCancelNotAllowed = 37(Add an emergency admin-override entrypoint to force-cancel a stuck settlement #457) —code already referenced all three.
DataKey::LastBulkAllow/LastBulkBlock(Add a rate-limiting mechanism to compliance's batch operations proportional to caller weight/role #454 bulk-op cooldown).DataKey::Balance(..)arity indeposit_one; add the missingenforce_withdrawal_limithelper; giveresolve_disputeits trailingOk(());complete
resolve_dispute_splitso a split resolution actually marks thedispute
ResolvedSplit, recordsclaimant_share_bps, emitsdispute_resolved_splitand releases the settlement hold (per its own doccomment) instead of leaving the settlement stuck
OnHold.enum DataKeydefinitions;re-add the dropped
multisigdependency.multisig_version_lock_test.rs; fix aprop_assert_eq!format-arg bug inmultisig_quorum_property_test.rs.Verification
cargo build --workspace— passescargo clippy --workspace -- -D warnings— passescargo test -p comebackhere-treasury— passes (all 38 test files, incl. thetwo new ones: 12 tests), except the pre-existing failures below
Pre-existing breakage NOT addressed here (unrelated to #467/#468)
These were already broken on
main(masked by the crates not compiling); leftfor separate follow-ups:
contracts/treasury/tests/record_approval_duplicate_benchmark_test.rs(3 tests) — the test assumespropose_settlementdoesn't pre-record the proposer's approval; the implementation does.tests/tests/release_escrow_settlement_ordering_test.rs::settlement_proposed_before_release_still_executes_correctly_after—create_invoice(5_000_000, 5_100_000, …)is rejected withAmountPrecision.contracts/settlement-workflow/tests/settlement_workflow_test.rs— needs aSettlementWorkflowErrortype +pause/unpausethat were reverted out of the lib.contracts/settlement-workflow/tests/divergent_admin_test.rs— not Soroban code (tokio/chrono/HashMap); has never compiled.cargo fmt --all -- --check— 26 files carry pre-existing formatting drift.🤖 Generated with Claude Code