Here you can find all important concepts, exercises, implementations, and step-by-step guides for increasing your knowledge around Fuzzing.
Fuzzing is a technique where the goal is to break the invariants of the contract by trying to break specific assertions with random data.
The benefit is that the contract is exposed to extreme conditions, helping to uncover hard-to-detect vulnerabilities.
Invariants are properties of our system that should always hold true.
E.g. A user should never be able to withdraw more money than they deposited
Defining good invariants is key; it shows that you truly understand and can explain in plain English what the protocol does and what it doesn't. You can get the idea from whitepapers (abstracted), docs, or the Solidity codebase.
- Functional-level invariants:
- They are stateless and can be tested in insolation.
- Target specific element
- System-level invariants:
- Focus on the entire system
- These invariants are usually stateful
- Don't target specific element, but focus to cover entire system funcitonality
- Valid State
- Enforcing valid states ensures that the user is always in one of theses states
- State transition
- Ensure that changes occur in the correct order and conditions without desviations
- Verified to occur only against specific functions, calls variable rules to prevent undesired state changes.
- Variable Transitions
- This verification ensures, similarly to state transitions, that variables change consistently.
- Different variables may have distinct change patterns, with some required to change only in specific directions for the system's coherence.
- Define Invariants from Documentation
- Write Your Properties in Plain English (PROPERTIES.md)
- Categore Your Properties
- Prioritize Your Invariants (A good strategy can be to start with the "easier to implement" and "most impactful" first)
- Writing Solidity Code for Invariants
-
Stateless Fuzzing (Easiest)
This approach is useful when you have a function that has an invariant. Random data to one function, where the state of the previous run is discarded.
-
Stateful Fuzzing - Open / Unguided (A little harder)
Random data & random function calls to many functions, where the final state of your previous run is the starting state of your next run.
This approach sends random data with random calls, but depending on the number of restrictions, it can be difficult to find bugs.
-
Stateful Fuzzing - Handler method/ Guided (Harder)
This method uses the same base as the open method but handles the restrictions on our random inputs to a set of specific random actions that can be called.
-
Formal Verification w/ Halmos (Hardest)
- Foundry
- Echidna
If you see any errors or want to add an observation, please create an issue or a PR. References are greatly appreciated. You can also contact me on Twitter at @pinalikefruit.
Take the observations in this repository as guidelines and a kickstarter to make fuzzing tests. This repo may contain errors or incorrect assumptions. If you find any, please help by creating an issue or a PR.