|
1 | | -use crate::{ |
2 | | - evm_types::OpcodeId, |
3 | | - l2_types::BlockTrace, |
4 | | - state_db::{CodeDB, StateDB}, |
5 | | - utils::is_precompiled, |
6 | | - Address, Error, H256, |
7 | | -}; |
8 | | -use ethers_core::types::Bytes; |
| 1 | +use crate::{l2_types::BlockTrace, Error, H256}; |
9 | 2 | use itertools::Itertools; |
10 | 3 |
|
11 | | -use super::ExecStep; |
12 | | - |
13 | | -/// Update codedb from statedb and trace |
14 | | -pub fn collect_codes( |
15 | | - block: &BlockTrace, |
16 | | - sdb: Option<&StateDB>, |
17 | | -) -> Result<Vec<(H256, Vec<u8>)>, Error> { |
18 | | - if !block.codes.is_empty() { |
19 | | - log::debug!("codes available in trace, skip collecting"); |
20 | | - return Ok(block |
21 | | - .codes |
22 | | - .iter() |
23 | | - .map(|b| (b.hash, b.code.to_vec())) |
24 | | - .collect_vec()); |
25 | | - } |
26 | | - |
27 | | - log::debug!("collect_codes for block {:?}", block.header.number); |
28 | | - if sdb.is_none() { |
29 | | - log::warn!("collect_codes without sdb can be slow"); |
30 | | - } |
31 | | - let mut codes = Vec::new(); |
32 | | - for (er_idx, execution_result) in block.execution_results.iter().enumerate() { |
33 | | - if let Some(bytecode) = &execution_result.byte_code { |
34 | | - if let Some(to) = &execution_result.to { |
35 | | - // Not contract deployment |
36 | | - let bytecode = decode_bytecode(bytecode)?.to_vec(); |
37 | | - let code_hash = to.poseidon_code_hash; |
38 | | - // code hash 0 means non-existed account |
39 | | - if !code_hash.is_zero() { |
40 | | - codes.push((code_hash, bytecode)); |
41 | | - } |
42 | | - //log::debug!("inserted tx bytecode {:?} {:?}", code_hash, hash); |
43 | | - } |
44 | | - } |
45 | | - |
46 | | - // filter all precompile calls, empty calls and create |
47 | | - let mut call_trace = execution_result |
48 | | - .call_trace |
49 | | - .flatten_trace(&execution_result.prestate) |
50 | | - .into_iter() |
51 | | - .filter(|call| { |
52 | | - let is_call_to_precompile = call.to.as_ref().map(is_precompiled).unwrap_or(false); |
53 | | - let is_call_to_empty = call.gas_used.is_zero() |
54 | | - && !call.call_type.is_create() |
55 | | - && call.is_callee_code_empty; |
56 | | - !(is_call_to_precompile || is_call_to_empty || call.call_type.is_create()) |
57 | | - }) |
58 | | - .collect::<Vec<_>>(); |
59 | | - //log::trace!("call_trace: {call_trace:?}"); |
60 | | - |
61 | | - for (idx, step) in execution_result.exec_steps.iter().enumerate().rev() { |
62 | | - if step.op.is_create() { |
63 | | - continue; |
64 | | - } |
65 | | - let call = if step.op.is_call() { |
66 | | - // filter call to empty/precompile/!precheck_ok |
67 | | - if let Some(next_step) = execution_result.exec_steps.get(idx + 1) { |
68 | | - // the call doesn't have inner steps, it could be: |
69 | | - // - a call to a precompiled contract |
70 | | - // - a call to an empty account |
71 | | - // - a call that !is_precheck_ok |
72 | | - if next_step.depth != step.depth + 1 { |
73 | | - log::trace!("skip call step due to no inner step, curr: {step:?}, next: {next_step:?}"); |
74 | | - continue; |
75 | | - } |
76 | | - } else { |
77 | | - // this is the final step, no inner steps |
78 | | - log::trace!("skip call step due this is the final step: {step:?}"); |
79 | | - continue; |
80 | | - } |
81 | | - let call = call_trace.pop(); |
82 | | - //log::trace!("call_trace pop: {call:?}, current step: {step:?}"); |
83 | | - call |
84 | | - } else { |
85 | | - None |
86 | | - }; |
87 | | - |
88 | | - if let Some(data) = &step.extra_data { |
89 | | - match step.op { |
90 | | - OpcodeId::CALL |
91 | | - | OpcodeId::CALLCODE |
92 | | - | OpcodeId::DELEGATECALL |
93 | | - | OpcodeId::STATICCALL => { |
94 | | - let call = call.unwrap(); |
95 | | - assert_eq!(call.call_type, step.op, "{call:?}"); |
96 | | - let code_idx = if block.transactions[er_idx].to.is_none() { |
97 | | - 0 |
98 | | - } else { |
99 | | - 1 |
100 | | - }; |
101 | | - let callee_code = data.get_code_at(code_idx); |
102 | | - let addr = call.to.unwrap(); |
103 | | - trace_code( |
104 | | - &mut codes, |
105 | | - callee_code.unwrap_or_default(), |
106 | | - step, |
107 | | - Some(addr), |
108 | | - sdb, |
109 | | - block, |
110 | | - ); |
111 | | - } |
112 | | - OpcodeId::EXTCODECOPY => { |
113 | | - let code = data.get_code_at(0); |
114 | | - if code.is_none() { |
115 | | - log::warn!("unable to fetch code from step. {step:?}"); |
116 | | - continue; |
117 | | - } |
118 | | - log::info!("trace extcodecopy! block {:?}", block.header.number); |
119 | | - trace_code(&mut codes, code.unwrap(), step, None, sdb, block); |
120 | | - } |
121 | | - |
122 | | - _ => {} |
123 | | - } |
124 | | - } |
125 | | - } |
| 4 | +/// Collect bytecodes from trace |
| 5 | +pub fn collect_codes(block: &BlockTrace) -> Result<Vec<(H256, Vec<u8>)>, Error> { |
| 6 | + if block.codes.is_empty() { |
| 7 | + return Err(Error::TracingError(format!( |
| 8 | + "codes not available for block {:?}", |
| 9 | + block.header.number |
| 10 | + ))); |
126 | 11 | } |
127 | | - |
128 | | - log::debug!("collect codes done"); |
129 | | - Ok(codes) |
130 | | -} |
131 | | - |
132 | | -fn trace_code( |
133 | | - codes: &mut Vec<(H256, Vec<u8>)>, |
134 | | - code: Bytes, |
135 | | - step: &ExecStep, |
136 | | - addr: Option<Address>, |
137 | | - // sdb is used to read codehash if available without recomputing |
138 | | - sdb: Option<&StateDB>, |
139 | | - block: &BlockTrace, |
140 | | -) { |
141 | | - let code_hash = addr.and_then(|addr| { |
142 | | - sdb.and_then(|sdb| { |
143 | | - let (_existed, acc_data) = sdb.get_account(&addr); |
144 | | - if acc_data.code_hash != CodeDB::empty_code_hash() && !code.is_empty() { |
145 | | - Some(acc_data.code_hash) |
146 | | - } else { |
147 | | - None |
148 | | - } |
149 | | - }) |
150 | | - }); |
151 | | - let code_hash = match code_hash { |
152 | | - Some(code_hash) if !code_hash.is_zero() => code_hash, |
153 | | - _ => { |
154 | | - let hash = CodeDB::hash(&code); |
155 | | - log::debug!( |
156 | | - "hash_code done: addr {addr:?}, size {}, hash {hash:?}, step {:?}, gas.left {:?}, block {:?}", |
157 | | - &code.len(), |
158 | | - step.op, |
159 | | - step.gas, |
160 | | - block.header.number, |
161 | | - ); |
162 | | - hash |
163 | | - } |
164 | | - }; |
165 | | - codes.push((code_hash, code.to_vec())); |
166 | | - log::trace!( |
167 | | - "trace code addr {:?}, size {} hash {:?}", |
168 | | - addr, |
169 | | - &code.len(), |
170 | | - code_hash |
171 | | - ); |
172 | | -} |
173 | | - |
174 | | -fn decode_bytecode(bytecode: &str) -> Result<Vec<u8>, Error> { |
175 | | - let mut stripped = if let Some(stripped) = bytecode.strip_prefix("0x") { |
176 | | - stripped.to_string() |
177 | | - } else { |
178 | | - bytecode.to_string() |
179 | | - }; |
180 | | - |
181 | | - let bytecode_len = stripped.len() as u64; |
182 | | - if (bytecode_len & 1) != 0 { |
183 | | - stripped = format!("0{stripped}"); |
184 | | - } |
185 | | - |
186 | | - hex::decode(stripped).map_err(Error::HexError) |
| 12 | + Ok(block |
| 13 | + .codes |
| 14 | + .iter() |
| 15 | + .map(|b| (b.hash, b.code.to_vec())) |
| 16 | + .collect_vec()) |
187 | 17 | } |
0 commit comments