Skip to content

[Testing] Implement Global Contract Invariant Tests for Accounting, Streaming, and Protocol Solvency #28

Description

@KarenZita01

Description

The streaming_invariant_tests.rs (10KB) and stream_balance_property_tests.rs (25KB) test streaming invariants, but there are no invariant tests for the broader contract state. Critical invariants like "total token balance in contract should equal sum of all meter balances + dust accumulator + gas buffers + insurance pool + protocol fees" are not tested. The buffer_tests.rs (14KB) tests gas buffer operations but doesn't verify the global token accounting invariant.

Invariant gaps:

  • No total supply invariant test (tokens in contract == user funds + protocol funds + dust)
  • No streaming invariant (sum of all stream rates == total outflow rate)
  • No admin action invariant (admin can never increase their own balance directly)
  • No oracle invariant (oracle prices must always be positive and bounded)
  • No insurance pool invariant (pool outflows cannot exceed pool inflows + initial capital)
  • No debt invariant (total debt across all users <= total collateral locked)

Technical Context & Impact

  • Affected Components/Files: contracts/utility_contracts/src/streaming_invariant_tests.rs, contracts/utility_contracts/src/stream_balance_property_tests.rs, contracts/utility_contracts/src/buffer_tests.rs
  • Impact: Protocol Soundness, Financial Safety, Bug Detection

Step-by-Step Implementation Guide

  1. Create tests/invariant_tests.rs with InvariantChecker struct that holds a snapshot of the contract state
  2. Implement global accounting invariant: check_total_token_accounting(env) -> Result:
    • Read token balance via token::Client::balance
    • Iterate all meters and sum their balances
    • Add dust accumulator value
    • Add gas buffer total
    • Add insurance pool value
    • Add protocol fee accumulator
    • Assert: token_balance == sum(meter_balances) + dust + gas + insurance + fees
  3. Implement streaming invariant: check_streaming_invariant(env) -> Result:
    • Sum all active stream rates
    • Compare with actual token outflow rate from contract (delta balance / delta time)
    • Assert divergence < rounding tolerance
  4. Add invariant test to every test function: Call check_invariants(env, before_state, after_state) after each state mutation
  5. Write fuzz harness with invariant checking: Random operations sequence, check invariants after each operation
  6. Implement admin privilege invariant: Verify admin cannot directly transfer contract tokens to themselves or bypass fee logic
  7. Add CI invariant job: Run invariant tests with optimized build for faster execution

Verification & Testing Steps

  1. Run invariant tests on clean state (should pass)
  2. Intentionally introduce accounting bug -> invariant test should fail, proving it works
  3. Run with random fuzz sequences for 1000 iterations, checking invariants after each
  4. Verify invariants hold after all existing test scenarios
  5. Measure performance overhead of invariant checking (<2x test execution time)
  6. Run full test suite with invariants enabled: cargo test --package utility_contracts -- --include-ignored

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions