1use crate::RpcAddress;
6use cfx_addr::Network;
7use cfx_parity_trace_types::{
8 Action as VmAction, ActionType as VmActionType, BlockExecTraces,
9 Call as VmCall, CallResult as VmCallResult, Create as VmCreate,
10 CreateResult as VmCreateResult, ExecTrace,
11 InternalTransferAction as VmInternalTransferAction,
12 LocalizedTrace as PrimitiveLocalizedTrace, Outcome,
13 SelfDestructAction as VmSelfDestruction, SetAuth as VmSetAuth,
14 SetAuthOutcome, TransactionExecTraces,
15};
16use cfx_rpc_primitives::Bytes;
17use cfx_types::{Space, H256, U256, U64};
18use cfx_vm_types::{CallType, CreateType};
19use primitives::SignedTransaction;
20use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
21use std::sync::Arc;
22use strum_macros::EnumDiscriminants;
23
24#[derive(Debug, Clone, PartialEq, EnumDiscriminants)]
25#[strum_discriminants(name(ActionType))]
26#[strum_discriminants(derive(Hash, Serialize, Deserialize))]
27#[strum_discriminants(serde(rename_all = "snake_case", deny_unknown_fields))]
28pub enum Action {
29 Call(Call),
30 Create(Create),
31 CallResult(CallResult),
32 CreateResult(CreateResult),
33 InternalTransferAction(InternalTransferAction),
34 SetAuth(SetAuth),
35 SelfDestruct(SelfDestructAction),
36}
37
38impl Action {
39 pub fn try_from(
40 action: VmAction, network: Network,
41 ) -> Result<Self, String> {
42 Ok(match action {
43 VmAction::Call(x) => Action::Call(Call::try_from(x, network)?),
44 VmAction::Create(x) => {
45 Action::Create(Create::try_from(x, network)?)
46 }
47 VmAction::CallResult(x) => Action::CallResult(x.into()),
48 VmAction::CreateResult(x) => {
49 Action::CreateResult(CreateResult::try_from(x, network)?)
50 }
51 VmAction::InternalTransferAction(x) => {
52 Action::InternalTransferAction(
53 InternalTransferAction::try_from(x, network)?,
54 )
55 }
56 VmAction::SetAuth(action) => {
57 Action::SetAuth(SetAuth::try_from(action, network)?)
58 }
59 VmAction::SelfDestruct(selfdestruct) => Action::SelfDestruct(
60 SelfDestructAction::try_from(selfdestruct, network)?,
61 ),
62 })
63 }
64}
65
66impl Into<VmActionType> for ActionType {
67 fn into(self) -> VmActionType {
68 match self {
69 Self::Call => VmActionType::Call,
70 Self::Create => VmActionType::Create,
71 Self::CallResult => VmActionType::CallResult,
72 Self::CreateResult => VmActionType::CreateResult,
73 Self::InternalTransferAction => {
74 VmActionType::InternalTransferAction
75 }
76 Self::SetAuth => VmActionType::SetAuth,
77 Self::SelfDestruct => VmActionType::SelfDestruct,
78 }
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct Call {
85 pub space: Space,
86 pub from: RpcAddress,
87 pub to: RpcAddress,
88 pub value: U256,
89 pub gas: U256,
90 pub input: Bytes,
91 pub call_type: CallType,
92}
93
94impl Call {
95 fn try_from(call: VmCall, network: Network) -> Result<Self, String> {
96 Ok(Self {
97 space: call.space,
98 from: RpcAddress::try_from_h160(call.from, network)?,
99 to: RpcAddress::try_from_h160(call.to, network)?,
100 value: call.value,
101 gas: call.gas,
102 input: call.input.into(),
103 call_type: call.call_type,
104 })
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub struct CallResult {
111 pub outcome: Outcome,
112 pub gas_left: U256,
113 pub return_data: Bytes,
114}
115
116impl From<VmCallResult> for CallResult {
117 fn from(result: VmCallResult) -> Self {
118 Self {
119 outcome: result.outcome,
120 gas_left: result.gas_left,
121 return_data: result.return_data.into(),
122 }
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize)]
127#[serde(rename_all = "camelCase")]
128pub struct Create {
129 pub space: Space,
130 pub from: RpcAddress,
131 pub value: U256,
132 pub gas: U256,
133 pub init: Bytes,
134 pub create_type: CreateType,
135}
136
137impl Create {
138 fn try_from(create: VmCreate, network: Network) -> Result<Self, String> {
139 Ok(Self {
140 space: create.space,
141 from: RpcAddress::try_from_h160(create.from, network)?,
142 value: create.value,
143 gas: create.gas,
144 init: create.init.into(),
145 create_type: create.create_type,
146 })
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Serialize)]
151#[serde(rename_all = "camelCase")]
152pub struct CreateResult {
153 pub outcome: Outcome,
154 pub addr: RpcAddress,
155 pub gas_left: U256,
156 pub return_data: Bytes,
157}
158
159impl CreateResult {
160 fn try_from(
161 result: VmCreateResult, network: Network,
162 ) -> Result<Self, String> {
163 Ok(Self {
164 outcome: result.outcome,
165 addr: RpcAddress::try_from_h160(result.addr, network)?,
166 gas_left: result.gas_left,
167 return_data: result.return_data.into(),
168 })
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Serialize)]
173#[serde(rename_all = "camelCase")]
174pub struct InternalTransferAction {
175 pub from: RpcAddress,
176 pub from_pocket: String,
177 pub from_space: String,
178 pub to: RpcAddress,
179 pub to_pocket: String,
180 pub to_space: String,
181 pub value: U256,
182}
183
184impl InternalTransferAction {
185 fn try_from(
186 action: VmInternalTransferAction, network: Network,
187 ) -> Result<Self, String> {
188 Ok(Self {
189 from: RpcAddress::try_from_h160(
190 action.from.inner_address_or_default(),
191 network,
192 )?,
193 from_pocket: action.from.pocket().into(),
194 from_space: action.from.space().into(),
195 to: RpcAddress::try_from_h160(
196 action.to.inner_address_or_default(),
197 network,
198 )?,
199 to_pocket: action.to.pocket().into(),
200 to_space: action.to.space().into(),
201 value: action.value,
202 })
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize)]
207#[serde(rename_all = "camelCase")]
208pub struct SetAuth {
209 pub space: Space,
210 pub address: RpcAddress,
212 pub chain_id: U256,
213 pub nonce: U256,
214 pub outcome: SetAuthOutcome,
216 pub author: Option<RpcAddress>,
218}
219
220impl SetAuth {
221 fn try_from(action: VmSetAuth, network: Network) -> Result<Self, String> {
222 let VmSetAuth {
223 space,
224 address,
225 chain_id,
226 nonce,
227 outcome,
228 author,
229 } = action;
230 Ok(Self {
231 space,
232 address: RpcAddress::try_from_h160(address, network)?,
233 chain_id,
234 nonce,
235 outcome,
236 author: match author {
237 Some(a) => Some(RpcAddress::try_from_h160(a, network)?),
238 None => None,
239 },
240 })
241 }
242}
243
244#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
246#[serde(rename_all = "camelCase")]
247pub struct SelfDestructAction {
248 pub space: Space,
250 pub address: RpcAddress,
252 pub balance: U256,
254 pub refund_address: RpcAddress,
256}
257
258impl SelfDestructAction {
259 fn try_from(
260 action: VmSelfDestruction, network: Network,
261 ) -> Result<Self, String> {
262 let VmSelfDestruction {
263 space,
264 address,
265 balance,
266 refund_address,
267 } = action;
268 Ok(Self {
269 space,
270 address: RpcAddress::try_from_h160(address, network)?,
271 refund_address: RpcAddress::try_from_h160(refund_address, network)?,
272 balance,
273 })
274 }
275}
276
277#[derive(Debug, Serialize)]
278#[serde(rename_all = "camelCase")]
279pub struct LocalizedBlockTrace {
280 pub transaction_traces: Vec<LocalizedTransactionTrace>,
281 pub epoch_hash: H256,
283 pub epoch_number: U256,
285 pub block_hash: H256,
287}
288
289#[derive(Debug, Serialize)]
290#[serde(rename_all = "camelCase")]
291pub struct LocalizedTransactionTrace {
292 pub traces: Vec<Trace>,
293 pub transaction_position: U64,
295 pub transaction_hash: H256,
297}
298
299#[derive(Debug)]
300pub struct Trace {
301 pub action: Action,
302 pub valid: bool,
303}
304
305impl Serialize for Trace {
306 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
307 where S: Serializer {
308 let mut struc = serializer.serialize_struct("LocalizedTrace", 8)?;
309
310 match self.action {
311 Action::Call(ref call) => {
312 struc.serialize_field("type", "call")?;
313 struc.serialize_field("action", call)?;
314 }
315 Action::Create(ref create) => {
316 struc.serialize_field("type", "create")?;
317 struc.serialize_field("action", create)?;
318 }
319 Action::CallResult(ref call_result) => {
320 struc.serialize_field("type", "call_result")?;
321 struc.serialize_field("action", call_result)?;
322 }
323 Action::CreateResult(ref create_result) => {
324 struc.serialize_field("type", "create_result")?;
325 struc.serialize_field("action", create_result)?;
326 }
327 Action::InternalTransferAction(ref internal_action) => {
328 struc.serialize_field("type", "internal_transfer_action")?;
329 struc.serialize_field("action", internal_action)?;
330 }
331 Action::SetAuth(ref set_auth) => {
332 struc.serialize_field("type", "set_auth")?;
333 struc.serialize_field("action", set_auth)?;
334 }
335 Action::SelfDestruct(ref selfdestruct) => {
336 struc.serialize_field("type", "suicide")?;
337 struc.serialize_field("action", selfdestruct)?;
338 }
339 }
340
341 struc.serialize_field("valid", &self.valid)?;
342 struc.end()
343 }
344}
345
346#[derive(Debug, Serialize)]
347#[serde(rename_all = "camelCase")]
348pub struct LocalizedTrace {
349 #[serde(flatten)]
350 pub trace: Trace,
351 pub epoch_hash: H256,
353 pub epoch_number: U256,
355 pub block_hash: H256,
357 pub transaction_position: U64,
359 pub transaction_hash: H256,
361}
362
363impl LocalizedTrace {
364 pub fn from(
365 trace: PrimitiveLocalizedTrace, network: Network,
366 ) -> Result<Self, String> {
367 Ok(LocalizedTrace {
368 trace: Trace {
369 action: Action::try_from(trace.action, network)?,
370 valid: trace.valid,
371 },
372 epoch_number: trace.epoch_number,
373 epoch_hash: trace.epoch_hash,
374 block_hash: trace.block_hash,
375 transaction_position: trace.transaction_position,
376 transaction_hash: trace.transaction_hash,
377 })
378 }
379}
380
381impl LocalizedTransactionTrace {
382 pub fn from(
383 traces: TransactionExecTraces, transaction_hash: H256,
384 transaction_position: usize, network: Network,
385 ) -> Result<Self, String> {
386 let traces: Vec<ExecTrace> = traces.into();
387
388 Ok(LocalizedTransactionTrace {
389 traces: traces
390 .into_iter()
391 .map(|t| {
392 let valid = t.valid;
393 Action::try_from(t.action, network)
394 .map(|action| Trace { action, valid })
395 })
396 .collect::<Result<_, _>>()?,
397 transaction_position: transaction_position.into(),
398 transaction_hash,
399 })
400 }
401}
402
403impl LocalizedBlockTrace {
404 pub fn from(
405 traces: BlockExecTraces, block_hash: H256, epoch_hash: H256,
406 epoch_number: u64, transactions: &Vec<Arc<SignedTransaction>>,
407 network: Network,
408 ) -> Result<Self, String> {
409 let traces: Vec<TransactionExecTraces> = traces.into();
410 if traces.len() != transactions.len() {
411 cfx_util_macros::bail!("trace and tx hash list length unmatch!");
412 }
413 let transaction_traces = traces
414 .into_iter()
415 .enumerate()
416 .filter_map(|(tx_pos, t)| match transactions[tx_pos].space() {
417 Space::Native => Some((transactions[tx_pos].hash(), t)),
418 Space::Ethereum => None,
419 })
420 .enumerate()
421 .map(|(rpc_index, (tx_hash, t))| {
422 LocalizedTransactionTrace::from(t, tx_hash, rpc_index, network)
423 })
424 .collect::<Result<_, _>>()?;
425
426 Ok(LocalizedBlockTrace {
427 transaction_traces,
428 epoch_hash,
429 epoch_number: epoch_number.into(),
430 block_hash,
431 })
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use cfx_addr::Network;
439
440 #[test]
441 fn test_localized_trace_serialization() {
442 let localized_trace = LocalizedTrace {
443 trace: Trace {
444 action: Action::Call(Call {
445 space: Space::Native,
446 from: RpcAddress::null(Network::Main).unwrap(),
447 to: RpcAddress::null(Network::Main).unwrap(),
448 value: U256::from(1000u64),
449 gas: U256::from(21000u64),
450 input: Bytes::from(vec![0x60, 0x60, 0x60, 0x40]),
451 call_type: CallType::Call,
452 }),
453 valid: true,
454 },
455 epoch_hash: Default::default(),
456 epoch_number: Default::default(),
457 block_hash: Default::default(),
458 transaction_position: U64::from(0),
459 transaction_hash: Default::default(),
460 };
461
462 let serialized =
463 serde_json::to_string_pretty(&localized_trace).unwrap();
464
465 let expected = r#"{
466 "type": "call",
467 "action": {
468 "space": "native",
469 "from": "CFX:TYPE.NULL:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0SFBNJM2",
470 "to": "CFX:TYPE.NULL:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0SFBNJM2",
471 "value": "0x3e8",
472 "gas": "0x5208",
473 "input": "0x60606040",
474 "callType": "call"
475 },
476 "valid": true,
477 "epochHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
478 "epochNumber": "0x0",
479 "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
480 "transactionPosition": "0x0",
481 "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
482}"#;
483 assert_eq!(serialized, expected,);
484 }
485}