# Notes on forge fuzzing
## Fuzzing intro
- when fuzzing and passing an arg into a method -> unit256, it isn't "random value" it usually tests the edges because that's where bugs typically lie
- Critical DSCheif bug from passing in hash function -> https://forum.openzeppelin.com/t/using-automatic-analysis-tools-with-makerdao-contracts/1021
-
### Config - foundry.toml
Fuzz
- seed
- allows your tests to be reproducable
- allows you to cache tests
- `max_test_rejects`
- allows you to shape the inputs of your fuzzer
- This is good to understand in context of `vm.assume`below... if to narrow an assumption will result in a reject that this param impacts
- Sometimes it wll use a dictionary
- add pushes and storage values
- Can bias the inputs to get them inside bounds
### Noteable forge std-cheats
`vm.assume()`
- `vm.assume(x > 1000000 && x < 123123123125)`
- If this condition is not met the fuzzer will count it as a rejection
- Can be thought of as a Echidna `require()`
- Happens typically in the "pre-condition" section of the test
- Nieve doesn't find the range you provide, it terminates the test... doesn't save off the failures and try to narrow
- If you have alot of rejecttions in your test don't just pump up the number of `max_test_rejects` use `bound()` instead
`makeAddr()`
- when you need an address it generates it
`writeLine()`
- decent way to add logs... specify file to write to.
`deal()`
- gives balances to an address
`bound()`
- assume is a bit limiting... `bound()` can be used as an alternative
- should be default over assume
## Invariant testing with forge (https://github.com/lucas-manuel/invariant-examples)
- Main diff instead of defining the functions int he test you are going to call you define the functions in the contract that are able to be called then the fuzzer calls them indepenently
- look at Target Contracts section in foundry book
- Fuzzer picks random func with random params, the state will persist in that contract then the invariants will run
- Depth
- num of function calls that will run
- all calls within a set of depths (or one run) share state
- Between each call, invariants are checked
- Runs
- num of times that entire set of depth calls are performed
- all calls within a run share state, the next run happens with a different set of state
- How to use target contracts and setup more sophisticated frameworks around invar testing
-
- open invariant testing
- out of the box things are too loose. you need to ensure that the calls your open invariant tests are making are actually reletivley possible
- start small! Itteratively add functionality
- Out of the box
- invariant tests are calling the solidity contracts directly
- This is bad because this is simply too random, the results of the test are no longer valuable
- You need handlers to sit inbetween!
- ONLY EXPOSE YOUR CUSTOM HANDLER LOGIC TO THE FUZZ TESTER CONTRACT, NOT THE CONTRACT YOUR TESTING!!!
- Structure
- top level: test contract
- mid level; handlers - wrapper functions.
- lowest level: Calls actual contract.
- Invariant contract st-forge
- exclude or target are prioritized but we are not aware which
-
- Deployment
- define invariants in invariant test contract
- deploy contracts to the TargetContract array
- foundry is looking at these arrays which look at target contracts and excluded contracts (essentially invariant config)
- exclude contracts by passing the addresses of the deployed contract to the `excludeContract()` method
- Handlers
- bounded and unbounded should have same interface (not sure why)
- Logic sits in unboudned contract
- Bound logic sits in bounded contract
- Handlers to the rescue!
- can compose multiple actions into these handlers
- good example would be minting -> depositing
- Could hold a set of actors that call methods
- Get way more control to shape the inputs that go into the contract
- Can hold state variables (commonly reffered to as ghost variables) that you set and maintain outside of your protocol
- don't always have to call protocol functions can also alter the state of the system and then perform an action (good example might be calling `skip()`)
- could have a `useRandomLp` method which retreives an actor and then pranks as them to perform an action
- could have handlers with modifiers that perform alternate actions too before your primary action
- How to start -> bounding your handler
- ensure what you have setup is working, no unexpcted reverts by adjusting foundry.toml
- set `fail_on_revert` = true
- bound vs unbounded
- bounded when you are starting... getting things up (zero reverts)
- more reliable to get up and going
- unbounded when you are more confident in the logic of your handlers (not that many reverting function calls)
- can expose false assumptions you made in your bounding
- can expose more unique scenarios in your protocol
- To increase visibility in invar testing (homeade logging)
- create mapping `numCalls["boundedLp.transfer"]++` that allows you to better see logs and track number of transactions that are happening in each handler
- write a `invariant_call_summary()` method. This will console.log out the `numCalls` in each LPHandler at the end of a set of actions, great for debugging
Re-entrancy
- Forge also has a re-entrancy feature although not sure if it is polished enough to expose bugs
Foundry vs Echinda
- Foundry
- easier to get up and running
- with the right patterns and setup can accomplish same level of coverage / comprehensive level of testing
- has more cheatcodes
- Echidna
- Coverage guidance for fuzzer inputs