forked from foundry-rs/foundry
-
Notifications
You must be signed in to change notification settings - Fork 0
/
fuzz.rs
171 lines (149 loc) · 5.42 KB
/
fuzz.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use crate::{
executor::{
backend::{diagnostic::RevertDiagnostic, Backend, DatabaseExt, LocalForkId},
fork::{CreateFork, ForkId},
},
Address,
};
use ethers::prelude::{H160, H256, U256};
use hashbrown::HashMap as Map;
use revm::{
db::DatabaseRef, Account, AccountInfo, Bytecode, Database, Env, Inspector, Log, Return,
SubRoutine, TransactOut,
};
use std::borrow::Cow;
use tracing::trace;
/// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called.
///
/// Any changes made during its existence that affect the caching layer of the underlying Database
/// will result in a clone of the initial Database. Therefore, this backend type is basically
/// a clone-on-write `Backend`, where cloning is only necessary if cheatcodes will modify the
/// `Backend`
///
/// Entire purpose of this type is for fuzzing. A test function fuzzer will repeatedly execute the
/// function via immutable raw (no state changes) calls.
///
/// **N.B.**: we're assuming cheatcodes that alter the state (like multi fork swapping) are niche.
/// If they executed during fuzzing, it will require a clone of the initial input database. This way
/// we can support these cheatcodes in fuzzing cheaply without adding overhead for fuzz tests that
/// don't make use of them. Alternatively each test case would require its own `Backend` clone,
/// which would add significant overhead for large fuzz sets even if the Database is not big after
/// setup.
#[derive(Debug, Clone)]
pub struct FuzzBackendWrapper<'a> {
/// The underlying immutable `Backend`
///
/// No calls on the `FuzzBackendWrapper` will ever persistently modify the `backend`'s state.
pub backend: Cow<'a, Backend>,
}
// === impl FuzzBackendWrapper ===
impl<'a> FuzzBackendWrapper<'a> {
pub fn new(backend: &'a Backend) -> Self {
Self { backend: Cow::Borrowed(backend) }
}
/// Executes the configured transaction of the `env` without committing state changes
pub fn inspect_ref<INSP>(
&mut self,
mut env: Env,
mut inspector: INSP,
) -> (Return, TransactOut, u64, Map<Address, Account>, Vec<Log>)
where
INSP: Inspector<Self>,
{
revm::evm_inner::<Self, true>(&mut env, self, &mut inspector).transact()
}
}
impl<'a> DatabaseExt for FuzzBackendWrapper<'a> {
fn snapshot(&mut self, subroutine: &SubRoutine, env: &Env) -> U256 {
trace!("fuzz: create snapshot");
self.backend.to_mut().snapshot(subroutine, env)
}
fn revert(
&mut self,
id: U256,
subroutine: &SubRoutine,
current: &mut Env,
) -> Option<SubRoutine> {
trace!(?id, "fuzz: revert snapshot");
self.backend.to_mut().revert(id, subroutine, current)
}
fn create_fork(
&mut self,
fork: CreateFork,
subroutine: &SubRoutine,
) -> eyre::Result<LocalForkId> {
trace!("fuzz: create fork");
self.backend.to_mut().create_fork(fork, subroutine)
}
fn select_fork(
&mut self,
id: LocalForkId,
env: &mut Env,
subroutine: &mut SubRoutine,
) -> eyre::Result<()> {
trace!(?id, "fuzz: select fork");
self.backend.to_mut().select_fork(id, env, subroutine)
}
fn roll_fork(
&mut self,
env: &mut Env,
block_number: U256,
id: Option<LocalForkId>,
) -> eyre::Result<()> {
trace!(?id, ?block_number, "fuzz: roll fork");
self.backend.to_mut().roll_fork(env, block_number, id)
}
fn active_fork_id(&self) -> Option<LocalForkId> {
self.backend.active_fork_id()
}
fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
self.backend.ensure_fork(id)
}
fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
self.backend.ensure_fork_id(id)
}
fn diagnose_revert(
&self,
callee: Address,
subroutine: &SubRoutine,
) -> Option<RevertDiagnostic> {
self.backend.diagnose_revert(callee, subroutine)
}
fn is_persistent(&self, acc: &Address) -> bool {
self.backend.is_persistent(acc)
}
fn remove_persistent_account(&mut self, account: &Address) -> bool {
self.backend.to_mut().remove_persistent_account(account)
}
fn add_persistent_account(&mut self, account: Address) -> bool {
self.backend.to_mut().add_persistent_account(account)
}
}
impl<'a> DatabaseRef for FuzzBackendWrapper<'a> {
fn basic(&self, address: H160) -> AccountInfo {
DatabaseRef::basic(self.backend.as_ref(), address)
}
fn code_by_hash(&self, code_hash: H256) -> Bytecode {
DatabaseRef::code_by_hash(self.backend.as_ref(), code_hash)
}
fn storage(&self, address: H160, index: U256) -> U256 {
DatabaseRef::storage(self.backend.as_ref(), address, index)
}
fn block_hash(&self, number: U256) -> H256 {
DatabaseRef::block_hash(self.backend.as_ref(), number)
}
}
impl<'a> Database for FuzzBackendWrapper<'a> {
fn basic(&mut self, address: H160) -> AccountInfo {
DatabaseRef::basic(self, address)
}
fn code_by_hash(&mut self, code_hash: H256) -> Bytecode {
DatabaseRef::code_by_hash(self, code_hash)
}
fn storage(&mut self, address: H160, index: U256) -> U256 {
DatabaseRef::storage(self, address, index)
}
fn block_hash(&mut self, number: U256) -> H256 {
DatabaseRef::block_hash(self, number)
}
}