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 { gas, output, .. } => {
95 println!(
96 " TX 1: Counter incremented (0 -> 1), gas used: {}",
97 gas.used()
98 );
99 if let revm::context_interface::result::Output::Call(bytes) = output {
100 let value = U256::from_be_slice(bytes);
101 println!(" Returned value: {value}");
102 }
103 }
104 _ => anyhow::bail!("TX 1 failed: {result1:?}"),
105 }
106
107 evm.db_mut().bump_bal_index(); let tx2 = TxEnv::builder()
110 .caller(caller)
111 .kind(TxKind::Call(contract_address))
112 .gas_limit(100_000)
113 .nonce(1)
114 .build()
115 .unwrap();
116
117 let result2 = evm.transact_commit(tx2.clone())?;
118 match &result2 {
119 ExecutionResult::Success { gas, output, .. } => {
120 println!(
121 " TX 2: Counter incremented (1 -> 2), gas used: {}",
122 gas.used()
123 );
124 if let revm::context_interface::result::Output::Call(bytes) = output {
125 let value = U256::from_be_slice(bytes);
126 println!(" Returned value: {value}");
127 }
128 }
129 _ => anyhow::bail!("TX 2 failed: {result2:?}"),
130 }
131
132 let bal = evm.db_mut().take_built_bal().expect("BAL should be built");
134
135 println!("\n Built BAL with {} accounts tracked", bal.accounts.len());
136 println!();
137
138 bal.pretty_print();
140
141 println!("\n--- Phase 2: Re-executing with BAL ---");
145
146 let bal_arc = Arc::new(bal);
148 let mut state_with_bal = State::builder().with_bal(bal_arc).build();
149
150 state_with_bal.insert_account(
152 caller,
153 revm::state::AccountInfo {
154 balance: U256::from(1_000_000_000_000_000_000u128),
155 nonce: 0,
156 ..Default::default()
157 },
158 );
159 let bytecode2 = revm::bytecode::Bytecode::new_raw(counter_contract.clone());
160 state_with_bal.insert_account(
161 contract_address,
162 revm::state::AccountInfo {
163 code_hash: keccak256(&counter_contract),
164 code: Some(bytecode2),
165 ..Default::default()
166 },
167 );
168
169 let ctx2 = Context::mainnet().with_db(&mut state_with_bal);
170 let mut evm2 = ctx2.build_mainnet();
171
172 evm2.db_mut().bump_bal_index(); let result1_replay = evm2.transact_commit(tx1)?;
177 match &result1_replay {
178 ExecutionResult::Success { gas, output, .. } => {
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 { gas, output, .. } => {
193 println!(" TX 2 replayed with BAL, gas used: {}", gas.used());
194 if let revm::context_interface::result::Output::Call(bytes) = output {
195 let value = U256::from_be_slice(bytes);
196 println!(" Returned value: {value}");
197 }
198 }
199 _ => anyhow::bail!("TX 2 replay failed: {result2_replay:?}"),
200 }
201
202 println!("\n=== BAL Example Complete ===");
203
204 Ok(())
205}