Overview
The credit accrual math has several invariants that must hold for all valid inputs:
Monotonicity : credits never decrease as ledgers_elapsed increases
Zero boost = unboosted : compute_credits(amount, 0, any_multiplier, rate, elapsed) == amount * rate * elapsed
Full boost (100%) with multiplier 1 = unboosted : compute_credits(amount, 100, 1, rate, elapsed) == amount * rate * elapsed
Boost always >= 1x : credits with boost enabled are always >= credits without boost (given multiplier >= 1)
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
Overview
The credit accrual math has several invariants that must hold for all valid inputs:
ledgers_elapsedincreasescompute_credits(amount, 0, any_multiplier, rate, elapsed) == amount * rate * elapsedcompute_credits(amount, 100, 1, rate, elapsed) == amount * rate * elapsedThese invariants are hard to test exhaustively with hand-crafted cases. Fuzz testing is the right tool.
Implementation
Use
cargo-fuzzor thearbitrarycrate withproptest:Acceptance Criteria
proptestadded as dev-dependencycargo testproptestconfiguration setscases = 10000for thorough coverage#[test])