Babysitter certora-prover
Formal verification using Certora Prover with CVL specification language. Supports invariant rules, parametric verification, ghost variables, and counterexample analysis for mathematical proof of contract correctness.
install
source · Clone the upstream repo
git clone https://github.com/a5c-ai/babysitter
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/a5c-ai/babysitter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/library/specializations/cryptography-blockchain/skills/certora-prover" ~/.claude/skills/a5c-ai-babysitter-certora-prover && rm -rf "$T"
manifest:
library/specializations/cryptography-blockchain/skills/certora-prover/SKILL.mdsource content
Certora Formal Verification Skill
Formal verification of smart contracts using Certora Prover, providing mathematical proofs of contract correctness.
Capabilities
- CVL Specifications: Write Certora Verification Language specs
- Invariant Rules: Define and verify state invariants
- Parametric Rules: Write comprehensive property tests
- Ghost Variables: Track abstract state
- Counterexamples: Analyze verification failures
- Loop Handling: Configure loop invariants and unrolling
- Summarization: Abstract complex functions
Installation
# Install Java (required) sudo apt install openjdk-17-jdk # Install Certora CLI pip install certora-cli # Set API key export CERTORAKEY=<your-api-key> # Verify installation certoraRun --version
Project Setup
Directory Structure
project/ ├── contracts/ │ └── Token.sol ├── certora/ │ ├── conf/ │ │ └── token.conf │ └── specs/ │ └── token.spec └── foundry.toml
Configuration File
# certora/conf/token.conf { "files": ["contracts/Token.sol"], "verify": "Token:certora/specs/token.spec", "solc": "solc-0.8.20", "msg": "Token verification", "rule_sanity": "basic", "optimistic_loop": true, "loop_iter": 3 }
CVL Specification Language
Basic Rules
// certora/specs/token.spec methods { function balanceOf(address) external returns (uint256) envfree; function totalSupply() external returns (uint256) envfree; function transfer(address, uint256) external returns (bool); } // Invariant: balance never exceeds total supply invariant balanceUnderSupply(address user) balanceOf(user) <= totalSupply() // Rule: transfer preserves total supply rule transferPreservesTotalSupply(address to, uint256 amount) { env e; uint256 supplyBefore = totalSupply(); transfer(e, to, amount); uint256 supplyAfter = totalSupply(); assert supplyBefore == supplyAfter, "Total supply changed after transfer"; }
Parametric Rules
// Parametric rule: any function preserves an invariant rule anyFunctionPreservesInvariant(method f) { env e; calldataarg args; uint256 supplyBefore = totalSupply(); f(e, args); uint256 supplyAfter = totalSupply(); assert supplyBefore == supplyAfter, "Total supply changed"; }
Ghost Variables
// Ghost variable to track sum of all balances ghost mathint sumBalances { init_state axiom sumBalances == 0; } // Hook to update ghost on balance changes hook Sstore balances[KEY address user] uint256 newBalance (uint256 oldBalance) STORAGE { sumBalances = sumBalances + newBalance - oldBalance; } // Invariant using ghost invariant totalSupplyIsSumOfBalances() to_mathint(totalSupply()) == sumBalances
Function Summaries
// Summary for external calls methods { function _.transfer(address, uint256) external => DISPATCHER(true); function _.balanceOf(address) external returns (uint256) => DISPATCHER(true); } // Havoc summary (non-deterministic) methods { function externalCall() external => HAVOC_ECF; } // Constant summary methods { function getConstant() external returns (uint256) => ALWAYS(100); }
Loop Handling
// Loop invariant rule loopInvariant() { env e; // Configure loop unrolling require e.msg.sender != 0; // Loop iterations are bounded by config processArray(e); assert true; // Verify loop terminates }
Running Verification
Basic Run
# Run verification certoraRun certora/conf/token.conf # Run specific rule certoraRun certora/conf/token.conf --rule transferPreservesTotalSupply # Run with message certoraRun certora/conf/token.conf --msg "PR #123 verification"
Advanced Options
# Sanity checks certoraRun certora/conf/token.conf --rule_sanity basic # Optimistic loop handling certoraRun certora/conf/token.conf --optimistic_loop --loop_iter 5 # Multi-contract verification certoraRun contracts/Token.sol contracts/Staking.sol \ --verify Token:specs/token.spec # Debug mode certoraRun certora/conf/token.conf --debug
Interpreting Results
Verification Output
Rule: transferPreservesTotalSupply Status: VERIFIED ✓ Time: 45s Rule: balanceUnderSupply Status: VIOLATED ✗ Counterexample: - user: 0x1234... - Initial balance: 100 - Final balance: 200 - Total supply: 150
Counterexample Analysis
- Check Call Trace: Understand the sequence of calls
- Examine State Changes: Track storage modifications
- Identify Assumptions: Check if assumptions are too weak
- Verify Model: Ensure CVL spec matches intent
Common Patterns
ERC-20 Verification
methods { function balanceOf(address) external returns (uint256) envfree; function totalSupply() external returns (uint256) envfree; function allowance(address, address) external returns (uint256) envfree; } // Transfer integrity rule transferIntegrity(address to, uint256 amount) { env e; address from = e.msg.sender; uint256 fromBalanceBefore = balanceOf(from); uint256 toBalanceBefore = balanceOf(to); require from != to; transfer(e, to, amount); uint256 fromBalanceAfter = balanceOf(from); uint256 toBalanceAfter = balanceOf(to); assert fromBalanceAfter == fromBalanceBefore - amount; assert toBalanceAfter == toBalanceBefore + amount; } // Allowance monotonicity rule approveIntegrity(address spender, uint256 amount) { env e; approve(e, spender, amount); assert allowance(e.msg.sender, spender) == amount; }
Access Control Verification
methods { function owner() external returns (address) envfree; function setOwner(address) external; } // Only owner can change owner rule onlyOwnerCanChangeOwner(address newOwner) { env e; address ownerBefore = owner(); setOwner(e, newOwner); assert e.msg.sender == ownerBefore, "Non-owner changed owner"; }
CI/CD Integration
GitHub Actions
name: Certora Verification on: [push, pull_request] jobs: certora: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Certora run: pip install certora-cli - name: Run Verification env: CERTORAKEY: ${{ secrets.CERTORAKEY }} run: certoraRun certora/conf/token.conf
Process Integration
| Process | Purpose |
|---|---|
| Primary verification |
| Deep security analysis |
| Protocol correctness |
| DeFi invariants |
| Governance properties |
Best Practices
- Start with simple invariants
- Use parametric rules for comprehensive coverage
- Document all assumptions
- Analyze counterexamples carefully
- Use ghost variables for complex state tracking
- Set appropriate loop bounds
- Run nightly verification in CI
See Also
- Static analysisskills/slither-analysis/SKILL.md
- Property fuzzingskills/echidna-fuzzer/SKILL.md
- Verification expertagents/formal-methods/AGENT.md- Certora Documentation