1pub use alloy_rpc_types_trace::parity::TraceType;
2use alloy_rpc_types_trace::parity::{StateDiff, VmTrace};
3use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
4use cfx_parity_trace_types::{
5 Action as VmAction, Outcome, SetAuth as VmSetAuth,
6 SetAuthOutcome as VmSetAuthOutcome,
7};
8use cfx_rpc_cfx_types::{
9 trace::{Action as CfxRpcAction, LocalizedTrace as CfxLocalizedTrace},
10 RpcAddress,
11};
12use cfx_rpc_primitives::Bytes;
13use cfx_rpc_utils::error::jsonrpsee_error_helpers::internal_error;
14use cfx_types::{address_util::AddressUtil, Address, H256, U256};
15use cfx_util_macros::bail;
16use cfx_vm_types::{CallType, CreateType};
17use jsonrpsee::types::ErrorObjectOwned;
18use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
19use std::{collections::HashMap, convert::TryFrom, fmt};
20
21#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
23#[serde(rename_all = "camelCase")]
24pub struct Create {
25 from: Address,
27 value: U256,
29 gas: U256,
31 init: Bytes,
33 create_type: CreateType,
35}
36
37#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
39#[serde(rename_all = "camelCase")]
40pub struct Call {
41 from: Address,
43 to: Address,
45 value: U256,
47 gas: U256,
49 input: Bytes,
51 call_type: CallType,
53}
54
55#[derive(
57 Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default,
58)]
59#[serde(rename_all = "camelCase")]
60pub struct SelfDestructAction {
61 pub address: Address,
63 pub balance: U256,
65 pub refund_address: Address,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum Action {
72 Call(Call),
74 Create(Create),
76 SelfDestruct(SelfDestructAction),
78 }
80
81impl Default for Action {
82 fn default() -> Self { Action::Call(Default::default()) }
83}
84
85impl Action {
86 pub fn gas(&self) -> Option<U256> {
87 match self {
88 Action::Call(ref call) => Some(call.gas),
89 Action::Create(ref create) => Some(create.gas),
90 Action::SelfDestruct(_) => None,
91 }
92 }
93}
94
95impl TryFrom<VmAction> for Action {
96 type Error = String;
97
98 fn try_from(cfx_action: VmAction) -> Result<Self, String> {
99 match cfx_action {
100 VmAction::Call(call) => Ok(Action::Call(Call {
101 from: call.from,
102 to: call.to,
103 value: call.value,
104 gas: call.gas,
105 input: call.input.into(),
106 call_type: call.call_type,
107 })),
108 VmAction::Create(create) => Ok(Action::Create(Create {
109 from: create.from,
110 value: create.value,
111 gas: create.gas,
112 init: create.init.into(),
113 create_type: create.create_type,
114 })),
115 VmAction::SelfDestruct(selfdestruct) => {
116 Ok(Action::SelfDestruct(SelfDestructAction {
117 address: selfdestruct.address,
118 balance: selfdestruct.balance,
119 refund_address: selfdestruct.refund_address,
120 }))
121 }
122 action => {
123 bail!("unsupported action in eth space: {:?}", action);
124 }
125 }
126 }
127}
128
129#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
131#[serde(rename_all = "camelCase")]
132pub struct CallResult {
133 gas_used: U256,
135 output: Bytes,
137}
138
139#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
141#[serde(rename_all = "camelCase")]
142pub struct CreateResult {
143 gas_used: U256,
145 code: Bytes,
147 address: Address,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, Default)]
153pub enum ActionResult {
154 Call(CallResult),
156 Create(CreateResult),
158 #[default]
160 None,
161}
162
163#[derive(Debug, Clone)]
165pub struct LocalizedTrace {
166 pub action: Action,
168 pub result: ActionResult,
170 pub error: Option<String>,
172 pub trace_address: Vec<usize>,
174 pub subtraces: usize,
176 pub transaction_position: usize,
178 pub transaction_hash: H256,
180 pub block_number: u64,
182 pub block_hash: H256,
184 pub valid: bool,
186}
187
188impl Serialize for LocalizedTrace {
189 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190 where S: Serializer {
191 let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?;
192 match self.action {
193 Action::Call(ref call) => {
194 struc.serialize_field("type", "call")?;
195 struc.serialize_field("action", call)?;
196 }
197 Action::Create(ref create) => {
198 struc.serialize_field("type", "create")?;
199 struc.serialize_field("action", create)?;
200 }
201 Action::SelfDestruct(ref selfdestruct) => {
202 struc.serialize_field("type", "suicide")?;
203 struc.serialize_field("action", selfdestruct)?;
204 }
205 }
206
207 match self.result {
208 ActionResult::Call(ref call) => {
209 struc.serialize_field("result", call)?
210 }
211 ActionResult::Create(ref create) => {
212 struc.serialize_field("result", create)?
213 }
214 ActionResult::None => {
215 struc.serialize_field("result", &None as &Option<u8>)?
216 }
217 }
218
219 if let Some(error) = &self.error {
220 struc.serialize_field("error", error)?;
221 }
222
223 struc.serialize_field("traceAddress", &self.trace_address)?;
224 struc.serialize_field("subtraces", &self.subtraces)?;
225 struc.serialize_field(
226 "transactionPosition",
227 &self.transaction_position,
228 )?;
229 struc.serialize_field("transactionHash", &self.transaction_hash)?;
230 struc.serialize_field("blockNumber", &self.block_number)?;
231 struc.serialize_field("blockHash", &self.block_hash)?;
232 struc.serialize_field("valid", &self.valid)?;
233
234 struc.end()
235 }
236}
237
238impl LocalizedTrace {
239 pub fn set_result(
240 &mut self, result: Option<VmAction>,
241 ) -> Result<(), ErrorObjectOwned> {
242 if !matches!(self.result, ActionResult::None) {
243 bail!(internal_error());
245 }
246 if result.is_none() {
247 self.result = ActionResult::None;
249 return Ok(());
250 }
251 let result = result.unwrap();
252 match result {
253 VmAction::CallResult(call_result) => {
254 if !matches!(self.action, Action::Call(_)) {
255 bail!(internal_error());
256 }
257 let gas =
258 self.action.gas().expect("call action should have gas");
259 let gas_used = gas - call_result.gas_left;
260 self.result = ActionResult::Call(CallResult {
261 gas_used,
262 output: call_result.return_data.clone().into(),
263 });
264 match call_result.outcome {
265 Outcome::Reverted => {
266 self.error = Some(TraceError::Reverted.to_string());
267 }
268 Outcome::Fail => {
269 self.error = Some(
270 TraceError::Error(call_result.return_data.into())
271 .to_string(),
272 );
273 }
274 _ => {}
275 }
276 }
277 VmAction::CreateResult(create_result) => {
278 if !matches!(self.action, Action::Create(_)) {
279 bail!(internal_error());
280 }
281 let gas =
283 self.action.gas().expect("call action should have gas");
284 let gas_used = gas - create_result.gas_left;
285 self.result = ActionResult::Create(CreateResult {
286 gas_used,
287 code: create_result.return_data.clone().into(),
288 address: create_result.addr,
289 });
290 match create_result.outcome {
291 Outcome::Reverted => {
292 self.error = Some(TraceError::Reverted.to_string());
293 }
294 Outcome::Fail => {
295 self.error = Some(
296 TraceError::Error(create_result.return_data.into())
297 .to_string(),
298 );
299 }
300 _ => {}
301 }
302 }
303 _ => bail!(internal_error()),
304 }
305 Ok(())
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Default)]
311pub struct TransactionTrace {
312 trace_address: Vec<usize>,
314 subtraces: usize,
316 action: Action,
318 result: ActionResult,
320 error: Option<String>,
322}
323
324impl From<LocalizedTrace> for TransactionTrace {
325 fn from(local_trace: LocalizedTrace) -> Self {
326 let LocalizedTrace {
327 action,
328 result,
329 error,
330 trace_address,
331 subtraces,
332 transaction_position: _,
333 transaction_hash: _,
334 block_number: _,
335 block_hash: _,
336 valid: _,
337 } = local_trace;
338 TransactionTrace {
339 action,
340 result,
341 error,
342 subtraces,
343 trace_address,
344 }
345 }
346}
347
348impl Serialize for TransactionTrace {
349 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
350 where S: Serializer {
351 let mut struc = serializer.serialize_struct("Trace", 4)?;
352 match self.action {
353 Action::Call(ref call) => {
354 struc.serialize_field("type", "call")?;
355 struc.serialize_field("action", call)?;
356 }
357 Action::Create(ref create) => {
358 struc.serialize_field("type", "create")?;
359 struc.serialize_field("action", create)?;
360 }
361 Action::SelfDestruct(ref selfdestruct) => {
362 struc.serialize_field("type", "suicide")?;
363 struc.serialize_field("action", selfdestruct)?;
364 }
365 }
366
367 match self.result {
368 ActionResult::Call(ref call) => {
369 struc.serialize_field("result", call)?
370 }
371 ActionResult::Create(ref create) => {
372 struc.serialize_field("result", create)?
373 }
374 ActionResult::None => {
375 struc.serialize_field("result", &None as &Option<u8>)?
376 }
377 }
378
379 if let Some(error) = &self.error {
380 struc.serialize_field("error", error)?;
381 }
382
383 struc.serialize_field("traceAddress", &self.trace_address)?;
384 struc.serialize_field("subtraces", &self.subtraces)?;
385
386 struc.end()
387 }
388}
389
390#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
392#[serde(rename_all = "camelCase")]
393pub struct TraceResults {
394 pub output: Bytes,
396 pub state_diff: Option<StateDiff>,
398 #[serde(default)]
400 pub trace: Vec<TransactionTrace>,
401 pub vm_trace: Option<VmTrace>,
403}
404
405#[derive(Debug, Clone)]
406pub enum TraceError {
407 Reverted,
409 Error(Bytes),
411}
412
413impl fmt::Display for TraceError {
414 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
415 let message = match &self {
416 TraceError::Reverted => "Reverted",
417 TraceError::Error(b) => {
420 std::str::from_utf8(&b.0).map_err(|_| fmt::Error)?
421 }
422 };
423 message.fmt(f)
424 }
425}
426
427#[derive(Debug, Clone, PartialEq, Serialize)]
428#[serde(rename_all = "camelCase")]
429pub struct SetAuth {
430 pub address: Address,
432 pub chain_id: U256,
433 pub nonce: U256,
434 pub author: Option<Address>,
435}
436
437#[derive(Debug, Clone, PartialEq, Serialize)]
439#[serde(rename_all = "camelCase")]
440pub struct LocalizedSetAuthTrace {
441 pub action: SetAuth,
443 pub result: VmSetAuthOutcome,
445 pub transaction_position: usize,
447 pub transaction_hash: H256,
449 pub block_number: u64,
451 pub block_hash: H256,
453}
454
455impl LocalizedSetAuthTrace {
456 pub fn new(
457 vm_action: &VmSetAuth, transaction_position: usize,
458 transaction_hash: H256, block_number: u64, block_hash: H256,
459 ) -> Self {
460 let action = SetAuth {
461 address: vm_action.address,
462 chain_id: vm_action.chain_id,
463 nonce: vm_action.nonce,
464 author: vm_action.author,
465 };
466 Self {
467 action,
468 result: vm_action.outcome,
469 transaction_position,
470 transaction_hash,
471 block_number,
472 block_hash,
473 }
474 }
475}
476
477#[derive(Debug, Serialize, Clone)]
478#[serde(rename_all = "camelCase")]
479pub struct EpochTrace {
480 cfx_traces: Vec<CfxLocalizedTrace>,
481 eth_traces: Vec<LocalizedTrace>,
482 mirror_address_map: HashMap<Address, RpcAddress>,
483}
484
485impl EpochTrace {
486 pub fn new(
487 cfx_traces: Vec<CfxLocalizedTrace>, eth_traces: Vec<LocalizedTrace>,
488 ) -> Self {
489 let mut mirror_address_map = HashMap::new();
490 for t in &cfx_traces {
491 if let CfxRpcAction::Call(action) = &t.trace.action {
492 if action.to.hex_address == CROSS_SPACE_CONTRACT_ADDRESS {
493 mirror_address_map.insert(
494 action.from.hex_address.evm_map().address,
495 action.from.clone(),
496 );
497 }
498 }
499 }
500 Self {
501 cfx_traces,
502 eth_traces,
503 mirror_address_map,
504 }
505 }
506}