Skip to content

Add property/invariant tests for vault deduct and batch_deduct accounting #406

@greatest0fallt1me

Description

@greatest0fallt1me

Description

INVARIANTS.md states the core vault property: VaultMeta.balance is always >= 0 and <= i128::MAX, and lists deduct / batch_deduct among the balance-mutating functions. The existing suite (contracts/vault/src/test.rs, test_idempotency.rs) exercises specific cases, but there are no randomized property/invariant tests that assert the accounting holds across arbitrary input sequences. This issue adds property-based tests that pin the documented invariants for the deduction path.

The contract logic to validate lives in deduct (contracts/vault/src/lib.rs:637) and batch_deduct (:725), where the batch performs a full validation pass — accumulating total and running with checked_add/checked_sub (:766:767) — before any transfer.

Requirements and context

Functional (properties to assert)

  • Over any sequence of valid deduct calls, the sum of successfully deducted amounts never exceeds the initial deposited balance, and balance() is never negative.
  • For batch_deduct over a random Vec<DeductItem> (within MAX_BATCH_SIZE = 50), the final balance equals initial_balance - sum(item.amount) when the batch succeeds, and equals the unchanged initial balance when any item fails validation (atomicity — no partial application).
  • A batch_deduct whose cumulative total exceeds the balance returns VaultError::InsufficientBalance and mutates nothing.
  • i128 boundary inputs (amounts near i128::MAX, balances near the cap) never panic the host outside the documented Overflow error path.

Non-functional / repo conventions

  • This repo currently has no property-testing dependency. Add proptest as a [dev-dependencies] entry in contracts/vault/Cargo.toml (Soroban unit tests run on the host target, so proptest is fine for #[cfg(test)]). Keep generated case counts modest so cargo test and cargo tarpaulin stay within the 120s per-binary timeout configured in tarpaulin.toml.
  • Tests must use the existing test harness style (the soroban_sdk Env, registered token contract, and helpers already used in contracts/vault/src/test.rs).
  • New tests must not lower workspace coverage below the 95% gate.

Acceptance criteria

  • proptest added to contracts/vault/Cargo.toml dev-dependencies; build stays green.
  • A new contracts/vault/src/test_invariants.rs module (registered with #[cfg(test)] mod test_invariants; in lib.rs) holds the property tests.
  • Property: cumulative successful deductions never exceed deposits; balance never negative.
  • Property: batch_deduct is all-or-nothing — failure leaves balance exactly unchanged.
  • i128 boundary inputs are covered and only ever surface VaultError::Overflow / InsufficientBalance, never an unhandled host panic.
  • cargo test --workspace passes; ./scripts/coverage.sh stays >= 95%.

Suggested execution

1. Fork the repo and create a branch

git checkout -b test/vault-deduct-invariants

2. Implement changes

  • contracts/vault/Cargo.toml — add proptest under [dev-dependencies].
  • contracts/vault/src/lib.rs — register the new test module.

3. Write/extend tests in contracts/vault/src/test_invariants.rs:

  • a proptest! generating random valid deduction sequences asserting the sum/non-negative properties;
  • a proptest! generating random Vec<DeductItem> (bounded by MAX_BATCH_SIZE) asserting exact post-balance on success and unchanged balance on validation failure;
  • explicit boundary cases at i128::MAX.

4. Test and commit

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
./scripts/check-wasm-size.sh
./scripts/coverage.sh

Example commit message

test(vault): add proptest invariants for deduct and batch_deduct accounting

Guidelines

  • Coverage must stay >= 95% (scripts/coverage.sh), CI-enforced via .github/workflows/coverage.yml.
  • Keep INVARIANTS.md accurate — if a property test reveals a doc gap, update the document and reference the new tests.
  • Keep generated case counts bounded so coverage runs inside the tarpaulin 120s timeout.
  • Timeframe: 96 hours.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GRANTFOX OSSGrantFox OSS programMAYBE REWARDEDGrantFox — potentially rewardedOFFICIAL CAMPAIGNGrantFox official campaignadvancedHigh complexity / deep contextsmart-contractSoroban / Rust contract worktestingTests and coverage

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions