cfxcore/consensus/consensus_graph/rpc_api/
phantom_block_provider.rs1use cfx_execute_helper::{
2 exec_tracer::recover_phantom_traces,
3 phantom_tx::build_bloom_and_recover_phantom,
4};
5use cfx_rpc_cfx_types::PhantomBlock;
6use cfx_types::{Bloom, Space, H256, U256};
7use cfxcore_errors::ProviderBlockError;
8use primitives::{receipt::Receipt, EpochNumber, TransactionStatus};
9use std::sync::Arc;
10
11use super::super::ConsensusGraph;
12
13impl ConsensusGraph {
14 pub fn get_phantom_block_bloom_filter(
15 &self, block_num: EpochNumber, pivot_assumption: H256,
16 ) -> Result<Option<Bloom>, ProviderBlockError> {
17 let hashes = self.get_block_hashes_by_epoch(block_num)?;
18
19 let pivot = match hashes.last() {
21 Some(p) => p,
22 None => return Err("Inconsistent state: empty epoch".into()),
23 };
24
25 if *pivot != pivot_assumption {
26 return Ok(None);
27 }
28
29 let genesis_hash = self.data_manager().true_genesis.hash();
31
32 if hashes.last() == Some(&genesis_hash) {
33 return Ok(Some(Bloom::zero()));
34 }
35
36 let mut bloom = Bloom::zero();
37
38 for h in &hashes {
39 let exec_info = match self
40 .data_manager()
41 .block_execution_result_by_hash_with_epoch(
42 h, pivot, false, false, ) {
45 None => return Ok(None),
46 Some(r) => r,
47 };
48
49 for receipt in exec_info.block_receipts.receipts.iter() {
50 if receipt.outcome_status == TransactionStatus::Skipped {
51 continue;
52 }
53
54 for log in &receipt.logs {
57 if log.space == Space::Ethereum {
58 bloom.accrue_bloom(&log.bloom());
59 }
60 }
61 }
62 }
63
64 Ok(Some(bloom))
65 }
66
67 pub fn get_phantom_block_pivot_by_number(
68 &self, block_num: EpochNumber, pivot_assumption: Option<H256>,
69 include_traces: bool,
70 ) -> Result<Option<PhantomBlock>, ProviderBlockError> {
71 self.get_phantom_block_by_number_inner(
72 block_num,
73 pivot_assumption,
74 include_traces,
75 true,
76 )
77 }
78
79 pub fn get_phantom_block_by_number(
80 &self, block_num: EpochNumber, pivot_assumption: Option<H256>,
81 include_traces: bool,
82 ) -> Result<Option<PhantomBlock>, ProviderBlockError> {
83 self.get_phantom_block_by_number_inner(
84 block_num,
85 pivot_assumption,
86 include_traces,
87 false,
88 )
89 }
90
91 fn get_phantom_block_by_number_inner(
92 &self, block_num: EpochNumber, pivot_assumption: Option<H256>,
93 include_traces: bool, only_pivot: bool,
94 ) -> Result<Option<PhantomBlock>, ProviderBlockError> {
95 let hashes = self.get_block_hashes_by_epoch(block_num)?;
96
97 let genesis = self.data_manager().true_genesis.clone();
99
100 if hashes.last() == Some(&genesis.hash()) {
101 return Ok(Some(PhantomBlock {
102 pivot_header: genesis.block_header.clone(),
103 transactions: vec![],
104 receipts: vec![],
105 errors: vec![],
106 bloom: Bloom::zero(),
107 traces: vec![],
108 total_gas_limit: U256::from(0),
109 }));
110 }
111
112 let blocks = match self
113 .data_manager()
114 .blocks_by_hash_list(&hashes, false )
115 {
116 None => return Ok(None),
117 Some(b) => b,
118 };
119
120 let pivot = match blocks.last() {
122 Some(p) => p,
123 None => return Err("Inconsistent state: empty epoch".into()),
124 };
125
126 if matches!(pivot_assumption, Some(h) if h != pivot.hash()) {
127 return Ok(None);
128 }
129
130 let mut phantom_block = PhantomBlock {
131 pivot_header: pivot.block_header.clone(),
132 transactions: vec![],
133 receipts: vec![],
134 errors: vec![],
135 bloom: Default::default(),
136 traces: vec![],
137 total_gas_limit: U256::from(0),
138 };
139
140 let mut accumulated_gas_used = U256::from(0);
141 let mut gas_used_offset;
142 let mut total_gas_limit = U256::from(0);
143
144 let iter_blocks = if only_pivot {
145 &blocks[blocks.len() - 1..]
146 } else {
147 &blocks[..]
148 };
149
150 for b in iter_blocks {
151 gas_used_offset = accumulated_gas_used;
152 let exec_info = match self
155 .data_manager()
156 .block_execution_result_by_hash_with_epoch(
157 &b.hash(),
158 &pivot.hash(),
159 false, false, ) {
162 None => return Ok(None),
163 Some(r) => r,
164 };
165
166 total_gas_limit += b.block_header.espace_gas_limit(
169 self.params
170 .can_pack_evm_transaction(b.block_header.height()),
171 );
172
173 let block_receipts = &exec_info.block_receipts.receipts;
174 let errors = &exec_info.block_receipts.tx_execution_error_messages;
175
176 let block_traces = if include_traces {
177 match self.data_manager().block_tx_traces_by_hash(&b.hash()) {
178 None => {
179 return Err("Error while creating phantom block: state is ready but traces not found, did you enable 'executive_trace'?".into());
180 }
181 Some((pivot_hash, block_traces)) => {
182 if b.transactions.len() != block_traces.len() {
184 return Err("Inconsistent state: transactions and traces length mismatch".into());
185 }
186
187 if pivot_hash != pivot.hash() {
189 return Err(
190 "Inconsistent state: pivot hash mismatch"
191 .into(),
192 );
193 }
194
195 block_traces
196 }
197 }
198 } else {
199 vec![]
200 };
201
202 if b.transactions.len() != block_receipts.len() {
204 return Err("Inconsistent state: transactions and receipts length mismatch".into());
205 }
206
207 let evm_chain_id = self.best_chain_id().in_evm_space();
208
209 for (id, tx) in b.transactions.iter().enumerate() {
210 match tx.space() {
211 Space::Ethereum => {
212 let receipt = &block_receipts[id];
213
214 if receipt.outcome_status == TransactionStatus::Skipped
216 {
217 continue;
218 }
219
220 phantom_block.transactions.push(tx.clone());
221
222 if *tx.gas_price() == 0.into() {
224 return Err("Inconsistent state: zero transaction gas price".into());
225 }
226
227 accumulated_gas_used =
228 gas_used_offset + receipt.accumulated_gas_used;
229
230 phantom_block.receipts.push(Receipt {
231 accumulated_gas_used,
232 outcome_status: receipt.outcome_status,
233 ..receipt.clone()
234 });
235
236 phantom_block.errors.push(errors[id].clone());
237 phantom_block.bloom.accrue_bloom(&receipt.log_bloom);
238
239 if include_traces {
240 phantom_block.traces.push(block_traces[id].clone());
241 }
242 }
243 Space::Native => {
244 if block_receipts[id].outcome_status
247 != TransactionStatus::Success
248 {
249 continue;
250 }
251
252 let (phantom_txs, _) = build_bloom_and_recover_phantom(
253 &block_receipts[id].logs[..],
254 tx.hash(),
255 );
256
257 if include_traces {
258 let tx_traces = block_traces[id].clone();
259
260 let phantom_traces =
261 recover_phantom_traces(tx_traces, tx.hash())?;
262
263 if phantom_txs.len() != phantom_traces.len() {
265 error!("Inconsistent state: phantom tx and trace length mismatch, txs.len = {:?}, traces.len = {:?}", phantom_txs.len(), phantom_traces.len());
266 return Err("Inconsistent state: phantom tx and trace length mismatch".into());
267 }
268
269 phantom_block.traces.extend(phantom_traces);
270 }
271
272 for p in phantom_txs {
273 phantom_block.transactions.push(Arc::new(
274 p.clone().into_eip155(evm_chain_id),
275 ));
276
277 let phantom_receipt =
279 p.into_receipt(accumulated_gas_used);
280
281 phantom_block
282 .bloom
283 .accrue_bloom(&phantom_receipt.log_bloom);
284
285 phantom_block.receipts.push(phantom_receipt);
286
287 phantom_block.errors.push("".into());
289 }
290 }
291 }
292 }
293 }
294
295 phantom_block.total_gas_limit = total_gas_limit;
296 Ok(Some(phantom_block))
297 }
298
299 pub fn get_phantom_block_by_hash(
300 &self, hash: &H256, include_traces: bool,
301 ) -> Result<Option<PhantomBlock>, ProviderBlockError> {
302 let epoch_num = match self.get_block_epoch_number(hash) {
303 None => return Ok(None),
304 Some(n) => n,
305 };
306
307 self.get_phantom_block_by_number(
308 EpochNumber::Number(epoch_num),
309 Some(*hash),
310 include_traces,
311 )
312 }
313}