1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
8
9use std::sync::Arc;
10
11use revm::{
12 bytecode::opcode,
13 context::{Context, ContextTr, TxEnv},
14 context_interface::result::ExecutionResult,
15 database::State,
16 primitives::{address, keccak256, Bytes, TxKind, U256},
17 ExecuteCommitEvm, MainBuilder, MainContext,
18};
19
20fn main() -> anyhow::Result<()> {
21 println!("=== Block Access List (BAL) Example ===\n");
22
23 let counter_contract: Bytes = [
31 opcode::PUSH0, opcode::SLOAD, opcode::PUSH1,
34 0x01, opcode::ADD, opcode::DUP1, opcode::PUSH0, opcode::SSTORE, opcode::PUSH0, opcode::MSTORE, opcode::PUSH1,
42 0x20, opcode::PUSH0, opcode::RETURN, ]
46 .into();
47
48 let caller = address!("0x1000000000000000000000000000000000000001");
49 let contract_address = address!("0x2000000000000000000000000000000000000002");
50
51 println!("--- Phase 1: Building BAL ---");
55
56 let mut state_for_building = State::builder().with_bal_builder().build();
57
58 state_for_building.insert_account(
60 caller,
61 revm::state::AccountInfo {
62 balance: U256::from(1_000_000_000_000_000_000u128),
63 nonce: 0,
64 ..Default::default()
65 },
66 );
67
68 let bytecode = revm::bytecode::Bytecode::new_raw(counter_contract.clone());
70 state_for_building.insert_account(
71 contract_address,
72 revm::state::AccountInfo {
73 code_hash: keccak256(&counter_contract),
74 code: Some(bytecode),
75 ..Default::default()
76 },
77 );
78
79 let ctx = Context::mainnet().with_db(&mut state_for_building);
80 let mut evm = ctx.build_mainnet();
81
82 evm.db_mut().bump_bal_index(); let tx1 = TxEnv::builder()
85 .caller(caller)
86 .kind(TxKind::Call(contract_address))
87 .gas_limit(100_000)
88 .nonce(0)
89 .build()
90 .unwrap();
91
92 let result1 = evm.transact_commit(tx1.clone())?;
93 match &result1 {
94 ExecutionResult::Success {
95 gas_used, output, ..
96 } => {
97 println!(" TX 1: Counter incremented (0 -> 1), gas used: {gas_used}");
98 if let revm::context_interface::result::Output::Call(bytes) = output {
99 let value = U256::from_be_slice(bytes);
100 println!(" Returned value: {value}");
101 }
102 }
103 _ => anyhow::bail!("TX 1 failed: {result1:?}"),
104 }
105
106 evm.db_mut().bump_bal_index(); let tx2 = TxEnv::builder()
109 .caller(caller)
110 .kind(TxKind::Call(contract_address))
111 .gas_limit(100_000)
112 .nonce(1)
113 .build()
114 .unwrap();
115
116 let result2 = evm.transact_commit(tx2.clone())?;
117 match &result2 {
118 ExecutionResult::Success {
119 gas_used, output, ..
120 } => {
121 println!(" TX 2: Counter incremented (1 -> 2), gas used: {gas_used}");
122 if let revm::context_interface::result::Output::Call(bytes) = output {
123 let value = U256::from_be_slice(bytes);
124 println!(" Returned value: {value}");
125 }
126 }
127 _ => anyhow::bail!("TX 2 failed: {result2:?}"),
128 }
129
130 let bal = evm.db_mut().take_built_bal().expect("BAL should be built");
132
133 println!("\n Built BAL with {} accounts tracked", bal.accounts.len());
134 println!();
135
136 bal.pretty_print();
138
139 println!("\n--- Phase 2: Re-executing with BAL ---");
143
144 let bal_arc = Arc::new(bal);
146 let mut state_with_bal = State::builder().with_bal(bal_arc).build();
147
148 state_with_bal.insert_account(
150 caller,
151 revm::state::AccountInfo {
152 balance: U256::from(1_000_000_000_000_000_000u128),
153 nonce: 0,
154 ..Default::default()
155 },
156 );
157 let bytecode2 = revm::bytecode::Bytecode::new_raw(counter_contract.clone());
158 state_with_bal.insert_account(
159 contract_address,
160 revm::state::AccountInfo {
161 code_hash: keccak256(&counter_contract),
162 code: Some(bytecode2),
163 ..Default::default()
164 },
165 );
166
167 let ctx2 = Context::mainnet().with_db(&mut state_with_bal);
168 let mut evm2 = ctx2.build_mainnet();
169
170 evm2.db_mut().bump_bal_index(); let result1_replay = evm2.transact_commit(tx1)?;
175 match &result1_replay {
176 ExecutionResult::Success {
177 gas_used, output, ..
178 } => {
179 println!(" TX 1 replayed with BAL, gas used: {gas_used}");
180 if let revm::context_interface::result::Output::Call(bytes) = output {
181 let value = U256::from_be_slice(bytes);
182 println!(" Returned value: {value}");
183 }
184 }
185 _ => anyhow::bail!("TX 1 replay failed: {result1_replay:?}"),
186 }
187
188 evm2.db_mut().bump_bal_index(); let result2_replay = evm2.transact_commit(tx2)?;
191 match &result2_replay {
192 ExecutionResult::Success {
193 gas_used, output, ..
194 } => {
195 println!(" TX 2 replayed with BAL, gas used: {gas_used}");
196 if let revm::context_interface::result::Output::Call(bytes) = output {
197 let value = U256::from_be_slice(bytes);
198 println!(" Returned value: {value}");
199 }
200 }
201 _ => anyhow::bail!("TX 2 replay failed: {result2_replay:?}"),
202 }
203
204 println!("\n=== BAL Example Complete ===");
205
206 Ok(())
207}