use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
use cfx_parity_trace_types::{
Action as VmAction, Outcome, SetAuth as VmSetAuth,
SetAuthOutcome as VmSetAuthOutcome,
};
use cfx_rpc_cfx_types::{
trace::{Action as CfxRpcAction, LocalizedTrace as CfxLocalizedTrace},
RpcAddress,
};
use cfx_rpc_primitives::Bytes;
use cfx_types::{address_util::AddressUtil, Address, H256, U256};
use cfx_util_macros::bail;
use cfx_vm_types::{CallType, CreateType};
use jsonrpc_core::Error as JsonRpcError;
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use std::{collections::HashMap, convert::TryFrom, fmt};
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Create {
from: Address,
value: U256,
gas: U256,
init: Bytes,
create_type: CreateType,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Call {
from: Address,
to: Address,
value: U256,
gas: U256,
input: Bytes,
call_type: CallType,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SelfDestructAction {
pub address: Address,
pub balance: U256,
pub refund_address: Address,
}
#[derive(Debug, Clone)]
pub enum Action {
Call(Call),
Create(Create),
SelfDestruct(SelfDestructAction),
}
impl TryFrom<VmAction> for Action {
type Error = String;
fn try_from(cfx_action: VmAction) -> Result<Self, String> {
match cfx_action {
VmAction::Call(call) => Ok(Action::Call(Call {
from: call.from,
to: call.to,
value: call.value,
gas: call.gas,
input: call.input.into(),
call_type: call.call_type,
})),
VmAction::Create(create) => Ok(Action::Create(Create {
from: create.from,
value: create.value,
gas: create.gas,
init: create.init.into(),
create_type: create.create_type,
})),
VmAction::SelfDestruct(selfdestruct) => {
Ok(Action::SelfDestruct(SelfDestructAction {
address: selfdestruct.address,
balance: selfdestruct.balance,
refund_address: selfdestruct.refund_address,
}))
}
action => {
bail!("unsupported action in eth space: {:?}", action);
}
}
}
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CallResult {
gas_used: U256,
output: Bytes,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CreateResult {
gas_used: U256,
code: Bytes,
address: Address,
}
#[derive(Debug, Clone)]
pub enum ActionResult {
Call(CallResult),
Create(CreateResult),
FailedCall(TraceError),
FailedCreate(TraceError),
None,
}
#[derive(Debug, Clone)]
pub struct LocalizedTrace {
pub action: Action,
pub result: ActionResult,
pub trace_address: Vec<usize>,
pub subtraces: usize,
pub transaction_position: usize,
pub transaction_hash: H256,
pub block_number: u64,
pub block_hash: H256,
pub valid: bool,
}
impl Serialize for LocalizedTrace {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?;
match self.action {
Action::Call(ref call) => {
struc.serialize_field("type", "call")?;
struc.serialize_field("action", call)?;
}
Action::Create(ref create) => {
struc.serialize_field("type", "create")?;
struc.serialize_field("action", create)?;
}
Action::SelfDestruct(ref selfdestruct) => {
struc.serialize_field("type", "suicide")?;
struc.serialize_field("action", selfdestruct)?;
}
}
match self.result {
ActionResult::Call(ref call) => {
struc.serialize_field("result", call)?
}
ActionResult::Create(ref create) => {
struc.serialize_field("result", create)?
}
ActionResult::FailedCall(ref error) => {
struc.serialize_field("error", &error.to_string())?
}
ActionResult::FailedCreate(ref error) => {
struc.serialize_field("error", &error.to_string())?
}
ActionResult::None => {
struc.serialize_field("result", &None as &Option<u8>)?
}
}
struc.serialize_field("traceAddress", &self.trace_address)?;
struc.serialize_field("subtraces", &self.subtraces)?;
struc.serialize_field(
"transactionPosition",
&self.transaction_position,
)?;
struc.serialize_field("transactionHash", &self.transaction_hash)?;
struc.serialize_field("blockNumber", &self.block_number)?;
struc.serialize_field("blockHash", &self.block_hash)?;
struc.serialize_field("valid", &self.valid)?;
struc.end()
}
}
impl LocalizedTrace {
pub fn set_result(
&mut self, result: Option<VmAction>,
) -> Result<(), JsonRpcError> {
if !matches!(self.result, ActionResult::None) {
bail!(JsonRpcError::internal_error());
}
if result.is_none() {
self.result = ActionResult::None;
return Ok(());
}
let result = result.unwrap();
match result {
VmAction::CallResult(call_result) => {
if !matches!(self.action, Action::Call(_)) {
bail!(JsonRpcError::internal_error());
}
match call_result.outcome {
Outcome::Success => {
self.result = ActionResult::Call(CallResult {
gas_used: call_result.gas_left,
output: call_result.return_data.into(),
})
}
Outcome::Reverted => {
self.result =
ActionResult::FailedCall(TraceError::Reverted);
}
Outcome::Fail => {
self.result = ActionResult::FailedCall(
TraceError::Error(call_result.return_data.into()),
);
}
}
}
VmAction::CreateResult(create_result) => {
if !matches!(self.action, Action::Create(_)) {
bail!(JsonRpcError::internal_error());
}
match create_result.outcome {
Outcome::Success => {
self.result = ActionResult::Create(CreateResult {
gas_used: create_result.gas_left,
code: create_result.return_data.into(),
address: create_result.addr,
})
}
Outcome::Reverted => {
self.result =
ActionResult::FailedCreate(TraceError::Reverted);
}
Outcome::Fail => {
self.result = ActionResult::FailedCreate(
TraceError::Error(create_result.return_data.into()),
);
}
}
}
_ => bail!(JsonRpcError::internal_error()),
}
Ok(())
}
}
#[derive(Debug)]
pub struct Trace {
trace_address: Vec<usize>,
subtraces: usize,
action: Action,
result: ActionResult,
}
impl Serialize for Trace {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut struc = serializer.serialize_struct("Trace", 4)?;
match self.action {
Action::Call(ref call) => {
struc.serialize_field("type", "call")?;
struc.serialize_field("action", call)?;
}
Action::Create(ref create) => {
struc.serialize_field("type", "create")?;
struc.serialize_field("action", create)?;
}
Action::SelfDestruct(ref selfdestruct) => {
struc.serialize_field("type", "suicide")?;
struc.serialize_field("action", selfdestruct)?;
}
}
match self.result {
ActionResult::Call(ref call) => {
struc.serialize_field("result", call)?
}
ActionResult::Create(ref create) => {
struc.serialize_field("result", create)?
}
ActionResult::FailedCall(ref error) => {
struc.serialize_field("error", &error.to_string())?
}
ActionResult::FailedCreate(ref error) => {
struc.serialize_field("error", &error.to_string())?
}
ActionResult::None => {
struc.serialize_field("result", &None as &Option<u8>)?
}
}
struc.serialize_field("traceAddress", &self.trace_address)?;
struc.serialize_field("subtraces", &self.subtraces)?;
struc.end()
}
}
#[derive(Debug, Clone)]
pub enum TraceError {
Reverted,
Error(Bytes),
}
impl fmt::Display for TraceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message = match &self {
TraceError::Reverted => "Reverted",
TraceError::Error(b) => {
std::str::from_utf8(&b.0).map_err(|_| fmt::Error)?
}
};
message.fmt(f)
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetAuth {
pub address: Address,
pub chain_id: U256,
pub nonce: U256,
pub author: Option<Address>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalizedSetAuthTrace {
pub action: SetAuth,
pub result: VmSetAuthOutcome,
pub transaction_position: usize,
pub transaction_hash: H256,
pub block_number: u64,
pub block_hash: H256,
}
impl LocalizedSetAuthTrace {
pub fn new(
vm_action: &VmSetAuth, transaction_position: usize,
transaction_hash: H256, block_number: u64, block_hash: H256,
) -> Self {
let action = SetAuth {
address: vm_action.address,
chain_id: vm_action.chain_id,
nonce: vm_action.nonce,
author: vm_action.author,
};
Self {
action,
result: vm_action.outcome,
transaction_position,
transaction_hash,
block_number,
block_hash,
}
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EpochTrace {
cfx_traces: Vec<CfxLocalizedTrace>,
eth_traces: Vec<LocalizedTrace>,
mirror_address_map: HashMap<Address, RpcAddress>,
}
impl EpochTrace {
pub fn new(
cfx_traces: Vec<CfxLocalizedTrace>, eth_traces: Vec<LocalizedTrace>,
) -> Self {
let mut mirror_address_map = HashMap::new();
for t in &cfx_traces {
if let CfxRpcAction::Call(action) = &t.action {
if action.to.hex_address == CROSS_SPACE_CONTRACT_ADDRESS {
mirror_address_map.insert(
action.from.hex_address.evm_map().address,
action.from.clone(),
);
}
}
}
Self {
cfx_traces,
eth_traces,
mirror_address_map,
}
}
}