Add seeded accounting conservation campaigns
🔎 Visual review of33516874: explainer, review flags and prompts to ask an AI about the change
Swap, liquidity, refund, and vault transitions can violate financial obligations even when each individual output looks valid. This change adds seeded property campaigns under a new AccountingPropertySuite in x/thorchain. Each campaign checks pool, Reserve, pending, paid, and vault balances against an independent accounting oracle.
The campaigns cover:
- Pool flow: random add, split-arrival, and withdraw sequences, ending with a full 10,000-bps withdrawal that checks the basis-point cap and gas kept in the pool. An add with a mismatched asset address must be rejected without changing state. A withdrawal that rounds to zero may be rejected only if state is untouched. The campaign fails on any add rejection other than dust that rounds to zero LP units. It also fails if the run never did an add, a split completion, and a withdrawal, so a handler that rejects every deposit cannot pass.
- Swap flow: swaps in both directions and three-part sub-swap sequences, checking that pool balances plus the RUNE fee sent to Reserve are conserved and that LP units do not change. The sub-swaps call the swapper directly, so they cover pool arithmetic and streaming event counters only. A swap to the same asset must be rejected with no pool, event, or outbound change.
- Persisted streaming swap: a V2 RUNE→ETH stream driven block by block through the advanced swap queue. Each block, the stored
State.In/State.Outmust match the pool and Reserve deltas. There must be no early outbound, exactly one final outbound of the released asset to the destination, and no repeat payout after the stream completes. - Pending liquidity refund: a staged deposit is a refundable liability, and repeating the withdrawal must not pay it twice.
- Native deposit pending conservation: a native RUNE
ADDmoves exactly the deposit from the account to Asgard and records it as pending on both the pool and the LP. A deposit with a bad memo must leave balances, pool, LP, and events unchanged. - Vault funds transfer persistence: coins debited from a retiring Asgard record and credited to an active one keep the same total after a keeper round-trip, and the pool stays unchanged. This checks bookkeeping only; it does not schedule or observe a migrate outbound.
- Refund outbound obligation: a refund creates one bounded outbound across the whole
TxOutDelayMaxscheduling window. Retrying the same inbound must not create a second liability, and the vault's coin stays put until the outbound is observed. - Seed 15 regression: a 1-bps withdrawal of 664 units that rounds to zero must be rejected without changing state.
- Oracle self-check: a one-unit change to any ledger term must be detected.
Seeds come from THORNODE_ACCOUNTING_SEED (a single seed) or THORNODE_ACCOUNTING_SEEDS (1–256). The default is four seeds, one of which is seed 15. scripts/test-accounting-property.sh runs a fast (4 seeds) or campaign (32 seeds) pass. A new scheduled-only accounting-properties-deep CI job runs the campaign pass and keeps its log as an artifact.
Validation: The four-seed fast and 32-seed campaign runs passed locally; the integrated root unit job passed. This topic is also included in draft !5181 (closed). This MR will run its own CI against develop; review and merge coordination should avoid landing the same change twice.