1use criterion::Criterion;
2use revm::{
3 bytecode::opcode,
4 context::TxEnv,
5 database::{InMemoryDB, BENCH_CALLER, BENCH_TARGET},
6 primitives::{address, Address, TxKind, U256},
7 state::{AccountInfo, Bytecode},
8 Context, ExecuteEvm, MainBuilder, MainContext,
9};
10
11const SUBCALL_TARGET_A: Address = address!("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
12const SUBCALL_TARGET_B: Address = address!("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
13
14fn make_loop_call_bytecode(target: Address, value: u8) -> Bytecode {
17 let mut code = vec![
18 opcode::PUSH2,
19 0x03,
20 0xE8, opcode::JUMPDEST, opcode::PUSH1,
23 0x00, opcode::PUSH1,
25 0x00, opcode::PUSH1,
27 0x00, opcode::PUSH1,
29 0x00, opcode::PUSH1,
31 value, opcode::PUSH20, ];
34 code.extend_from_slice(target.as_slice());
35 code.extend_from_slice(&[
36 opcode::GAS, opcode::CALL,
38 opcode::POP, opcode::PUSH1,
40 0x01, opcode::SWAP1,
42 opcode::SUB,
43 opcode::DUP1, opcode::PUSH1,
45 0x03, opcode::JUMPI, opcode::POP, opcode::STOP,
49 ]);
50 let bytecode = Bytecode::new_raw(code.into());
51 let _ = bytecode.legacy_jump_table();
52 bytecode
53}
54
55fn make_stop_bytecode() -> Bytecode {
57 let bytecode = Bytecode::new_raw([opcode::STOP].into());
58 let _ = bytecode.legacy_jump_table();
59 bytecode
60}
61
62fn make_subcall_bytecode(target: Address) -> Bytecode {
64 let mut code = vec![
65 opcode::PUSH1,
66 0x00, opcode::PUSH1,
68 0x00, opcode::PUSH1,
70 0x00, opcode::PUSH1,
72 0x00, opcode::PUSH1,
74 0x00, opcode::PUSH20, ];
77 code.extend_from_slice(target.as_slice());
78 code.extend_from_slice(&[opcode::GAS, opcode::CALL, opcode::POP, opcode::STOP]);
79 let bytecode = Bytecode::new_raw(code.into());
80 let _ = bytecode.legacy_jump_table();
81 bytecode
82}
83
84pub fn run(criterion: &mut Criterion) {
85 {
87 let mut db = InMemoryDB::default();
88 db.insert_account_info(
89 BENCH_CALLER,
90 AccountInfo {
91 balance: U256::from(u128::MAX),
92 ..Default::default()
93 },
94 );
95 db.insert_account_info(
96 BENCH_TARGET,
97 AccountInfo {
98 balance: U256::from(u128::MAX),
99 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 1)),
100 ..Default::default()
101 },
102 );
103 db.insert_account_info(
104 SUBCALL_TARGET_A,
105 AccountInfo {
106 code: Some(make_stop_bytecode()),
107 ..Default::default()
108 },
109 );
110
111 let mut evm = Context::mainnet()
112 .with_db(db)
113 .modify_cfg_chained(|c| {
114 c.disable_nonce_check = true;
115 c.tx_gas_limit_cap = Some(u64::MAX);
116 })
117 .build_mainnet();
118
119 let tx = TxEnv::builder()
120 .caller(BENCH_CALLER)
121 .kind(TxKind::Call(BENCH_TARGET))
122 .gas_limit(u64::MAX)
123 .build()
124 .unwrap();
125
126 criterion.bench_function("subcall_1000_transfer_1wei", |b| {
127 b.iter_batched(
128 || tx.clone(),
129 |input| evm.transact_one(input).unwrap(),
130 criterion::BatchSize::SmallInput,
131 );
132 });
133 }
134
135 {
137 let mut db = InMemoryDB::default();
138 db.insert_account_info(
139 BENCH_CALLER,
140 AccountInfo {
141 balance: U256::from(u128::MAX),
142 ..Default::default()
143 },
144 );
145 db.insert_account_info(
146 BENCH_TARGET,
147 AccountInfo {
148 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 0)),
149 ..Default::default()
150 },
151 );
152 db.insert_account_info(
153 SUBCALL_TARGET_A,
154 AccountInfo {
155 code: Some(make_stop_bytecode()),
156 ..Default::default()
157 },
158 );
159
160 let mut evm = Context::mainnet()
161 .with_db(db)
162 .modify_cfg_chained(|c| {
163 c.disable_nonce_check = true;
164 c.tx_gas_limit_cap = Some(u64::MAX);
165 })
166 .build_mainnet();
167
168 let tx = TxEnv::builder()
169 .caller(BENCH_CALLER)
170 .kind(TxKind::Call(BENCH_TARGET))
171 .gas_limit(u64::MAX)
172 .build()
173 .unwrap();
174
175 criterion.bench_function("subcall_1000_same_account", |b| {
176 b.iter_batched(
177 || tx.clone(),
178 |input| evm.transact_one(input).unwrap(),
179 criterion::BatchSize::SmallInput,
180 );
181 });
182 }
183
184 {
186 let mut db = InMemoryDB::default();
187 db.insert_account_info(
188 BENCH_CALLER,
189 AccountInfo {
190 balance: U256::from(u128::MAX),
191 ..Default::default()
192 },
193 );
194 db.insert_account_info(
195 BENCH_TARGET,
196 AccountInfo {
197 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 0)),
198 ..Default::default()
199 },
200 );
201 db.insert_account_info(
202 SUBCALL_TARGET_A,
203 AccountInfo {
204 code: Some(make_subcall_bytecode(SUBCALL_TARGET_B)),
205 ..Default::default()
206 },
207 );
208 db.insert_account_info(
209 SUBCALL_TARGET_B,
210 AccountInfo {
211 code: Some(make_stop_bytecode()),
212 ..Default::default()
213 },
214 );
215
216 let mut evm = Context::mainnet()
217 .with_db(db)
218 .modify_cfg_chained(|c| {
219 c.disable_nonce_check = true;
220 c.tx_gas_limit_cap = Some(u64::MAX);
221 })
222 .build_mainnet();
223
224 let tx = TxEnv::builder()
225 .caller(BENCH_CALLER)
226 .kind(TxKind::Call(BENCH_TARGET))
227 .gas_limit(u64::MAX)
228 .build()
229 .unwrap();
230
231 criterion.bench_function("subcall_1000_nested", |b| {
232 b.iter_batched(
233 || tx.clone(),
234 |input| evm.transact_one(input).unwrap(),
235 criterion::BatchSize::SmallInput,
236 );
237 });
238 }
239}