Skip to content

add state-machine tests and fuzz harness for lifecycle/payout invariants#130

Open
Adeolu01 wants to merge 1 commit into
TevaLabs:mainfrom
Adeolu01:testing-fuzzing
Open

add state-machine tests and fuzz harness for lifecycle/payout invariants#130
Adeolu01 wants to merge 1 commit into
TevaLabs:mainfrom
Adeolu01:testing-fuzzing

Conversation

@Adeolu01

@Adeolu01 Adeolu01 commented Jun 1, 2026

Copy link
Copy Markdown

Add deterministic state-machine tests and proptest fuzz harness for lifecycle and payout invariants

closes #103
closes #102

Changes

contracts/src/tests/state_machine_tests.rs (new)

Introduces a 34-test state-machine suite that documents the full protocol
transition graph as inline comments and encodes every edge as a concrete test.

Allowed transitions covered (both UpDown and Precision modes):

  • Uninit → Idle via initialize()
  • Idle → Betting via create_round() (both modes)
  • Betting → Betting via place_bet() / place_precision_prediction() (with position/event assertions)
  • Betting / Locked → Idle via cancel_round() (with refund and event assertions)
  • Resolvable → Idle via resolve_round() (with pending-winnings and event assertions)
  • Idle (post-resolve / post-cancel) → Betting via create_round()
  • Any → Paused via pause_contract(); Paused → previous state via unpause_contract()
  • Idle → Idle via claim_winnings() when pending > 0

Rejected transitions (each asserts the exact error variant):

  • Uninit → AdminNotSet on create_round()
  • Idle → NoActiveRound on resolve_round() and place_bet()
  • Idle → RoundNotCancellable on cancel_round()
  • Betting (before end_ledger) → RoundNotEnded on resolve_round()
  • Betting → RoundAlreadyActive on create_round()
  • Cancelled → RoundNotCancellable on cancel_round()
  • Cancelled → NoActiveRound on resolve_round()
  • Paused → ContractPaused on create_round(), place_bet(), place_precision_prediction(), resolve_round(), claim_winnings()
  • UpDown round → WrongModeForPrediction on place_precision_prediction()
  • Precision round → WrongModeForPrediction on place_bet()

Regression cases included: sequential round-id monotonicity, cross-round nonce
scope, and precision mode full lifecycle.

contracts/src/tests/fuzz_harness.rs (new)

Adds 8 proptest suites (64–256 cases each by default; overridable via
PROPTEST_CASES) covering five encoded invariants:

  1. Pot conservation — total pending winnings after resolution ≤ total pool (UpDown and Precision)
  2. No negative balances — every user balance and pending amount ≥ 0 after any operation
  3. Cancellation full refund — sum(pending_winnings) == sum(stakes) after cancel_round (UpDown and Precision)
  4. Claim idempotency — a second claim_winnings call returns 0 and leaves balance unchanged
  5. Multi-round state isolation — new round starts with empty pools and no carried-over positions

Additional fuzz scenarios:

  • Variable participant count (1–4 up-side, 1–4 down-side) for UpDown rounds
  • Variable participant count (3 fixed with spread predictions) for Precision rounds

contracts/src/tests/mod.rs

Registers fuzz_harness and state_machine_tests modules.

contracts/src/tests/cost_benchmarks.rs

Fixes a pre-existing compiler error (let → let mut for Budget variable).

.github/workflows/ci.yml

Adds a fuzz-harness CI job that runs the harness with PROPTEST_CASES=256 and
PROPTEST_DISABLE_FAILURE_PERSISTENCE=true for deterministic, bounded CI
execution. The job is wired into ci-success so a fuzz regression blocks merge.

Run the fuzz harness locally with more cases:
PROPTEST_CASES=512 cargo test --package xelma-contract fuzz_harness -- --nocapture

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Testing: implement deterministic protocol state-machine tests Testing/fuzzing: add fuzz harness for lifecycle and payout invariants

1 participant