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 Bytecode::new_raw(code.into())
51}
52
53fn make_stop_bytecode() -> Bytecode {
55 Bytecode::new_raw([opcode::STOP].into())
56}
57
58fn make_subcall_bytecode(target: Address) -> Bytecode {
60 let mut code = vec![
61 opcode::PUSH1,
62 0x00, opcode::PUSH1,
64 0x00, opcode::PUSH1,
66 0x00, opcode::PUSH1,
68 0x00, opcode::PUSH1,
70 0x00, opcode::PUSH20, ];
73 code.extend_from_slice(target.as_slice());
74 code.extend_from_slice(&[opcode::GAS, opcode::CALL, opcode::POP, opcode::STOP]);
75 Bytecode::new_raw(code.into())
76}
77
78pub fn run(criterion: &mut Criterion) {
79 {
81 let mut db = InMemoryDB::default();
82 db.insert_account_info(
83 BENCH_CALLER,
84 AccountInfo {
85 balance: U256::from(u128::MAX),
86 ..Default::default()
87 },
88 );
89 db.insert_account_info(
90 BENCH_TARGET,
91 AccountInfo {
92 balance: U256::from(u128::MAX),
93 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 1)),
94 ..Default::default()
95 },
96 );
97 db.insert_account_info(
98 SUBCALL_TARGET_A,
99 AccountInfo {
100 code: Some(make_stop_bytecode()),
101 ..Default::default()
102 },
103 );
104
105 let mut evm = Context::mainnet()
106 .with_db(db)
107 .modify_cfg_chained(|c| {
108 c.disable_nonce_check = true;
109 c.tx_gas_limit_cap = Some(u64::MAX);
110 })
111 .build_mainnet();
112
113 let tx = TxEnv::builder()
114 .caller(BENCH_CALLER)
115 .kind(TxKind::Call(BENCH_TARGET))
116 .gas_limit(u64::MAX)
117 .build()
118 .unwrap();
119
120 criterion.bench_function("subcall_1000_transfer_1wei", |b| {
121 b.iter_batched(
122 || tx.clone(),
123 |input| evm.transact_one(input).unwrap(),
124 criterion::BatchSize::SmallInput,
125 );
126 });
127 }
128
129 {
131 let mut db = InMemoryDB::default();
132 db.insert_account_info(
133 BENCH_CALLER,
134 AccountInfo {
135 balance: U256::from(u128::MAX),
136 ..Default::default()
137 },
138 );
139 db.insert_account_info(
140 BENCH_TARGET,
141 AccountInfo {
142 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 0)),
143 ..Default::default()
144 },
145 );
146 db.insert_account_info(
147 SUBCALL_TARGET_A,
148 AccountInfo {
149 code: Some(make_stop_bytecode()),
150 ..Default::default()
151 },
152 );
153
154 let mut evm = Context::mainnet()
155 .with_db(db)
156 .modify_cfg_chained(|c| {
157 c.disable_nonce_check = true;
158 c.tx_gas_limit_cap = Some(u64::MAX);
159 })
160 .build_mainnet();
161
162 let tx = TxEnv::builder()
163 .caller(BENCH_CALLER)
164 .kind(TxKind::Call(BENCH_TARGET))
165 .gas_limit(u64::MAX)
166 .build()
167 .unwrap();
168
169 criterion.bench_function("subcall_1000_same_account", |b| {
170 b.iter_batched(
171 || tx.clone(),
172 |input| evm.transact_one(input).unwrap(),
173 criterion::BatchSize::SmallInput,
174 );
175 });
176 }
177
178 {
180 let mut db = InMemoryDB::default();
181 db.insert_account_info(
182 BENCH_CALLER,
183 AccountInfo {
184 balance: U256::from(u128::MAX),
185 ..Default::default()
186 },
187 );
188 db.insert_account_info(
189 BENCH_TARGET,
190 AccountInfo {
191 code: Some(make_loop_call_bytecode(SUBCALL_TARGET_A, 0)),
192 ..Default::default()
193 },
194 );
195 db.insert_account_info(
196 SUBCALL_TARGET_A,
197 AccountInfo {
198 code: Some(make_subcall_bytecode(SUBCALL_TARGET_B)),
199 ..Default::default()
200 },
201 );
202 db.insert_account_info(
203 SUBCALL_TARGET_B,
204 AccountInfo {
205 code: Some(make_stop_bytecode()),
206 ..Default::default()
207 },
208 );
209
210 let mut evm = Context::mainnet()
211 .with_db(db)
212 .modify_cfg_chained(|c| {
213 c.disable_nonce_check = true;
214 c.tx_gas_limit_cap = Some(u64::MAX);
215 })
216 .build_mainnet();
217
218 let tx = TxEnv::builder()
219 .caller(BENCH_CALLER)
220 .kind(TxKind::Call(BENCH_TARGET))
221 .gas_limit(u64::MAX)
222 .build()
223 .unwrap();
224
225 criterion.bench_function("subcall_1000_nested", |b| {
226 b.iter_batched(
227 || tx.clone(),
228 |input| evm.transact_one(input).unwrap(),
229 criterion::BatchSize::SmallInput,
230 );
231 });
232 }
233}