# Echidna — Smart Contracts के लिए Property-Based Fuzzing
Manual testing से bugs miss हो जाते हैं। Echidna random inputs generate करके edge cases automatically test करता है।
Fuzzing क्या है?
Fuzzing = random/malformed inputs से program break करने की कोशिश।
Traditional testing:
function testTransfer() public {
token.transfer(alice, 100);
assertEq(token.balanceOf(alice), 100);
}
Fuzzing:
function testTransfer(address to, uint amount) public {
// Echidna automatically tries:
// - amount = 0
// - amount = type(uint).max
// - to = address(0)
// - to = address(this)
// ... और हजारों combinations
}
Echidna Installation
# Homebrew (macOS)
brew install echidna
# Docker
docker pull trailofbits/echidna
# Binary (Linux/Windows)
# GitHub releases से download करें
Basic Example: Token Contract
contract Token {
mapping(address => uint) public balances;
uint public totalSupply;
function mint(address to, uint amount) public {
balances[to] += amount;
totalSupply += amount;
}
function burn(address from, uint amount) public {
balances[from] -= amount;
totalSupply -= amount;
}
}
Echidna Test Contract
contract TokenTest is Token {
// Property: total supply = sum of balances
function echidna_total_supply_correct() public view returns (bool) {
return totalSupply >= balances[msg.sender];
}
// Property: balances never negative (implicit — Solidity reverts)
// Property: no overflow
function echidna_no_overflow() public view returns (bool) {
return totalSupply <= type(uint128).max;
}
}
Run करें
echidna-test token.sol --contract TokenTest
Output:
echidna_total_supply_correct: failed!
Call sequence:
mint(0x0, 1000)
burn(0x0, 500)
mint(0x10000, 2000)
Echidna ने bug find किया! Total supply का calculation multiple addresses के साथ break हो रहा है।
Property Types
1. Invariants
Always true होनी चाहिए:
function echidna_vault_solvent() public view returns (bool) {
return vault.totalAssets() >= vault.totalDebt();
}
2. State Transitions
Valid states:
enum State { Pending, Active, Closed }
State public state;
function echidna_valid_state() public view returns (bool) {
return uint(state) <= 2;
}
3. Conservation Laws
function echidna_conservation() public view returns (bool) {
return address(this).balance + withdrawnAmount == initialBalance;
}
Real-World Example: AMM Pool
contract AMMPool {
uint public reserveA;
uint public reserveB;
uint public constant k;
constructor() {
k = 1000000 * 1e18;
reserveA = 1000 * 1e18;
reserveB = 1000 * 1e18;
}
function swap(uint amountIn, bool aForB) public {
if (aForB) {
uint amountOut = reserveB - (k / (reserveA + amountIn));
reserveA += amountIn;
reserveB -= amountOut;
} else {
uint amountOut = reserveA - (k / (reserveB + amountIn));
reserveB += amountIn;
reserveA -= amountOut;
}
}
}
Properties
contract AMMTest is AMMPool {
// Property 1: Constant product
function echidna_constant_product() public view returns (bool) {
return reserveA * reserveB >= k;
}
// Property 2: Reserves never zero
function echidna_reserves_positive() public view returns (bool) {
return reserveA > 0 && reserveB > 0;
}
// Property 3: No free money
uint initialValue;
function echidna_no_profit() public view returns (bool) {
return reserveA + reserveB <= initialValue * 2;
}
}
Configuration: echidna.yaml
testMode: assertion
testLimit: 50000
seqLen: 100
contractAddr: "0x00a329c0648769A73afAc7F9381E08FB43dBEA72"
deployer: "0x10000"
sender: ["0x10000", "0x20000", "0x30000"]
Advanced: Corpus & Coverage
Echidna interesting inputs save करता है:
echidna-test contract.sol --corpus-dir=corpus
Subsequent runs में:
- Corpus से seeds use होते हैं
- Faster bug discovery
- Regression testing
Coverage Report
echidna-test contract.sol --coverage
यह HTML report generate करता है जो uncovered branches दिखाता है।
Echidna vs Foundry Fuzz
| Feature | Echidna | Foundry |
|---|---|---|
| Input Generation | Smarter (grammar-based) | Random |
| State Management | Persistent | Per-test reset |
| Speed | Slower | Faster |
| Learning Curve | Steeper | Easier |
Recommendation: Dono use करें।
- Foundry fuzz — quick unit tests
- Echidna — deep property testing
Common Pitfalls
1. Weak Properties
// ❌ Too weak — हमेशा true
function echidna_weak() public view returns (bool) {
return true;
}
// ✅ Meaningful
function echidna_strong() public view returns (bool) {
return invariantCheck();
}
2. Not Enough Sequences
Default testLimit कम हो सकता है:
testLimit: 100000 # increase
3. Missing Preconditions
function swap(uint amountIn, bool aForB) public {
require(amountIn > 0); // precondition add करें
// ...
}
निष्कर्ष
Echidna smart contract testing में revolution है:
- Automated edge case discovery
- Property-based approach
- Real bugs in production code
हर serious Solidity project को Echidna use करना चाहिए। यह manual testing की limitations को eliminate करता है।
अगला step: अपने contracts में invariants define करें और Echidna run करें। आप surprised होंगे कितने bugs मिलते हैं।