cfx_rpc_eth_impl/
trace.rs

1use std::vec;
2
3use cfx_addr::Network;
4use cfx_parity_trace_types::Action;
5use cfx_rpc_cfx_impl::TraceHandler;
6use cfx_rpc_cfx_types::PhantomBlock;
7use cfx_rpc_common_impl::trace::{
8    into_eth_localized_traces, primitive_traces_to_eth_localized_traces,
9};
10use cfx_rpc_eth_api::TraceApiServer;
11use cfx_rpc_eth_types::{
12    trace::{LocalizedSetAuthTrace, LocalizedTrace as EthLocalizedTrace},
13    BlockId, Index, LocalizedTrace, TraceFilter,
14};
15use cfx_rpc_utils::error::{
16    jsonrpc_error_helpers::error_object_owned_to_jsonrpc_error,
17    jsonrpsee_error_helpers::{internal_error, invalid_params_rpc_err},
18};
19use cfx_types::H256;
20use cfx_util_macros::unwrap_option_or_return_result_none as unwrap_or_return;
21use cfxcore::{errors::Result as CoreResult, SharedConsensusGraph};
22use jsonrpsee::{core::RpcResult, types::ErrorObjectOwned};
23use log::warn;
24use primitives::EpochNumber;
25pub struct TraceApi {
26    trace_handler: TraceHandler,
27}
28
29impl TraceApi {
30    pub fn new(consensus: SharedConsensusGraph, network: Network) -> TraceApi {
31        let trace_handler = TraceHandler::new(network, consensus);
32        TraceApi { trace_handler }
33    }
34
35    pub fn get_block(
36        &self, block_number: BlockId,
37    ) -> CoreResult<Option<PhantomBlock>> {
38        let phantom_block = match block_number {
39            BlockId::Hash { hash, .. } => self
40                .trace_handler
41                .consensus_graph()
42                .get_phantom_block_by_hash(
43                    &hash, true, /* include_traces */
44                )
45                .map_err(|e| invalid_params_rpc_err(e, None::<()>))?,
46
47            _ => self
48                .trace_handler
49                .consensus_graph()
50                .get_phantom_block_by_number(
51                    block_number.try_into()?,
52                    None,
53                    true, /* include_traces */
54                )
55                .map_err(|e| invalid_params_rpc_err(e, None::<()>))?,
56        };
57
58        Ok(phantom_block)
59    }
60
61    pub fn block_traces(
62        &self, block_number: BlockId,
63    ) -> CoreResult<Option<Vec<LocalizedTrace>>> {
64        let phantom_block = self.get_block(block_number)?;
65
66        unwrap_or_return!(phantom_block);
67
68        let mut eth_traces = Vec::new();
69        let block_number = phantom_block.pivot_header.height();
70        let block_hash = phantom_block.pivot_header.hash();
71
72        for (idx, tx_traces) in phantom_block.traces.into_iter().enumerate() {
73            let tx_hash = phantom_block.transactions[idx].hash();
74            let tx_eth_traces = into_eth_localized_traces(
75                &tx_traces.0,
76                block_number,
77                block_hash,
78                tx_hash,
79                idx,
80            )
81            .map_err(|e| {
82                warn!("Internal error on trace reconstruction: {}", e);
83                internal_error()
84            })?;
85            eth_traces.extend(tx_eth_traces);
86        }
87
88        Ok(Some(eth_traces))
89    }
90
91    pub fn block_set_auth_traces(
92        &self, block_number: BlockId,
93    ) -> CoreResult<Option<Vec<LocalizedSetAuthTrace>>> {
94        let phantom_block = self.get_block(block_number)?;
95
96        unwrap_or_return!(phantom_block);
97
98        let mut eth_traces = Vec::new();
99        let block_number = phantom_block.pivot_header.height();
100        let block_hash = phantom_block.pivot_header.hash();
101
102        for (idx, tx_traces) in phantom_block.traces.into_iter().enumerate() {
103            let tx_hash = phantom_block.transactions[idx].hash();
104
105            let tx_eth_traces: Vec<LocalizedSetAuthTrace> = tx_traces
106                .0
107                .iter()
108                .filter_map(|trace| match trace.action {
109                    Action::SetAuth(ref set_auth) => {
110                        Some(LocalizedSetAuthTrace::new(
111                            set_auth,
112                            idx,
113                            tx_hash,
114                            block_number,
115                            block_hash,
116                        ))
117                    }
118                    _ => None,
119                })
120                .collect();
121
122            eth_traces.extend(tx_eth_traces);
123        }
124
125        Ok(Some(eth_traces))
126    }
127
128    pub fn filter_traces(
129        &self, filter: TraceFilter,
130    ) -> CoreResult<Vec<LocalizedTrace>> {
131        let primitive_filter = filter
132            .into_primitive()
133            .map_err(error_object_owned_to_jsonrpc_error)?;
134
135        let Some(primitive_traces) = self
136            .trace_handler
137            .filter_primitives_traces_impl(primitive_filter)?
138        else {
139            return Ok(vec![]);
140        };
141
142        let traces =
143            primitive_traces_to_eth_localized_traces(&primitive_traces)
144                .map_err(|e| {
145                    warn!("Internal error on trace reconstruction: {}", e);
146                    internal_error()
147                })?;
148        Ok(traces)
149    }
150
151    pub fn transaction_traces(
152        &self, tx_hash: H256,
153    ) -> CoreResult<Option<Vec<EthLocalizedTrace>>> {
154        let tx_index = self
155            .trace_handler
156            .data_man
157            .transaction_index_by_hash(&tx_hash, false /* update_cache */);
158
159        unwrap_or_return!(tx_index);
160
161        let epoch_num = self
162            .trace_handler
163            .consensus
164            .get_block_epoch_number(&tx_index.block_hash);
165
166        unwrap_or_return!(epoch_num);
167
168        let phantom_block = self
169            .trace_handler
170            .consensus_graph()
171            .get_phantom_block_by_number(
172                EpochNumber::Number(epoch_num),
173                None,
174                true, /* include_traces */
175            )
176            .map_err(|e| invalid_params_rpc_err(e, None::<()>))?;
177
178        unwrap_or_return!(phantom_block);
179
180        // find tx corresponding to `tx_hash`
181        let id = phantom_block
182            .transactions
183            .iter()
184            .position(|tx| tx.hash() == tx_hash);
185
186        unwrap_or_return!(id);
187
188        let tx = &phantom_block.transactions[id];
189        let tx_traces = phantom_block.traces[id].clone();
190
191        let eth_traces = into_eth_localized_traces(
192            &tx_traces.0,
193            epoch_num,
194            phantom_block.pivot_header.hash(),
195            tx.hash(),
196            id,
197        )
198        .map_err(|e| {
199            warn!("Internal error on trace reconstruction: {}", e);
200            internal_error()
201        })?;
202
203        Ok(Some(eth_traces))
204    }
205}
206
207#[async_trait::async_trait]
208impl TraceApiServer for TraceApi {
209    async fn block_traces(
210        &self, block_number: BlockId,
211    ) -> RpcResult<Option<Vec<LocalizedTrace>>> {
212        self.block_traces(block_number).map_err(|err| err.into())
213    }
214
215    async fn filter_traces(
216        &self, filter: TraceFilter,
217    ) -> RpcResult<Vec<LocalizedTrace>> {
218        self.filter_traces(filter).map_err(|err| err.into())
219    }
220
221    async fn transaction_traces(
222        &self, tx_hash: H256,
223    ) -> RpcResult<Option<Vec<EthLocalizedTrace>>> {
224        self.transaction_traces(tx_hash).map_err(|err| err.into())
225    }
226
227    async fn block_set_auth_traces(
228        &self, block_number: BlockId,
229    ) -> RpcResult<Option<Vec<LocalizedSetAuthTrace>>> {
230        self.block_set_auth_traces(block_number)
231            .map_err(|err| err.into())
232    }
233
234    async fn trace_get(
235        &self, tx_hash: H256, indices: Vec<Index>,
236    ) -> RpcResult<Option<LocalizedTrace>> {
237        if indices.is_empty() {
238            return Ok(None);
239        }
240        let Some(traces) = self
241            .transaction_traces(tx_hash)
242            .map_err(|err| ErrorObjectOwned::from(err))?
243        else {
244            return Ok(None);
245        };
246        let index = indices[0].value();
247        Ok(traces.get(index).cloned())
248    }
249}