use std::convert::TryFrom;
use vapory_types::{U256, Address, Bloom, BloomInput};
use tetsy_bytes::Bytes;
use tetsy_rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable};
use tetsy_rlp_derive::{RlpEncodable, RlpDecodable};
use tetsy_vm::ActionParams;
use vvm::ActionType;
use super::error::Error;
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
pub struct CallResult {
pub gas_used: U256,
pub output: Bytes,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CallType {
Call,
CallCode,
DelegateCall,
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"),
}
}
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct CreateResult {
pub gas_used: U256,
pub code: Bytes,
pub address: Address,
}
impl CreateResult {
pub fn bloom(&self) -> Bloom {
BloomInput::Raw(self.address.as_bytes()).into()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CreationMethod {
Create,
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")),
}))
}
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Call {
pub from: Address,
pub to: Address,
pub value: U256,
pub gas: U256,
pub input: Bytes,
pub call_type: BackwardsCompatibleCallType,
}
#[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)
}
}
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);
}
}
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.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: CallType::try_from(p.action_type).ok().into(),
},
_ => Call {
from: p.sender,
to: p.address,
value: p.value.value(),
gas: p.gas,
input: p.data.unwrap_or_else(Vec::new),
call_type: CallType::try_from(p.action_type).ok().into(),
},
}
}
}
impl Call {
pub fn bloom(&self) -> Bloom {
let mut bloom = Bloom::default();
bloom.accrue(BloomInput::Raw(self.from.as_bytes()));
bloom.accrue(BloomInput::Raw(self.to.as_bytes()));
bloom
}
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Create {
pub from: Address,
pub value: U256,
pub gas: U256,
pub init: Bytes,
#[rlp(default)]
pub creation_method: Option<CreationMethod>,
}
impl From<ActionParams> for Create {
fn from(p: ActionParams) -> Self {
Create {
from: p.sender,
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(),
}
}
}
impl Create {
pub fn bloom(&self) -> Bloom {
BloomInput::Raw(self.from.as_bytes()).into()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum RewardType {
Block,
Uncle,
EmptyStep,
External,
}
impl Encodable for RewardType {
fn rlp_append(&self, s: &mut RlpStream) {
let v = match *self {
RewardType::Block => 0u32,
RewardType::Uncle => 1,
RewardType::EmptyStep => 2,
RewardType::External => 3,
};
Encodable::rlp_append(&v, s);
}
}
impl Decodable for RewardType {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
rlp.as_val().and_then(|v| Ok(match v {
0u32 => RewardType::Block,
1 => RewardType::Uncle,
2 => RewardType::EmptyStep,
3 => RewardType::External,
_ => return Err(DecoderError::Custom("Invalid value of RewardType item")),
}))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Reward {
pub author: Address,
pub value: U256,
pub reward_type: RewardType,
}
impl Reward {
pub fn bloom(&self) -> Bloom {
BloomInput::Raw(self.author.as_bytes()).into()
}
}
impl Encodable for Reward {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.author);
s.append(&self.value);
s.append(&self.reward_type);
}
}
impl Decodable for Reward {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let res = Reward {
author: rlp.val_at(0)?,
value: rlp.val_at(1)?,
reward_type: rlp.val_at(2)?,
};
Ok(res)
}
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct Suicide {
pub address: Address,
pub refund_address: Address,
pub balance: U256,
}
impl Suicide {
pub fn bloom(&self) -> Bloom {
let mut bloom = Bloom::default();
bloom.accrue(BloomInput::Raw(self.address.as_bytes()));
bloom.accrue(BloomInput::Raw(self.refund_address.as_bytes()));
bloom
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Action {
Call(Call),
Create(Create),
Suicide(Suicide),
Reward(Reward),
}
impl Encodable for Action {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
match *self {
Action::Call(ref call) => {
s.append(&0u8);
s.append(call);
},
Action::Create(ref create) => {
s.append(&1u8);
s.append(create);
},
Action::Suicide(ref suicide) => {
s.append(&2u8);
s.append(suicide);
},
Action::Reward(ref reward) => {
s.append(&3u8);
s.append(reward);
}
}
}
}
impl Decodable for Action {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let action_type: u8 = rlp.val_at(0)?;
match action_type {
0 => rlp.val_at(1).map(Action::Call),
1 => rlp.val_at(1).map(Action::Create),
2 => rlp.val_at(1).map(Action::Suicide),
3 => rlp.val_at(1).map(Action::Reward),
_ => Err(DecoderError::Custom("Invalid action type.")),
}
}
}
impl Action {
pub fn bloom(&self) -> Bloom {
match *self {
Action::Call(ref call) => call.bloom(),
Action::Create(ref create) => create.bloom(),
Action::Suicide(ref suicide) => suicide.bloom(),
Action::Reward(ref reward) => reward.bloom(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Res {
Call(CallResult),
Create(CreateResult),
FailedCall(Error),
FailedCreate(Error),
None,
}
impl Encodable for Res {
fn rlp_append(&self, s: &mut RlpStream) {
match *self {
Res::Call(ref call) => {
s.begin_list(2);
s.append(&0u8);
s.append(call);
},
Res::Create(ref create) => {
s.begin_list(2);
s.append(&1u8);
s.append(create);
},
Res::FailedCall(ref err) => {
s.begin_list(2);
s.append(&2u8);
s.append(err);
},
Res::FailedCreate(ref err) => {
s.begin_list(2);
s.append(&3u8);
s.append(err);
},
Res::None => {
s.begin_list(1);
s.append(&4u8);
}
}
}
}
impl Decodable for Res {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let action_type: u8 = rlp.val_at(0)?;
match action_type {
0 => rlp.val_at(1).map(Res::Call),
1 => rlp.val_at(1).map(Res::Create),
2 => rlp.val_at(1).map(Res::FailedCall),
3 => rlp.val_at(1).map(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")),
}
}
}
impl Res {
pub fn bloom(&self) -> Bloom {
match *self {
Res::Create(ref create) => create.bloom(),
Res::Call(_) | Res::FailedCall(_) | Res::FailedCreate(_) | Res::None => Default::default(),
}
}
pub fn succeeded(&self) -> bool {
match *self {
Res::Call(_) | Res::Create(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct MemoryDiff {
pub offset: usize,
pub data: Bytes,
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct StorageDiff {
pub location: U256,
pub value: U256,
}
#[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)]
pub struct VMExecutedOperation {
pub gas_used: U256,
pub stack_push: Vec<U256>,
pub mem_diff: Option<MemoryDiff>,
pub store_diff: Option<StorageDiff>,
}
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
pub struct VMOperation {
pub pc: usize,
pub instruction: u8,
pub gas_cost: U256,
pub executed: Option<VMExecutedOperation>,
}
#[derive(Debug, Clone, PartialEq, Default, RlpEncodable, RlpDecodable)]
pub struct VMTrace {
pub parent_step: usize,
pub code: Bytes,
pub operations: Vec<VMOperation>,
pub subs: Vec<VMTrace>,
}
#[cfg(test)]
mod tests {
use tetsy_rlp::{RlpStream, Encodable};
use tetsy_rlp_derive::{RlpEncodable, RlpDecodable};
use super::{Address, Bytes, Call, CallType, Create, CreationMethod, U256};
#[test]
fn test_call_type_backwards_compatibility() {
#[derive(Debug, Clone, PartialEq, RlpEncodable)]
struct OldCall {
from: Address,
to: Address,
value: U256,
gas: U256,
input: Bytes,
call_type: OldCallType,
}
#[allow(dead_code)]
#[derive(Debug, PartialEq, Clone)]
enum OldCallType {
None,
Call,
CallCode,
DelegateCall,
StaticCall,
}
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 = tetsy_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(),
};
assert_eq!(tetsy_rlp::decode(&old_encoded), Ok(new_call.clone()));
let new_encoded = tetsy_rlp::encode(&new_call);
assert_eq!(tetsy_rlp::decode(&new_encoded), Ok(new_call));
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 = tetsy_rlp::encode(&none_call);
assert_eq!(tetsy_rlp::decode(&none_encoded), Ok(none_call));
}
#[test]
fn test_creation_method_backwards_compatibility() {
#[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 = tetsy_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,
};
assert_eq!(tetsy_rlp::decode(&old_encoded), Ok(new_create.clone()));
let new_encoded = tetsy_rlp::encode(&new_create);
assert_eq!(tetsy_rlp::decode(&new_encoded), Ok(new_create));
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 = tetsy_rlp::encode(&some_create);
assert_eq!(tetsy_rlp::decode(&some_encoded), Ok(some_create));
}
}