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, )
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, )
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 );
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, )
171 .map_err(RpcError::invalid_params)?;
172
173 unwrap_or_return!(phantom_block);
174
175 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}