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