cfx_rpc_eth_impl/
trace.rs1use 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, )
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, )
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 );
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, )
176 .map_err(|e| invalid_params_rpc_err(e, None::<()>))?;
177
178 unwrap_or_return!(phantom_block);
179
180 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}