Skip to content

Add automated fuzz testing for credit calculation invariants #37

Description

@prodbycorne

Overview

The credit accrual math has several invariants that must hold for all valid inputs:

  1. Monotonicity: credits never decrease as ledgers_elapsed increases
  2. Zero boost = unboosted: compute_credits(amount, 0, any_multiplier, rate, elapsed) == amount * rate * elapsed
  3. Full boost (100%) with multiplier 1 = unboosted: compute_credits(amount, 100, 1, rate, elapsed) == amount * rate * elapsed
  4. Boost always >= 1x: credits with boost enabled are always >= credits without boost (given multiplier >= 1)
  5. No overflow (after the fix in issue farming-pool: credit calculation can overflow i128 for large stakes and elapsed ledgers #12): no valid input combination should produce a negative result

These invariants are hard to test exhaustively with hand-crafted cases. Fuzz testing is the right tool.

Implementation

Use cargo-fuzz or the arbitrary crate with proptest:

# Install proptest
[dev-dependencies]
proptest = "1"
// soroban/contracts/farming-pool/src/test.rs

proptest! {
    #[test]
    fn credits_are_non_negative(
        amount in 1i128..=1_000_000_000_000i128,
        allocation_pct in 0u32..=100,
        multiplier in 1u32..=10,
        credit_rate in 1i128..=1_000_000i128,
        ledgers_elapsed in 0u32..=1_036_800,  // up to 60 days
    ) {
        let credits = compute_credits(amount, allocation_pct, multiplier, credit_rate, ledgers_elapsed);
        prop_assert!(credits >= 0);
    }

    #[test]
    fn boost_never_reduces_credits(
        amount in 1i128..=1_000_000_000i128,
        allocation_pct in 1u32..=100,
        multiplier in 2u32..=10,  // boost only helps when multiplier > 1
        credit_rate in 1i128..=1_000i128,
        ledgers_elapsed in 1u32..=518_400,
    ) {
        let boosted = compute_credits(amount, allocation_pct, multiplier, credit_rate, ledgers_elapsed);
        let unboosted = compute_credits(amount, 0, multiplier, credit_rate, ledgers_elapsed);
        prop_assert!(boosted >= unboosted);
    }
}

Acceptance Criteria

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignsecuritySecurity vulnerability or hardeningtestingTests and test 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