1use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
2use cfx_parity_trace_types::{
3 Action as VmAction, Outcome, SetAuth as VmSetAuth,
4 SetAuthOutcome as VmSetAuthOutcome,
5};
6use cfx_rpc_cfx_types::{
7 trace::{Action as CfxRpcAction, LocalizedTrace as CfxLocalizedTrace},
8 RpcAddress,
9};
10use cfx_rpc_primitives::Bytes;
11use cfx_types::{address_util::AddressUtil, Address, H256, U256};
12use cfx_util_macros::bail;
13use cfx_vm_types::{CallType, CreateType};
14use jsonrpc_core::Error as JsonRpcError;
15use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
16use std::{collections::HashMap, convert::TryFrom, fmt};
17
18#[derive(Debug, Serialize, Clone)]
20#[serde(rename_all = "camelCase")]
21pub struct Create {
22 from: Address,
24 value: U256,
26 gas: U256,
28 init: Bytes,
30 create_type: CreateType,
32}
33
34#[derive(Debug, Serialize, Clone)]
36#[serde(rename_all = "camelCase")]
37pub struct Call {
38 from: Address,
40 to: Address,
42 value: U256,
44 gas: U256,
46 input: Bytes,
48 call_type: CallType,
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct SelfDestructAction {
56 pub address: Address,
58 pub balance: U256,
60 pub refund_address: Address,
62}
63
64#[derive(Debug, Clone)]
66pub enum Action {
67 Call(Call),
69 Create(Create),
71 SelfDestruct(SelfDestructAction),
73 }
75
76impl Action {
77 pub fn gas(&self) -> Option<U256> {
78 match self {
79 Action::Call(ref call) => Some(call.gas),
80 Action::Create(ref create) => Some(create.gas),
81 Action::SelfDestruct(_) => None,
82 }
83 }
84}
85
86impl TryFrom<VmAction> for Action {
87 type Error = String;
88
89 fn try_from(cfx_action: VmAction) -> Result<Self, String> {
90 match cfx_action {
91 VmAction::Call(call) => Ok(Action::Call(Call {
92 from: call.from,
93 to: call.to,
94 value: call.value,
95 gas: call.gas,
96 input: call.input.into(),
97 call_type: call.call_type,
98 })),
99 VmAction::Create(create) => Ok(Action::Create(Create {
100 from: create.from,
101 value: create.value,
102 gas: create.gas,
103 init: create.init.into(),
104 create_type: create.create_type,
105 })),
106 VmAction::SelfDestruct(selfdestruct) => {
107 Ok(Action::SelfDestruct(SelfDestructAction {
108 address: selfdestruct.address,
109 balance: selfdestruct.balance,
110 refund_address: selfdestruct.refund_address,
111 }))
112 }
113 action => {
114 bail!("unsupported action in eth space: {:?}", action);
115 }
116 }
117 }
118}
119
120#[derive(Debug, Serialize, Clone)]
122#[serde(rename_all = "camelCase")]
123pub struct CallResult {
124 gas_used: U256,
126 output: Bytes,
128}
129
130#[derive(Debug, Serialize, Clone)]
132#[serde(rename_all = "camelCase")]
133pub struct CreateResult {
134 gas_used: U256,
136 code: Bytes,
138 address: Address,
140}
141
142#[derive(Debug, Clone)]
144pub enum ActionResult {
145 Call(CallResult),
147 Create(CreateResult),
149 None,
151}
152
153#[derive(Debug, Clone)]
155pub struct LocalizedTrace {
156 pub action: Action,
158 pub result: ActionResult,
160 pub error: Option<String>,
162 pub trace_address: Vec<usize>,
164 pub subtraces: usize,
166 pub transaction_position: usize,
168 pub transaction_hash: H256,
170 pub block_number: u64,
172 pub block_hash: H256,
174 pub valid: bool,
176}
177
178impl Serialize for LocalizedTrace {
179 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
180 where S: Serializer {
181 let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?;
182 match self.action {
183 Action::Call(ref call) => {
184 struc.serialize_field("type", "call")?;
185 struc.serialize_field("action", call)?;
186 }
187 Action::Create(ref create) => {
188 struc.serialize_field("type", "create")?;
189 struc.serialize_field("action", create)?;
190 }
191 Action::SelfDestruct(ref selfdestruct) => {
192 struc.serialize_field("type", "suicide")?;
193 struc.serialize_field("action", selfdestruct)?;
194 }
195 }
196
197 match self.result {
198 ActionResult::Call(ref call) => {
199 struc.serialize_field("result", call)?
200 }
201 ActionResult::Create(ref create) => {
202 struc.serialize_field("result", create)?
203 }
204 ActionResult::None => {
205 struc.serialize_field("result", &None as &Option<u8>)?
206 }
207 }
208
209 if let Some(error) = &self.error {
210 struc.serialize_field("error", error)?;
211 }
212
213 struc.serialize_field("traceAddress", &self.trace_address)?;
214 struc.serialize_field("subtraces", &self.subtraces)?;
215 struc.serialize_field(
216 "transactionPosition",
217 &self.transaction_position,
218 )?;
219 struc.serialize_field("transactionHash", &self.transaction_hash)?;
220 struc.serialize_field("blockNumber", &self.block_number)?;
221 struc.serialize_field("blockHash", &self.block_hash)?;
222 struc.serialize_field("valid", &self.valid)?;
223
224 struc.end()
225 }
226}
227
228impl LocalizedTrace {
229 pub fn set_result(
230 &mut self, result: Option<VmAction>,
231 ) -> Result<(), JsonRpcError> {
232 if !matches!(self.result, ActionResult::None) {
233 bail!(JsonRpcError::internal_error());
235 }
236 if result.is_none() {
237 self.result = ActionResult::None;
239 return Ok(());
240 }
241 let result = result.unwrap();
242 match result {
243 VmAction::CallResult(call_result) => {
244 if !matches!(self.action, Action::Call(_)) {
245 bail!(JsonRpcError::internal_error());
246 }
247 let gas =
248 self.action.gas().expect("call action should have gas");
249 let gas_used = gas - call_result.gas_left;
250 self.result = ActionResult::Call(CallResult {
251 gas_used,
252 output: call_result.return_data.clone().into(),
253 });
254 match call_result.outcome {
255 Outcome::Reverted => {
256 self.error = Some(TraceError::Reverted.to_string());
257 }
258 Outcome::Fail => {
259 self.error = Some(
260 TraceError::Error(call_result.return_data.into())
261 .to_string(),
262 );
263 }
264 _ => {}
265 }
266 }
267 VmAction::CreateResult(create_result) => {
268 if !matches!(self.action, Action::Create(_)) {
269 bail!(JsonRpcError::internal_error());
270 }
271 let gas =
273 self.action.gas().expect("call action should have gas");
274 let gas_used = gas - create_result.gas_left;
275 self.result = ActionResult::Create(CreateResult {
276 gas_used,
277 code: create_result.return_data.clone().into(),
278 address: create_result.addr,
279 });
280 match create_result.outcome {
281 Outcome::Reverted => {
282 self.error = Some(TraceError::Reverted.to_string());
283 }
284 Outcome::Fail => {
285 self.error = Some(
286 TraceError::Error(create_result.return_data.into())
287 .to_string(),
288 );
289 }
290 _ => {}
291 }
292 }
293 _ => bail!(JsonRpcError::internal_error()),
294 }
295 Ok(())
296 }
297}
298
299#[derive(Debug)]
301pub struct Trace {
302 trace_address: Vec<usize>,
304 subtraces: usize,
306 action: Action,
308 result: ActionResult,
310 error: Option<String>,
312}
313
314impl Serialize for Trace {
315 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
316 where S: Serializer {
317 let mut struc = serializer.serialize_struct("Trace", 4)?;
318 match self.action {
319 Action::Call(ref call) => {
320 struc.serialize_field("type", "call")?;
321 struc.serialize_field("action", call)?;
322 }
323 Action::Create(ref create) => {
324 struc.serialize_field("type", "create")?;
325 struc.serialize_field("action", create)?;
326 }
327 Action::SelfDestruct(ref selfdestruct) => {
328 struc.serialize_field("type", "suicide")?;
329 struc.serialize_field("action", selfdestruct)?;
330 }
331 }
332
333 match self.result {
334 ActionResult::Call(ref call) => {
335 struc.serialize_field("result", call)?
336 }
337 ActionResult::Create(ref create) => {
338 struc.serialize_field("result", create)?
339 }
340 ActionResult::None => {
341 struc.serialize_field("result", &None as &Option<u8>)?
342 }
343 }
344
345 if let Some(error) = &self.error {
346 struc.serialize_field("error", error)?;
347 }
348
349 struc.serialize_field("traceAddress", &self.trace_address)?;
350 struc.serialize_field("subtraces", &self.subtraces)?;
351
352 struc.end()
353 }
354}
355
356#[derive(Debug, Clone)]
357pub enum TraceError {
358 Reverted,
360 Error(Bytes),
362}
363
364impl fmt::Display for TraceError {
365 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
366 let message = match &self {
367 TraceError::Reverted => "Reverted",
368 TraceError::Error(b) => {
371 std::str::from_utf8(&b.0).map_err(|_| fmt::Error)?
372 }
373 };
374 message.fmt(f)
375 }
376}
377
378#[derive(Debug, Clone, PartialEq, Serialize)]
379#[serde(rename_all = "camelCase")]
380pub struct SetAuth {
381 pub address: Address,
383 pub chain_id: U256,
384 pub nonce: U256,
385 pub author: Option<Address>,
386}
387
388#[derive(Debug, Clone, PartialEq, Serialize)]
390#[serde(rename_all = "camelCase")]
391pub struct LocalizedSetAuthTrace {
392 pub action: SetAuth,
394 pub result: VmSetAuthOutcome,
396 pub transaction_position: usize,
398 pub transaction_hash: H256,
400 pub block_number: u64,
402 pub block_hash: H256,
404}
405
406impl LocalizedSetAuthTrace {
407 pub fn new(
408 vm_action: &VmSetAuth, transaction_position: usize,
409 transaction_hash: H256, block_number: u64, block_hash: H256,
410 ) -> Self {
411 let action = SetAuth {
412 address: vm_action.address,
413 chain_id: vm_action.chain_id,
414 nonce: vm_action.nonce,
415 author: vm_action.author,
416 };
417 Self {
418 action,
419 result: vm_action.outcome,
420 transaction_position,
421 transaction_hash,
422 block_number,
423 block_hash,
424 }
425 }
426}
427
428#[derive(Debug, Serialize)]
429#[serde(rename_all = "camelCase")]
430pub struct EpochTrace {
431 cfx_traces: Vec<CfxLocalizedTrace>,
432 eth_traces: Vec<LocalizedTrace>,
433 mirror_address_map: HashMap<Address, RpcAddress>,
434}
435
436impl EpochTrace {
437 pub fn new(
438 cfx_traces: Vec<CfxLocalizedTrace>, eth_traces: Vec<LocalizedTrace>,
439 ) -> Self {
440 let mut mirror_address_map = HashMap::new();
441 for t in &cfx_traces {
442 if let CfxRpcAction::Call(action) = &t.trace.action {
443 if action.to.hex_address == CROSS_SPACE_CONTRACT_ADDRESS {
444 mirror_address_map.insert(
445 action.from.hex_address.evm_map().address,
446 action.from.clone(),
447 );
448 }
449 }
450 }
451 Self {
452 cfx_traces,
453 eth_traces,
454 mirror_address_map,
455 }
456 }
457}