|
|
|
|
@@ -16,12 +16,19 @@
|
|
|
|
|
|
|
|
|
|
//! Tracing data types.
|
|
|
|
|
|
|
|
|
|
// ================== NOTE ========================
|
|
|
|
|
// IF YOU'RE ADDING A FIELD TO A STRUCT WITH
|
|
|
|
|
// RLP ENCODING, MAKE SURE IT'S DONE IN A BACKWARDS
|
|
|
|
|
// COMPATIBLE WAY!
|
|
|
|
|
// ================== NOTE ========================
|
|
|
|
|
|
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
use ethereum_types::{U256, Address, Bloom, BloomInput};
|
|
|
|
|
use parity_bytes::Bytes;
|
|
|
|
|
use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable};
|
|
|
|
|
use rlp_derive::{RlpEncodable, RlpDecodable};
|
|
|
|
|
use vm::ActionParams;
|
|
|
|
|
use evm::CallType;
|
|
|
|
|
use evm::ActionType;
|
|
|
|
|
use super::error::Error;
|
|
|
|
|
|
|
|
|
|
/// `Call` result.
|
|
|
|
|
@@ -33,6 +40,57 @@ pub struct CallResult {
|
|
|
|
|
pub output: Bytes,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `Call` type. Distinguish between different types of contract interactions.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub enum CallType {
|
|
|
|
|
/// Call
|
|
|
|
|
Call,
|
|
|
|
|
/// Call code
|
|
|
|
|
CallCode,
|
|
|
|
|
/// Delegate call
|
|
|
|
|
DelegateCall,
|
|
|
|
|
/// Static call
|
|
|
|
|
StaticCall,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<ActionType> for CallType {
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
fn try_from(action_type: ActionType) -> Result<Self, Self::Error> {
|
|
|
|
|
match action_type {
|
|
|
|
|
ActionType::Call => Ok(CallType::Call),
|
|
|
|
|
ActionType::CallCode => Ok(CallType::CallCode),
|
|
|
|
|
ActionType::DelegateCall => Ok(CallType::DelegateCall),
|
|
|
|
|
ActionType::StaticCall => Ok(CallType::StaticCall),
|
|
|
|
|
ActionType::Create => Err("Create cannot be converted to CallType"),
|
|
|
|
|
ActionType::Create2 => Err("Create2 cannot be converted to CallType"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Encodable for CallType {
|
|
|
|
|
fn rlp_append(&self, s: &mut RlpStream) {
|
|
|
|
|
let v = match *self {
|
|
|
|
|
CallType::Call => 0u32,
|
|
|
|
|
CallType::CallCode => 1,
|
|
|
|
|
CallType::DelegateCall => 2,
|
|
|
|
|
CallType::StaticCall => 3,
|
|
|
|
|
};
|
|
|
|
|
Encodable::rlp_append(&v, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Decodable for CallType {
|
|
|
|
|
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
|
|
|
|
rlp.as_val().and_then(|v| Ok(match v {
|
|
|
|
|
1u32 => CallType::Call,
|
|
|
|
|
2 => CallType::CallCode,
|
|
|
|
|
3 => CallType::DelegateCall,
|
|
|
|
|
4 => CallType::StaticCall,
|
|
|
|
|
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `Create` result.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
|
|
|
|
pub struct CreateResult {
|
|
|
|
|
@@ -51,6 +109,49 @@ impl CreateResult {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// `Create` method. Distinguish between use of `CREATE` and `CREATE2` opcodes in an action.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
|
pub enum CreationMethod {
|
|
|
|
|
/// Create
|
|
|
|
|
Create,
|
|
|
|
|
/// Create2
|
|
|
|
|
Create2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<ActionType> for CreationMethod {
|
|
|
|
|
type Error = &'static str;
|
|
|
|
|
fn try_from(action_type: ActionType) -> Result<Self, Self::Error> {
|
|
|
|
|
match action_type {
|
|
|
|
|
ActionType::Call => Err("Call cannot be converted to CreationMethod"),
|
|
|
|
|
ActionType::CallCode => Err("CallCode cannot be converted to CreationMethod"),
|
|
|
|
|
ActionType::DelegateCall => Err("DelegateCall cannot be converted to CreationMethod"),
|
|
|
|
|
ActionType::StaticCall => Err("StaticCall cannot be converted to CreationMethod"),
|
|
|
|
|
ActionType::Create => Ok(CreationMethod::Create),
|
|
|
|
|
ActionType::Create2 => Ok(CreationMethod::Create2),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Encodable for CreationMethod {
|
|
|
|
|
fn rlp_append(&self, s: &mut RlpStream) {
|
|
|
|
|
let v = match *self {
|
|
|
|
|
CreationMethod::Create => 0u32,
|
|
|
|
|
CreationMethod::Create2 => 1,
|
|
|
|
|
};
|
|
|
|
|
Encodable::rlp_append(&v, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Decodable for CreationMethod {
|
|
|
|
|
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
|
|
|
|
rlp.as_val().and_then(|v| Ok(match v {
|
|
|
|
|
0u32 => CreationMethod::Create,
|
|
|
|
|
1 => CreationMethod::Create2,
|
|
|
|
|
_ => return Err(DecoderError::Custom("Invalid value of CreationMethod item")),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Description of a _call_ action, either a `CALL` operation or a message transaction.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
|
|
|
|
pub struct Call {
|
|
|
|
|
@@ -65,19 +166,96 @@ pub struct Call {
|
|
|
|
|
/// The input data provided to the call.
|
|
|
|
|
pub input: Bytes,
|
|
|
|
|
/// The type of the call.
|
|
|
|
|
pub call_type: CallType,
|
|
|
|
|
pub call_type: BackwardsCompatibleCallType,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// This is essentially an `Option<CallType>`, but with a custom
|
|
|
|
|
/// `rlp` en/de-coding which preserves backwards compatibility with
|
|
|
|
|
/// the older encodings used in parity-ethereum versions < 2.7 and 2.7.0.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct BackwardsCompatibleCallType(pub Option<CallType>);
|
|
|
|
|
|
|
|
|
|
impl From<Option<CallType>> for BackwardsCompatibleCallType {
|
|
|
|
|
fn from(option: Option<CallType>) -> Self {
|
|
|
|
|
BackwardsCompatibleCallType(option)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Encoding is the same as `CallType_v2_6_x`.
|
|
|
|
|
impl Encodable for BackwardsCompatibleCallType {
|
|
|
|
|
fn rlp_append(&self, s: &mut RlpStream) {
|
|
|
|
|
let v = match self.0 {
|
|
|
|
|
None => 0u32,
|
|
|
|
|
Some(CallType::Call) => 1,
|
|
|
|
|
Some(CallType::CallCode) => 2,
|
|
|
|
|
Some(CallType::DelegateCall) => 3,
|
|
|
|
|
Some(CallType::StaticCall) => 4,
|
|
|
|
|
};
|
|
|
|
|
Encodable::rlp_append(&v, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to decode it as `CallType_v2_6_x` first, and then as `Option<CallType_v2_7_0>`.
|
|
|
|
|
impl Decodable for BackwardsCompatibleCallType {
|
|
|
|
|
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
|
|
|
|
if rlp.is_data() {
|
|
|
|
|
rlp.as_val().and_then(|v| Ok(match v {
|
|
|
|
|
0u32 => None,
|
|
|
|
|
1 => Some(CallType::Call),
|
|
|
|
|
2 => Some(CallType::CallCode),
|
|
|
|
|
3 => Some(CallType::DelegateCall),
|
|
|
|
|
4 => Some(CallType::StaticCall),
|
|
|
|
|
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
|
|
|
|
}.into()))
|
|
|
|
|
} else {
|
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
enum CallType_v2_7_0 {
|
|
|
|
|
Call,
|
|
|
|
|
CallCode,
|
|
|
|
|
DelegateCall,
|
|
|
|
|
StaticCall,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Decodable for CallType_v2_7_0 {
|
|
|
|
|
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
|
|
|
|
|
rlp.as_val().and_then(|v| Ok(match v {
|
|
|
|
|
0u32 => CallType_v2_7_0::Call,
|
|
|
|
|
1 => CallType_v2_7_0::CallCode,
|
|
|
|
|
2 => CallType_v2_7_0::DelegateCall,
|
|
|
|
|
3 => CallType_v2_7_0::StaticCall,
|
|
|
|
|
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<CallType_v2_7_0> for CallType {
|
|
|
|
|
fn from(old_call_type: CallType_v2_7_0) -> Self {
|
|
|
|
|
match old_call_type {
|
|
|
|
|
CallType_v2_7_0::Call => Self::Call,
|
|
|
|
|
CallType_v2_7_0::CallCode => Self::CallCode,
|
|
|
|
|
CallType_v2_7_0::DelegateCall => Self::DelegateCall,
|
|
|
|
|
CallType_v2_7_0::StaticCall => Self::StaticCall,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let optional: Option<CallType_v2_7_0> = Decodable::decode(rlp)?;
|
|
|
|
|
Ok(optional.map(Into::into).into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ActionParams> for Call {
|
|
|
|
|
fn from(p: ActionParams) -> Self {
|
|
|
|
|
match p.call_type {
|
|
|
|
|
CallType::DelegateCall | CallType::CallCode => Call {
|
|
|
|
|
match p.action_type {
|
|
|
|
|
ActionType::DelegateCall | ActionType::CallCode => Call {
|
|
|
|
|
from: p.address,
|
|
|
|
|
to: p.code_address,
|
|
|
|
|
value: p.value.value(),
|
|
|
|
|
gas: p.gas,
|
|
|
|
|
input: p.data.unwrap_or_else(Vec::new),
|
|
|
|
|
call_type: p.call_type,
|
|
|
|
|
call_type: CallType::try_from(p.action_type).ok().into(),
|
|
|
|
|
},
|
|
|
|
|
_ => Call {
|
|
|
|
|
from: p.sender,
|
|
|
|
|
@@ -85,7 +263,7 @@ impl From<ActionParams> for Call {
|
|
|
|
|
value: p.value.value(),
|
|
|
|
|
gas: p.gas,
|
|
|
|
|
input: p.data.unwrap_or_else(Vec::new),
|
|
|
|
|
call_type: p.call_type,
|
|
|
|
|
call_type: CallType::try_from(p.action_type).ok().into(),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -113,6 +291,9 @@ pub struct Create {
|
|
|
|
|
pub gas: U256,
|
|
|
|
|
/// The init code.
|
|
|
|
|
pub init: Bytes,
|
|
|
|
|
/// Creation method (CREATE vs CREATE2).
|
|
|
|
|
#[rlp(default)]
|
|
|
|
|
pub creation_method: Option<CreationMethod>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ActionParams> for Create {
|
|
|
|
|
@@ -122,6 +303,7 @@ impl From<ActionParams> for Create {
|
|
|
|
|
value: p.value.value(),
|
|
|
|
|
gas: p.gas,
|
|
|
|
|
init: p.code.map_or_else(Vec::new, |c| (*c).clone()),
|
|
|
|
|
creation_method: CreationMethod::try_from(p.action_type).ok().into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -429,3 +611,132 @@ pub struct VMTrace {
|
|
|
|
|
/// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction.
|
|
|
|
|
pub subs: Vec<VMTrace>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use rlp::{RlpStream, Encodable};
|
|
|
|
|
use rlp_derive::{RlpEncodable, RlpDecodable};
|
|
|
|
|
use super::{Address, Bytes, Call, CallType, Create, CreationMethod, U256};
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_call_type_backwards_compatibility() {
|
|
|
|
|
// Call type in version < 2.7.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, RlpEncodable)]
|
|
|
|
|
struct OldCall {
|
|
|
|
|
from: Address,
|
|
|
|
|
to: Address,
|
|
|
|
|
value: U256,
|
|
|
|
|
gas: U256,
|
|
|
|
|
input: Bytes,
|
|
|
|
|
call_type: OldCallType,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CallType type in version < 2.7.
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
|
enum OldCallType {
|
|
|
|
|
None,
|
|
|
|
|
Call,
|
|
|
|
|
CallCode,
|
|
|
|
|
DelegateCall,
|
|
|
|
|
StaticCall,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CallType rlp encoding in version < 2.7.
|
|
|
|
|
impl Encodable for OldCallType {
|
|
|
|
|
fn rlp_append(&self, s: &mut RlpStream) {
|
|
|
|
|
let v = match *self {
|
|
|
|
|
OldCallType::None => 0u32,
|
|
|
|
|
OldCallType::Call => 1,
|
|
|
|
|
OldCallType::CallCode => 2,
|
|
|
|
|
OldCallType::DelegateCall => 3,
|
|
|
|
|
OldCallType::StaticCall => 4,
|
|
|
|
|
};
|
|
|
|
|
Encodable::rlp_append(&v, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let old_call = OldCall {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
to: Address::from_low_u64_be(2),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
input: vec![5],
|
|
|
|
|
call_type: OldCallType::DelegateCall,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let old_encoded = rlp::encode(&old_call);
|
|
|
|
|
|
|
|
|
|
let new_call = Call {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
to: Address::from_low_u64_be(2),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
input: vec![5],
|
|
|
|
|
call_type: Some(CallType::DelegateCall).into(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// `old_call` should be deserialized successfully into `new_call`
|
|
|
|
|
assert_eq!(rlp::decode(&old_encoded), Ok(new_call.clone()));
|
|
|
|
|
// test a roundtrip with `Some` `call_type`
|
|
|
|
|
let new_encoded = rlp::encode(&new_call);
|
|
|
|
|
assert_eq!(rlp::decode(&new_encoded), Ok(new_call));
|
|
|
|
|
|
|
|
|
|
// test a roundtrip with `None` `call_type`
|
|
|
|
|
let none_call = Call {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
to: Address::from_low_u64_be(2),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
input: vec![5],
|
|
|
|
|
call_type: None.into(),
|
|
|
|
|
};
|
|
|
|
|
let none_encoded = rlp::encode(&none_call);
|
|
|
|
|
assert_eq!(rlp::decode(&none_encoded), Ok(none_call));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_creation_method_backwards_compatibility() {
|
|
|
|
|
// Create type in version < 2.7.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
|
|
|
|
|
struct OldCreate {
|
|
|
|
|
from: Address,
|
|
|
|
|
value: U256,
|
|
|
|
|
gas: U256,
|
|
|
|
|
init: Bytes,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let old_create = OldCreate {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
init: vec![5],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let old_encoded = rlp::encode(&old_create);
|
|
|
|
|
let new_create = Create {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
init: vec![5],
|
|
|
|
|
creation_method: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// `old_create` should be deserialized successfully into `new_create`
|
|
|
|
|
assert_eq!(rlp::decode(&old_encoded), Ok(new_create.clone()));
|
|
|
|
|
// test a roundtrip with `None` `creation_method`
|
|
|
|
|
let new_encoded = rlp::encode(&new_create);
|
|
|
|
|
assert_eq!(rlp::decode(&new_encoded), Ok(new_create));
|
|
|
|
|
|
|
|
|
|
// test a roundtrip with `Some` `creation_method`
|
|
|
|
|
let some_create = Create {
|
|
|
|
|
from: Address::from_low_u64_be(1),
|
|
|
|
|
value: U256::from(3),
|
|
|
|
|
gas: U256::from(4),
|
|
|
|
|
init: vec![5],
|
|
|
|
|
creation_method: Some(CreationMethod::Create2),
|
|
|
|
|
};
|
|
|
|
|
let some_encoded = rlp::encode(&some_create);
|
|
|
|
|
assert_eq!(rlp::decode(&some_encoded), Ok(some_create));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|