Reformat the source code

This commit is contained in:
Artem Vorotnikov
2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
742 changed files with 175791 additions and 141379 deletions

View File

@@ -15,10 +15,10 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params.
use ethereum_types::{U256, H256, Address};
use bytes::Bytes;
use hash::{keccak, KECCAK_EMPTY};
use ethereum_types::{Address, H256, U256};
use ethjson;
use hash::{keccak, KECCAK_EMPTY};
use call_type::CallType;
@@ -27,107 +27,110 @@ use std::sync::Arc;
/// Transaction value
#[derive(Clone, Debug)]
pub enum ActionValue {
/// Value that should be transfered
Transfer(U256),
/// Apparent value for transaction (not transfered)
Apparent(U256)
/// Value that should be transfered
Transfer(U256),
/// Apparent value for transaction (not transfered)
Apparent(U256),
}
/// Type of the way parameters encoded
#[derive(Clone, Debug)]
pub enum ParamsType {
/// Parameters are included in code
Embedded,
/// Parameters are passed in data section
Separate,
/// Parameters are included in code
Embedded,
/// Parameters are passed in data section
Separate,
}
impl ActionValue {
/// Returns action value as U256.
pub fn value(&self) -> U256 {
match *self {
ActionValue::Transfer(x) | ActionValue::Apparent(x) => x
}
}
/// Returns action value as U256.
pub fn value(&self) -> U256 {
match *self {
ActionValue::Transfer(x) | ActionValue::Apparent(x) => x,
}
}
/// Returns the transfer action value of the U256-convertable raw value
pub fn transfer<T: Into<U256>>(transfer_value: T) -> ActionValue {
ActionValue::Transfer(transfer_value.into())
}
/// Returns the transfer action value of the U256-convertable raw value
pub fn transfer<T: Into<U256>>(transfer_value: T) -> ActionValue {
ActionValue::Transfer(transfer_value.into())
}
/// Returns the apparent action value of the U256-convertable raw value
pub fn apparent<T: Into<U256>>(apparent_value: T) -> ActionValue {
ActionValue::Apparent(apparent_value.into())
}
/// Returns the apparent action value of the U256-convertable raw value
pub fn apparent<T: Into<U256>>(apparent_value: T) -> ActionValue {
ActionValue::Apparent(apparent_value.into())
}
}
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
/// Action (call/create) input params. Everything else should be specified in Externalities.
#[derive(Clone, Debug)]
pub struct ActionParams {
/// Address of currently executed code.
pub code_address: Address,
/// Hash of currently executed code.
pub code_hash: Option<H256>,
/// Receive address. Usually equal to code_address,
/// except when called using CALLCODE.
pub address: Address,
/// Sender of current part of the transaction.
pub sender: Address,
/// Transaction initiator.
pub origin: Address,
/// Gas paid up front for transaction execution
pub gas: U256,
/// Gas price.
pub gas_price: U256,
/// Transaction value.
pub value: ActionValue,
/// Code being executed.
pub code: Option<Arc<Bytes>>,
/// Input data.
pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
/// Param types encoding
pub params_type: ParamsType,
/// Address of currently executed code.
pub code_address: Address,
/// Hash of currently executed code.
pub code_hash: Option<H256>,
/// Receive address. Usually equal to code_address,
/// except when called using CALLCODE.
pub address: Address,
/// Sender of current part of the transaction.
pub sender: Address,
/// Transaction initiator.
pub origin: Address,
/// Gas paid up front for transaction execution
pub gas: U256,
/// Gas price.
pub gas_price: U256,
/// Transaction value.
pub value: ActionValue,
/// Code being executed.
pub code: Option<Arc<Bytes>>,
/// Input data.
pub data: Option<Bytes>,
/// Type of call
pub call_type: CallType,
/// Param types encoding
pub params_type: ParamsType,
}
impl Default for ActionParams {
/// Returns default ActionParams initialized with zeros
fn default() -> ActionParams {
ActionParams {
code_address: Address::new(),
code_hash: Some(KECCAK_EMPTY),
address: Address::new(),
sender: Address::new(),
origin: Address::new(),
gas: U256::zero(),
gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()),
code: None,
data: None,
call_type: CallType::None,
params_type: ParamsType::Separate,
}
}
/// Returns default ActionParams initialized with zeros
fn default() -> ActionParams {
ActionParams {
code_address: Address::new(),
code_hash: Some(KECCAK_EMPTY),
address: Address::new(),
sender: Address::new(),
origin: Address::new(),
gas: U256::zero(),
gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()),
code: None,
data: None,
call_type: CallType::None,
params_type: ParamsType::Separate,
}
}
}
impl From<ethjson::vm::Transaction> for ActionParams {
fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams {
code_address: Address::new(),
code_hash: Some(keccak(&*t.code)),
address: address,
sender: t.sender.into(),
origin: t.origin.into(),
code: Some(Arc::new(t.code.into())),
data: Some(t.data.into()),
gas: t.gas.into(),
gas_price: t.gas_price.into(),
value: ActionValue::Transfer(t.value.into()),
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
params_type: ParamsType::Separate,
}
}
fn from(t: ethjson::vm::Transaction) -> Self {
let address: Address = t.address.into();
ActionParams {
code_address: Address::new(),
code_hash: Some(keccak(&*t.code)),
address: address,
sender: t.sender.into(),
origin: t.origin.into(),
code: Some(Arc::new(t.code.into())),
data: Some(t.data.into()),
gas: t.gas.into(),
gas_price: t.gas_price.into(),
value: ActionValue::Transfer(t.value.into()),
call_type: match address.is_zero() {
true => CallType::None,
false => CallType::Call,
}, // TODO @debris is this correct?
params_type: ParamsType::Separate,
}
}
}

View File

@@ -16,71 +16,73 @@
//! EVM call types.
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
/// The type of the call-like instruction.
#[derive(Debug, PartialEq, Clone)]
pub enum CallType {
/// Not a CALL.
None,
/// CALL.
Call,
/// CALLCODE.
CallCode,
/// DELEGATECALL.
DelegateCall,
/// STATICCALL
StaticCall,
/// Not a CALL.
None,
/// CALL.
Call,
/// CALLCODE.
CallCode,
/// DELEGATECALL.
DelegateCall,
/// STATICCALL
StaticCall,
}
impl Encodable for CallType {
fn rlp_append(&self, s: &mut RlpStream) {
let v = match *self {
CallType::None => 0u32,
CallType::Call => 1,
CallType::CallCode => 2,
CallType::DelegateCall => 3,
CallType::StaticCall => 4,
};
Encodable::rlp_append(&v, s);
}
fn rlp_append(&self, s: &mut RlpStream) {
let v = match *self {
CallType::None => 0u32,
CallType::Call => 1,
CallType::CallCode => 2,
CallType::DelegateCall => 3,
CallType::StaticCall => 4,
};
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 {
0u32 => CallType::None,
1 => CallType::Call,
2 => CallType::CallCode,
3 => CallType::DelegateCall,
4 => CallType::StaticCall,
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
}))
}
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
rlp.as_val().and_then(|v| {
Ok(match v {
0u32 => CallType::None,
1 => CallType::Call,
2 => CallType::CallCode,
3 => CallType::DelegateCall,
4 => CallType::StaticCall,
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
})
})
}
}
#[cfg(test)]
mod tests {
use rlp::*;
use super::CallType;
use super::CallType;
use rlp::*;
#[test]
fn encode_call_type() {
let ct = CallType::Call;
#[test]
fn encode_call_type() {
let ct = CallType::Call;
let mut s = RlpStream::new_list(2);
s.append(&ct);
assert!(!s.is_finished(), "List shouldn't finished yet");
s.append(&ct);
assert!(s.is_finished(), "List should be finished now");
s.out();
}
let mut s = RlpStream::new_list(2);
s.append(&ct);
assert!(!s.is_finished(), "List shouldn't finished yet");
s.append(&ct);
assert!(s.is_finished(), "List should be finished now");
s.out();
}
#[test]
fn should_encode_and_decode_call_type() {
let original = CallType::Call;
let encoded = encode(&original);
let decoded = decode(&encoded).expect("failure decoding CallType");
assert_eq!(original, decoded);
}
#[test]
fn should_encode_and_decode_call_type() {
let original = CallType::Call;
let encoded = encode(&original);
let decoded = decode(&encoded).expect("failure decoding CallType");
assert_eq!(original, decoded);
}
}

View File

@@ -16,11 +16,10 @@
//! Environment information for transaction execution.
use std::cmp;
use std::sync::Arc;
use hash::keccak;
use ethereum_types::{U256, H256, Address};
use ethereum_types::{Address, H256, U256};
use ethjson;
use hash::keccak;
use std::{cmp, sync::Arc};
type BlockNumber = u64;
@@ -31,79 +30,88 @@ pub type LastHashes = Vec<H256>;
/// Information concerning the execution environment for a message-call/contract-creation.
#[derive(Debug, Clone)]
pub struct EnvInfo {
/// The block number.
pub number: BlockNumber,
/// The block author.
pub author: Address,
/// The block timestamp.
pub timestamp: u64,
/// The block difficulty.
pub difficulty: U256,
/// The block gas limit.
pub gas_limit: U256,
/// The last 256 block hashes.
pub last_hashes: Arc<LastHashes>,
/// The gas used.
pub gas_used: U256,
/// The block number.
pub number: BlockNumber,
/// The block author.
pub author: Address,
/// The block timestamp.
pub timestamp: u64,
/// The block difficulty.
pub difficulty: U256,
/// The block gas limit.
pub gas_limit: U256,
/// The last 256 block hashes.
pub last_hashes: Arc<LastHashes>,
/// The gas used.
pub gas_used: U256,
}
impl Default for EnvInfo {
fn default() -> Self {
EnvInfo {
number: 0,
author: Address::default(),
timestamp: 0,
difficulty: 0.into(),
gas_limit: 0.into(),
last_hashes: Arc::new(vec![]),
gas_used: 0.into(),
}
}
fn default() -> Self {
EnvInfo {
number: 0,
author: Address::default(),
timestamp: 0,
difficulty: 0.into(),
gas_limit: 0.into(),
last_hashes: Arc::new(vec![]),
gas_used: 0.into(),
}
}
}
impl From<ethjson::vm::Env> for EnvInfo {
fn from(e: ethjson::vm::Env) -> Self {
let number = e.number.into();
EnvInfo {
number,
author: e.author.into(),
difficulty: e.difficulty.into(),
gas_limit: e.gas_limit.into(),
timestamp: e.timestamp.into(),
last_hashes: Arc::new((1..cmp::min(number + 1, 257)).map(|i| keccak(format!("{}", number - i).as_bytes())).collect()),
gas_used: U256::default(),
}
}
fn from(e: ethjson::vm::Env) -> Self {
let number = e.number.into();
EnvInfo {
number,
author: e.author.into(),
difficulty: e.difficulty.into(),
gas_limit: e.gas_limit.into(),
timestamp: e.timestamp.into(),
last_hashes: Arc::new(
(1..cmp::min(number + 1, 257))
.map(|i| keccak(format!("{}", number - i).as_bytes()))
.collect(),
),
gas_used: U256::default(),
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
use ethereum_types::{U256, Address};
use ethjson;
use super::*;
use ethereum_types::{Address, U256};
use ethjson;
use std::str::FromStr;
#[test]
fn it_serializes_from_json() {
let env_info = EnvInfo::from(ethjson::vm::Env {
author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
number: ethjson::uint::Uint(U256::from(1_112_339)),
difficulty: ethjson::uint::Uint(U256::from(50_000)),
gas_limit: ethjson::uint::Uint(U256::from(40_000)),
timestamp: ethjson::uint::Uint(U256::from(1_100))
});
#[test]
fn it_serializes_from_json() {
let env_info = EnvInfo::from(ethjson::vm::Env {
author: ethjson::hash::Address(
Address::from_str("000000f00000000f000000000000f00000000f00").unwrap(),
),
number: ethjson::uint::Uint(U256::from(1_112_339)),
difficulty: ethjson::uint::Uint(U256::from(50_000)),
gas_limit: ethjson::uint::Uint(U256::from(40_000)),
timestamp: ethjson::uint::Uint(U256::from(1_100)),
});
assert_eq!(env_info.number, 1112339);
assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap());
assert_eq!(env_info.gas_limit, 40000.into());
assert_eq!(env_info.difficulty, 50000.into());
assert_eq!(env_info.gas_used, 0.into());
}
assert_eq!(env_info.number, 1112339);
assert_eq!(
env_info.author,
Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()
);
assert_eq!(env_info.gas_limit, 40000.into());
assert_eq!(env_info.difficulty, 50000.into());
assert_eq!(env_info.gas_used, 0.into());
}
#[test]
fn it_can_be_created_as_default() {
let default_env_info = EnvInfo::default();
#[test]
fn it_can_be_created_as_default() {
let default_env_info = EnvInfo::default();
assert_eq!(default_env_info.difficulty, 0.into());
}
assert_eq!(default_env_info.difficulty, 0.into());
}
}

View File

@@ -16,104 +16,115 @@
//! VM errors module
use ::{ResumeCall, ResumeCreate};
use ethereum_types::Address;
use action_params::ActionParams;
use std::fmt;
use ethereum_types::Address;
use ethtrie;
use std::fmt;
use ResumeCall;
use ResumeCreate;
#[derive(Debug)]
pub enum TrapKind {
Call(ActionParams),
Create(ActionParams, Address),
Call(ActionParams),
Create(ActionParams, Address),
}
pub enum TrapError<Call, Create> {
Call(ActionParams, Call),
Create(ActionParams, Address, Create),
Call(ActionParams, Call),
Create(ActionParams, Address, Create),
}
/// VM errors.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
/// transaction execution. But it does not mean that transaction
/// was invalid. Balance still should be transfered and nonce
/// should be increased.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination {
/// Position the code tried to jump to.
destination: usize
},
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction {
/// Unrecognized opcode
instruction: u8,
},
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements was requested by instruction
wanted: usize,
/// How many elements were on stack
on_stack: usize
},
/// When execution would exceed defined Stack Limit
OutOfStack {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements instruction wanted to push
wanted: usize,
/// What was the stack limit
limit: usize
},
/// Built-in contract failed on given input
BuiltIn(&'static str),
/// When execution tries to modify the state in static context
MutableCallInStaticContext,
/// Likely to cause consensus issues.
Internal(String),
/// Wasm runtime error
Wasm(String),
/// Out of bounds access in RETURNDATACOPY.
OutOfBounds,
/// Execution has been reverted with REVERT.
Reverted,
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
/// transaction execution. But it does not mean that transaction
/// was invalid. Balance still should be transfered and nonce
/// should be increased.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination {
/// Position the code tried to jump to.
destination: usize,
},
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction {
/// Unrecognized opcode
instruction: u8,
},
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements was requested by instruction
wanted: usize,
/// How many elements were on stack
on_stack: usize,
},
/// When execution would exceed defined Stack Limit
OutOfStack {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements instruction wanted to push
wanted: usize,
/// What was the stack limit
limit: usize,
},
/// Built-in contract failed on given input
BuiltIn(&'static str),
/// When execution tries to modify the state in static context
MutableCallInStaticContext,
/// Likely to cause consensus issues.
Internal(String),
/// Wasm runtime error
Wasm(String),
/// Out of bounds access in RETURNDATACOPY.
OutOfBounds,
/// Execution has been reverted with REVERT.
Reverted,
}
impl From<Box<ethtrie::TrieError>> for Error {
fn from(err: Box<ethtrie::TrieError>) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
fn from(err: Box<ethtrie::TrieError>) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
}
impl From<ethtrie::TrieError> for Error {
fn from(err: ethtrie::TrieError) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
fn from(err: ethtrie::TrieError) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
OutOfGas => write!(f, "Out of gas"),
BadJumpDestination { destination } => write!(f, "Bad jump destination {:x}", destination),
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
StackUnderflow { instruction, wanted, on_stack } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack),
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
Internal(ref msg) => write!(f, "Internal error: {}", msg),
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
OutOfBounds => write!(f, "Out of bounds"),
Reverted => write!(f, "Reverted"),
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
OutOfGas => write!(f, "Out of gas"),
BadJumpDestination { destination } => {
write!(f, "Bad jump destination {:x}", destination)
}
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
StackUnderflow {
instruction,
wanted,
on_stack,
} => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack),
OutOfStack {
instruction,
wanted,
limit,
} => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
Internal(ref msg) => write!(f, "Internal error: {}", msg),
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
OutOfBounds => write!(f, "Out of bounds"),
Reverted => write!(f, "Reverted"),
}
}
}
pub type Result<T> = ::std::result::Result<T, Error>;

View File

@@ -16,162 +16,172 @@
//! Interface for Evm externalities.
use std::sync::Arc;
use ethereum_types::{U256, H256, Address};
use bytes::Bytes;
use call_type::CallType;
use env_info::EnvInfo;
use schedule::Schedule;
use return_data::ReturnData;
use error::{Result, TrapKind};
use ethereum_types::{Address, H256, U256};
use return_data::ReturnData;
use schedule::Schedule;
use std::sync::Arc;
#[derive(Debug)]
/// Result of externalities create function.
pub enum ContractCreateResult {
/// Returned when creation was successfull.
/// Contains an address of newly created contract and gas left.
Created(Address, U256),
/// Returned when contract creation failed.
/// VM doesn't have to know the reason.
Failed,
/// Reverted with REVERT.
Reverted(U256, ReturnData),
/// Returned when creation was successfull.
/// Contains an address of newly created contract and gas left.
Created(Address, U256),
/// Returned when contract creation failed.
/// VM doesn't have to know the reason.
Failed,
/// Reverted with REVERT.
Reverted(U256, ReturnData),
}
#[derive(Debug)]
/// Result of externalities call function.
pub enum MessageCallResult {
/// Returned when message call was successfull.
/// Contains gas left and output data.
Success(U256, ReturnData),
/// Returned when message call failed.
/// VM doesn't have to know the reason.
Failed,
/// Returned when message call was reverted.
/// Contains gas left and output data.
Reverted(U256, ReturnData),
/// Returned when message call was successfull.
/// Contains gas left and output data.
Success(U256, ReturnData),
/// Returned when message call failed.
/// VM doesn't have to know the reason.
Failed,
/// Returned when message call was reverted.
/// Contains gas left and output data.
Reverted(U256, ReturnData),
}
/// Specifies how an address is calculated for a new contract.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum CreateContractAddress {
/// Address is calculated from sender and nonce. pWASM `create` scheme.
FromSenderAndNonce,
/// Address is calculated from sender, salt and code hash. pWASM `create2` scheme and EIP-1014 CREATE2 scheme.
FromSenderSaltAndCodeHash(H256),
/// Address is calculated from code hash and sender. Used by pwasm create ext.
FromSenderAndCodeHash,
/// Address is calculated from sender and nonce. pWASM `create` scheme.
FromSenderAndNonce,
/// Address is calculated from sender, salt and code hash. pWASM `create2` scheme and EIP-1014 CREATE2 scheme.
FromSenderSaltAndCodeHash(H256),
/// Address is calculated from code hash and sender. Used by pwasm create ext.
FromSenderAndCodeHash,
}
/// Externalities interface for EVMs
pub trait Ext {
/// Returns the storage value for a given key if reversion happens on the current transaction.
fn initial_storage_at(&self, key: &H256) -> Result<H256>;
/// Returns the storage value for a given key if reversion happens on the current transaction.
fn initial_storage_at(&self, key: &H256) -> Result<H256>;
/// Returns a value for given key.
fn storage_at(&self, key: &H256) -> Result<H256>;
/// Returns a value for given key.
fn storage_at(&self, key: &H256) -> Result<H256>;
/// Stores a value for given key.
fn set_storage(&mut self, key: H256, value: H256) -> Result<()>;
/// Stores a value for given key.
fn set_storage(&mut self, key: H256, value: H256) -> Result<()>;
/// Determine whether an account exists.
fn exists(&self, address: &Address) -> Result<bool>;
/// Determine whether an account exists.
fn exists(&self, address: &Address) -> Result<bool>;
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
fn exists_and_not_null(&self, address: &Address) -> Result<bool>;
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
fn exists_and_not_null(&self, address: &Address) -> Result<bool>;
/// Balance of the origin account.
fn origin_balance(&self) -> Result<U256>;
/// Balance of the origin account.
fn origin_balance(&self) -> Result<U256>;
/// Returns address balance.
fn balance(&self, address: &Address) -> Result<U256>;
/// Returns address balance.
fn balance(&self, address: &Address) -> Result<U256>;
/// Returns the hash of one of the 256 most recent complete blocks.
fn blockhash(&mut self, number: &U256) -> H256;
/// Returns the hash of one of the 256 most recent complete blocks.
fn blockhash(&mut self, number: &U256) -> H256;
/// Creates new contract.
///
/// Returns gas_left and contract address if contract creation was successful.
fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>;
/// Creates new contract.
///
/// Returns gas_left and contract address if contract creation was successful.
fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>;
/// Message call.
///
/// Returns Err, if we run out of gas.
/// Otherwise returns call_result which contains gas left
/// and true if subcall was successfull.
fn call(
&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
call_type: CallType,
trap: bool
) -> ::std::result::Result<MessageCallResult, TrapKind>;
/// Message call.
///
/// Returns Err, if we run out of gas.
/// Otherwise returns call_result which contains gas left
/// and true if subcall was successfull.
fn call(
&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
call_type: CallType,
trap: bool,
) -> ::std::result::Result<MessageCallResult, TrapKind>;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>>;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>>;
/// Returns code hash at given address
fn extcodehash(&self, address: &Address) -> Result<Option<H256>>;
/// Returns code hash at given address
fn extcodehash(&self, address: &Address) -> Result<Option<H256>>;
/// Returns code size at given address
fn extcodesize(&self, address: &Address) -> Result<Option<usize>>;
/// Returns code size at given address
fn extcodesize(&self, address: &Address) -> Result<Option<usize>>;
/// Creates log entry with given topics and data
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()>;
/// Creates log entry with given topics and data
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()>;
/// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high.
fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result<U256>;
/// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high.
fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> Result<U256>;
/// Should be called when contract commits suicide.
/// Address to which funds should be refunded.
fn suicide(&mut self, refund_address: &Address) -> Result<()> ;
/// Should be called when contract commits suicide.
/// Address to which funds should be refunded.
fn suicide(&mut self, refund_address: &Address) -> Result<()>;
/// Returns schedule.
fn schedule(&self) -> &Schedule;
/// Returns schedule.
fn schedule(&self) -> &Schedule;
/// Returns environment info.
fn env_info(&self) -> &EnvInfo;
/// Returns environment info.
fn env_info(&self) -> &EnvInfo;
/// Returns the chain ID of the blockchain
fn chain_id(&self) -> u64;
/// Returns the chain ID of the blockchain
fn chain_id(&self) -> u64;
/// Returns current depth of execution.
///
/// If contract A calls contract B, and contract B calls C,
/// then A depth is 0, B is 1, C is 2 and so on.
fn depth(&self) -> usize;
/// Returns current depth of execution.
///
/// If contract A calls contract B, and contract B calls C,
/// then A depth is 0, B is 1, C is 2 and so on.
fn depth(&self) -> usize;
/// Increments sstore refunds counter.
fn add_sstore_refund(&mut self, value: usize);
/// Increments sstore refunds counter.
fn add_sstore_refund(&mut self, value: usize);
/// Decrements sstore refunds counter.
fn sub_sstore_refund(&mut self, value: usize);
/// Decrements sstore refunds counter.
fn sub_sstore_refund(&mut self, value: usize);
/// Decide if any more operations should be traced. Passthrough for the VM trace.
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false }
/// Decide if any more operations should be traced. Passthrough for the VM trace.
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool {
false
}
/// Prepare to trace an operation. Passthrough for the VM trace.
/// For each call of `trace_prepare_execute` either `trace_failed` or `trace_executed` MUST be called.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {}
/// Prepare to trace an operation. Passthrough for the VM trace.
/// For each call of `trace_prepare_execute` either `trace_failed` or `trace_executed` MUST be called.
fn trace_prepare_execute(
&mut self,
_pc: usize,
_instruction: u8,
_gas_cost: U256,
_mem_written: Option<(usize, usize)>,
_store_written: Option<(U256, U256)>,
) {
}
/// Trace the execution failure of a single instruction.
fn trace_failed(&mut self) {}
/// Trace the execution failure of a single instruction.
fn trace_failed(&mut self) {}
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}
/// Check if running in static context.
fn is_static(&self) -> bool;
/// Check if running in static context.
fn is_static(&self) -> bool;
}

View File

@@ -17,46 +17,46 @@
//! Virtual machines support library
extern crate ethereum_types;
extern crate parity_bytes as bytes;
extern crate ethjson;
extern crate rlp;
extern crate keccak_hash as hash;
extern crate parity_bytes as bytes;
extern crate patricia_trie_ethereum as ethtrie;
extern crate rlp;
mod action_params;
mod call_type;
mod env_info;
mod schedule;
mod error;
mod ext;
mod return_data;
mod error;
mod schedule;
pub mod tests;
pub use action_params::{ActionParams, ActionValue, ParamsType};
pub use call_type::CallType;
pub use env_info::{EnvInfo, LastHashes};
pub use schedule::{Schedule, CleanDustMode, WasmCosts};
pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress};
pub use return_data::{ReturnData, GasLeft};
pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError};
pub use error::{Error, ExecTrapError, ExecTrapResult, Result, TrapError, TrapKind, TrapResult};
pub use ext::{ContractCreateResult, CreateContractAddress, Ext, MessageCallResult};
pub use return_data::{GasLeft, ReturnData};
pub use schedule::{CleanDustMode, Schedule, WasmCosts};
/// Virtual Machine interface
pub trait Exec: Send {
/// This function should be used to execute transaction.
/// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left.
fn exec(self: Box<Self>, ext: &mut Ext) -> ExecTrapResult<GasLeft>;
/// This function should be used to execute transaction.
/// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left.
fn exec(self: Box<Self>, ext: &mut Ext) -> ExecTrapResult<GasLeft>;
}
/// Resume call interface
pub trait ResumeCall: Send {
/// Resume an execution for call, returns back the Vm interface.
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<Exec>;
/// Resume an execution for call, returns back the Vm interface.
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<Exec>;
}
/// Resume create interface
pub trait ResumeCreate: Send {
/// Resume an execution from create, returns back the Vm interface.
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<Exec>;
/// Resume an execution from create, returns back the Vm interface.
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<Exec>;
}

View File

@@ -21,46 +21,46 @@ use ethereum_types::U256;
/// Return data buffer. Holds memory from a previous call and a slice into that memory.
#[derive(Debug)]
pub struct ReturnData {
mem: Vec<u8>,
offset: usize,
size: usize,
mem: Vec<u8>,
offset: usize,
size: usize,
}
impl ::std::ops::Deref for ReturnData {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mem[self.offset..self.offset + self.size]
}
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mem[self.offset..self.offset + self.size]
}
}
impl ReturnData {
/// Create empty `ReturnData`.
pub fn empty() -> Self {
ReturnData {
mem: Vec::new(),
offset: 0,
size: 0,
}
}
/// Create `ReturnData` from give buffer and slice.
pub fn new(mem: Vec<u8>, offset: usize, size: usize) -> Self {
ReturnData { mem, offset, size }
}
/// Create empty `ReturnData`.
pub fn empty() -> Self {
ReturnData {
mem: Vec::new(),
offset: 0,
size: 0,
}
}
/// Create `ReturnData` from give buffer and slice.
pub fn new(mem: Vec<u8>, offset: usize, size: usize) -> Self {
ReturnData { mem, offset, size }
}
}
/// Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug)]
pub enum GasLeft {
/// Known gas left
Known(U256),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: ReturnData,
/// Apply or revert state changes on revert.
apply_state: bool
},
/// Known gas left
Known(U256),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: ReturnData,
/// Apply or revert state changes on revert.
apply_state: bool,
},
}

View File

@@ -15,379 +15,386 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Cost schedule and other parameterisations for the EVM.
use std::collections::HashMap;
use ethereum_types::U256;
use std::collections::HashMap;
/// Definition of schedules that can be applied to a version.
#[derive(Debug)]
pub enum VersionedSchedule {
PWasm,
PWasm,
}
/// Definition of the cost schedule and other parameterisations for the EVM.
#[derive(Debug)]
pub struct Schedule {
/// Does it support exceptional failed code deposit
pub exceptional_failed_code_deposit: bool,
/// Does it have a delegate cal
pub have_delegate_call: bool,
/// Does it have a CREATE2 instruction
pub have_create2: bool,
/// Does it have a REVERT instruction
pub have_revert: bool,
/// Does it have a EXTCODEHASH instruction
pub have_extcodehash: bool,
/// VM stack limit
pub stack_limit: usize,
/// Max number of nested calls/creates
pub max_depth: usize,
/// Gas prices for instructions in all tiers
pub tier_step_gas: [usize; 8],
/// Gas price for `EXP` opcode
pub exp_gas: usize,
/// Additional gas for `EXP` opcode for each byte of exponent
pub exp_byte_gas: usize,
/// Gas price for `SHA3` opcode
pub sha3_gas: usize,
/// Additional gas for `SHA3` opcode for each word of hashed memory
pub sha3_word_gas: usize,
/// Gas price for loading from storage
pub sload_gas: usize,
/// Gas price for setting new value to storage (`storage==0`, `new!=0`)
pub sstore_set_gas: usize,
/// Gas price for altering value in storage
pub sstore_reset_gas: usize,
/// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`)
pub sstore_refund_gas: usize,
/// Gas price for `JUMPDEST` opcode
pub jumpdest_gas: usize,
/// Gas price for `LOG*`
pub log_gas: usize,
/// Additional gas for data in `LOG*`
pub log_data_gas: usize,
/// Additional gas for each topic in `LOG*`
pub log_topic_gas: usize,
/// Gas price for `CREATE` opcode
pub create_gas: usize,
/// Gas price for `*CALL*` opcodes
pub call_gas: usize,
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
pub call_stipend: usize,
/// Additional gas required for value transfer (`CALL|CALLCODE`)
pub call_value_transfer_gas: usize,
/// Additional gas for creating new account (`CALL|CALLCODE`)
pub call_new_account_gas: usize,
/// Refund for SUICIDE
pub suicide_refund_gas: usize,
/// Gas for used memory
pub memory_gas: usize,
/// Coefficient used to convert memory size to gas price for memory
pub quad_coeff_div: usize,
/// Cost for contract length when executing `CREATE`
pub create_data_gas: usize,
/// Maximum code size when creating a contract.
pub create_data_limit: usize,
/// Transaction cost
pub tx_gas: usize,
/// `CREATE` transaction cost
pub tx_create_gas: usize,
/// Additional cost for empty data transaction
pub tx_data_zero_gas: usize,
/// Additional cost for non-empty data transaction
pub tx_data_non_zero_gas: usize,
/// Gas price for copying memory
pub copy_gas: usize,
/// Price of EXTCODESIZE
pub extcodesize_gas: usize,
/// Base price of EXTCODECOPY
pub extcodecopy_base_gas: usize,
/// Price of BALANCE
pub balance_gas: usize,
/// Price of EXTCODEHASH
pub extcodehash_gas: usize,
/// Price of SUICIDE
pub suicide_gas: usize,
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
pub suicide_to_new_account_cost: usize,
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit.
/// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
pub sub_gas_cap_divisor: Option<usize>,
/// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value.
pub no_empty: bool,
/// Kill empty accounts if touched.
pub kill_empty: bool,
/// Blockhash instruction gas cost.
pub blockhash_gas: usize,
/// Static Call opcode enabled.
pub have_static_call: bool,
/// RETURNDATA and RETURNDATASIZE opcodes enabled.
pub have_return_data: bool,
/// SHL, SHR, SAR opcodes enabled.
pub have_bitwise_shifting: bool,
/// CHAINID opcode enabled.
pub have_chain_id: bool,
/// SELFBALANCE opcode enabled.
pub have_selfbalance: bool,
/// Kill basic accounts below this balance if touched.
pub kill_dust: CleanDustMode,
/// Enable EIP-1283 rules
pub eip1283: bool,
/// Enable EIP-1706 rules
pub eip1706: bool,
/// VM execution does not increase null signed address nonce if this field is true.
pub keep_unsigned_nonce: bool,
/// Wasm extra schedule settings, if wasm activated
pub wasm: Option<WasmCosts>,
/// Does it support exceptional failed code deposit
pub exceptional_failed_code_deposit: bool,
/// Does it have a delegate cal
pub have_delegate_call: bool,
/// Does it have a CREATE2 instruction
pub have_create2: bool,
/// Does it have a REVERT instruction
pub have_revert: bool,
/// Does it have a EXTCODEHASH instruction
pub have_extcodehash: bool,
/// VM stack limit
pub stack_limit: usize,
/// Max number of nested calls/creates
pub max_depth: usize,
/// Gas prices for instructions in all tiers
pub tier_step_gas: [usize; 8],
/// Gas price for `EXP` opcode
pub exp_gas: usize,
/// Additional gas for `EXP` opcode for each byte of exponent
pub exp_byte_gas: usize,
/// Gas price for `SHA3` opcode
pub sha3_gas: usize,
/// Additional gas for `SHA3` opcode for each word of hashed memory
pub sha3_word_gas: usize,
/// Gas price for loading from storage
pub sload_gas: usize,
/// Gas price for setting new value to storage (`storage==0`, `new!=0`)
pub sstore_set_gas: usize,
/// Gas price for altering value in storage
pub sstore_reset_gas: usize,
/// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`)
pub sstore_refund_gas: usize,
/// Gas price for `JUMPDEST` opcode
pub jumpdest_gas: usize,
/// Gas price for `LOG*`
pub log_gas: usize,
/// Additional gas for data in `LOG*`
pub log_data_gas: usize,
/// Additional gas for each topic in `LOG*`
pub log_topic_gas: usize,
/// Gas price for `CREATE` opcode
pub create_gas: usize,
/// Gas price for `*CALL*` opcodes
pub call_gas: usize,
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
pub call_stipend: usize,
/// Additional gas required for value transfer (`CALL|CALLCODE`)
pub call_value_transfer_gas: usize,
/// Additional gas for creating new account (`CALL|CALLCODE`)
pub call_new_account_gas: usize,
/// Refund for SUICIDE
pub suicide_refund_gas: usize,
/// Gas for used memory
pub memory_gas: usize,
/// Coefficient used to convert memory size to gas price for memory
pub quad_coeff_div: usize,
/// Cost for contract length when executing `CREATE`
pub create_data_gas: usize,
/// Maximum code size when creating a contract.
pub create_data_limit: usize,
/// Transaction cost
pub tx_gas: usize,
/// `CREATE` transaction cost
pub tx_create_gas: usize,
/// Additional cost for empty data transaction
pub tx_data_zero_gas: usize,
/// Additional cost for non-empty data transaction
pub tx_data_non_zero_gas: usize,
/// Gas price for copying memory
pub copy_gas: usize,
/// Price of EXTCODESIZE
pub extcodesize_gas: usize,
/// Base price of EXTCODECOPY
pub extcodecopy_base_gas: usize,
/// Price of BALANCE
pub balance_gas: usize,
/// Price of EXTCODEHASH
pub extcodehash_gas: usize,
/// Price of SUICIDE
pub suicide_gas: usize,
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
pub suicide_to_new_account_cost: usize,
/// If Some(x): let limit = GAS * (x - 1) / x; let CALL's gas = min(requested, limit). let CREATE's gas = limit.
/// If None: let CALL's gas = (requested > GAS ? [OOG] : GAS). let CREATE's gas = GAS
pub sub_gas_cap_divisor: Option<usize>,
/// Don't ever make empty accounts; contracts start with nonce=1. Also, don't charge 25k when sending/suicide zero-value.
pub no_empty: bool,
/// Kill empty accounts if touched.
pub kill_empty: bool,
/// Blockhash instruction gas cost.
pub blockhash_gas: usize,
/// Static Call opcode enabled.
pub have_static_call: bool,
/// RETURNDATA and RETURNDATASIZE opcodes enabled.
pub have_return_data: bool,
/// SHL, SHR, SAR opcodes enabled.
pub have_bitwise_shifting: bool,
/// CHAINID opcode enabled.
pub have_chain_id: bool,
/// SELFBALANCE opcode enabled.
pub have_selfbalance: bool,
/// Kill basic accounts below this balance if touched.
pub kill_dust: CleanDustMode,
/// Enable EIP-1283 rules
pub eip1283: bool,
/// Enable EIP-1706 rules
pub eip1706: bool,
/// VM execution does not increase null signed address nonce if this field is true.
pub keep_unsigned_nonce: bool,
/// Wasm extra schedule settings, if wasm activated
pub wasm: Option<WasmCosts>,
}
/// Wasm cost table
#[derive(Debug)]
pub struct WasmCosts {
/// Default opcode cost
pub regular: u32,
/// Div operations multiplier.
pub div: u32,
/// Div operations multiplier.
pub mul: u32,
/// Memory (load/store) operations multiplier.
pub mem: u32,
/// General static query of U256 value from env-info
pub static_u256: u32,
/// General static query of Address value from env-info
pub static_address: u32,
/// Memory stipend. Amount of free memory (in 64kb pages) each contract can use for stack.
pub initial_mem: u32,
/// Grow memory cost, per page (64kb)
pub grow_mem: u32,
/// Memory copy cost, per byte
pub memcpy: u32,
/// Max stack height (native WebAssembly stack limiter)
pub max_stack_height: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_mul: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_div: u32,
/// Whether create2 extern function is activated.
pub have_create2: bool,
/// Whether gasleft extern function is activated.
pub have_gasleft: bool,
/// Default opcode cost
pub regular: u32,
/// Div operations multiplier.
pub div: u32,
/// Div operations multiplier.
pub mul: u32,
/// Memory (load/store) operations multiplier.
pub mem: u32,
/// General static query of U256 value from env-info
pub static_u256: u32,
/// General static query of Address value from env-info
pub static_address: u32,
/// Memory stipend. Amount of free memory (in 64kb pages) each contract can use for stack.
pub initial_mem: u32,
/// Grow memory cost, per page (64kb)
pub grow_mem: u32,
/// Memory copy cost, per byte
pub memcpy: u32,
/// Max stack height (native WebAssembly stack limiter)
pub max_stack_height: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_mul: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_div: u32,
/// Whether create2 extern function is activated.
pub have_create2: bool,
/// Whether gasleft extern function is activated.
pub have_gasleft: bool,
}
impl Default for WasmCosts {
fn default() -> Self {
WasmCosts {
regular: 1,
div: 16,
mul: 4,
mem: 2,
static_u256: 64,
static_address: 40,
initial_mem: 4096,
grow_mem: 8192,
memcpy: 1,
max_stack_height: 64*1024,
opcodes_mul: 3,
opcodes_div: 8,
have_create2: false,
have_gasleft: false,
}
}
fn default() -> Self {
WasmCosts {
regular: 1,
div: 16,
mul: 4,
mem: 2,
static_u256: 64,
static_address: 40,
initial_mem: 4096,
grow_mem: 8192,
memcpy: 1,
max_stack_height: 64 * 1024,
opcodes_mul: 3,
opcodes_div: 8,
have_create2: false,
have_gasleft: false,
}
}
}
/// Dust accounts cleanup mode.
#[derive(Debug, PartialEq, Eq)]
pub enum CleanDustMode {
/// Dust cleanup is disabled.
Off,
/// Basic dust accounts will be removed.
BasicOnly,
/// Basic and contract dust accounts will be removed.
WithCodeAndStorage,
/// Dust cleanup is disabled.
Off,
/// Basic dust accounts will be removed.
BasicOnly,
/// Basic and contract dust accounts will be removed.
WithCodeAndStorage,
}
impl Schedule {
/// Schedule for the Frontier-era of the Ethereum main net.
pub fn new_frontier() -> Schedule {
Self::new(false, false, 21000)
}
/// Schedule for the Frontier-era of the Ethereum main net.
pub fn new_frontier() -> Schedule {
Self::new(false, false, 21000)
}
/// Schedule for the Homestead-era of the Ethereum main net.
pub fn new_homestead() -> Schedule {
Self::new(true, true, 53000)
}
/// Schedule for the Homestead-era of the Ethereum main net.
pub fn new_homestead() -> Schedule {
Self::new(true, true, 53000)
}
/// Schedule for the post-EIP-150-era of the Ethereum main net.
pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule {
Schedule {
exceptional_failed_code_deposit: true,
have_delegate_call: true,
have_create2: false,
have_revert: false,
have_return_data: false,
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10,
exp_byte_gas: if fix_exp {50} else {10},
sha3_gas: 30,
sha3_word_gas: 6,
sload_gas: 200,
sstore_set_gas: 20000,
sstore_reset_gas: 5000,
sstore_refund_gas: 15000,
jumpdest_gas: 1,
log_gas: 375,
log_data_gas: 8,
log_topic_gas: 375,
create_gas: 32000,
call_gas: 700,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
suicide_refund_gas: 24000,
memory_gas: 3,
quad_coeff_div: 512,
create_data_gas: 200,
create_data_limit: max_code_size,
tx_gas: 21000,
tx_create_gas: 53000,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
extcodesize_gas: 700,
extcodecopy_base_gas: 700,
extcodehash_gas: 400,
balance_gas: 400,
suicide_gas: 5000,
suicide_to_new_account_cost: 25000,
sub_gas_cap_divisor: Some(64),
no_empty: no_empty,
kill_empty: kill_empty,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip1283: false,
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
}
}
/// Schedule for the post-EIP-150-era of the Ethereum main net.
pub fn new_post_eip150(
max_code_size: usize,
fix_exp: bool,
no_empty: bool,
kill_empty: bool,
) -> Schedule {
Schedule {
exceptional_failed_code_deposit: true,
have_delegate_call: true,
have_create2: false,
have_revert: false,
have_return_data: false,
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10,
exp_byte_gas: if fix_exp { 50 } else { 10 },
sha3_gas: 30,
sha3_word_gas: 6,
sload_gas: 200,
sstore_set_gas: 20000,
sstore_reset_gas: 5000,
sstore_refund_gas: 15000,
jumpdest_gas: 1,
log_gas: 375,
log_data_gas: 8,
log_topic_gas: 375,
create_gas: 32000,
call_gas: 700,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
suicide_refund_gas: 24000,
memory_gas: 3,
quad_coeff_div: 512,
create_data_gas: 200,
create_data_limit: max_code_size,
tx_gas: 21000,
tx_create_gas: 53000,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
extcodesize_gas: 700,
extcodecopy_base_gas: 700,
extcodehash_gas: 400,
balance_gas: 400,
suicide_gas: 5000,
suicide_to_new_account_cost: 25000,
sub_gas_cap_divisor: Some(64),
no_empty: no_empty,
kill_empty: kill_empty,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip1283: false,
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
}
}
/// Schedule for the Byzantium fork of the Ethereum main net.
pub fn new_byzantium() -> Schedule {
let mut schedule = Self::new_post_eip150(24576, true, true, true);
schedule.have_create2 = true;
schedule.have_revert = true;
schedule.have_static_call = true;
schedule.have_return_data = true;
schedule
}
/// Schedule for the Byzantium fork of the Ethereum main net.
pub fn new_byzantium() -> Schedule {
let mut schedule = Self::new_post_eip150(24576, true, true, true);
schedule.have_create2 = true;
schedule.have_revert = true;
schedule.have_static_call = true;
schedule.have_return_data = true;
schedule
}
/// Schedule for the Constantinople fork of the Ethereum main net.
pub fn new_constantinople() -> Schedule {
let mut schedule = Self::new_byzantium();
schedule.have_bitwise_shifting = true;
schedule
}
/// Schedule for the Constantinople fork of the Ethereum main net.
pub fn new_constantinople() -> Schedule {
let mut schedule = Self::new_byzantium();
schedule.have_bitwise_shifting = true;
schedule
}
/// Schedule for the Istanbul fork of the Ethereum main net.
pub fn new_istanbul() -> Schedule {
let mut schedule = Self::new_constantinople();
schedule.have_chain_id = true; // EIP 1344
schedule.tx_data_non_zero_gas = 16; // EIP 2028
schedule.sload_gas = 800; // EIP 1884
schedule.balance_gas = 700; // EIP 1884
schedule.extcodehash_gas = 700; // EIP 1884
schedule.have_selfbalance = true; // EIP 1884
schedule
}
/// Schedule for the Istanbul fork of the Ethereum main net.
pub fn new_istanbul() -> Schedule {
let mut schedule = Self::new_constantinople();
schedule.have_chain_id = true; // EIP 1344
schedule.tx_data_non_zero_gas = 16; // EIP 2028
schedule.sload_gas = 800; // EIP 1884
schedule.balance_gas = 700; // EIP 1884
schedule.extcodehash_gas = 700; // EIP 1884
schedule.have_selfbalance = true; // EIP 1884
schedule
}
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule {
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
have_create2: false,
have_revert: false,
have_return_data: false,
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10,
exp_byte_gas: 10,
sha3_gas: 30,
sha3_word_gas: 6,
sload_gas: 50,
sstore_set_gas: 20000,
sstore_reset_gas: 5000,
sstore_refund_gas: 15000,
jumpdest_gas: 1,
log_gas: 375,
log_data_gas: 8,
log_topic_gas: 375,
create_gas: 32000,
call_gas: 40,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
suicide_refund_gas: 24000,
memory_gas: 3,
quad_coeff_div: 512,
create_data_gas: 200,
create_data_limit: usize::max_value(),
tx_gas: 21000,
tx_create_gas: tcg,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
extcodesize_gas: 20,
extcodecopy_base_gas: 20,
extcodehash_gas: 400,
balance_gas: 20,
suicide_gas: 0,
suicide_to_new_account_cost: 0,
sub_gas_cap_divisor: None,
no_empty: false,
kill_empty: false,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip1283: false,
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
}
}
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule {
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
have_create2: false,
have_revert: false,
have_return_data: false,
have_bitwise_shifting: false,
have_chain_id: false,
have_selfbalance: false,
have_extcodehash: false,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10,
exp_byte_gas: 10,
sha3_gas: 30,
sha3_word_gas: 6,
sload_gas: 50,
sstore_set_gas: 20000,
sstore_reset_gas: 5000,
sstore_refund_gas: 15000,
jumpdest_gas: 1,
log_gas: 375,
log_data_gas: 8,
log_topic_gas: 375,
create_gas: 32000,
call_gas: 40,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
suicide_refund_gas: 24000,
memory_gas: 3,
quad_coeff_div: 512,
create_data_gas: 200,
create_data_limit: usize::max_value(),
tx_gas: 21000,
tx_create_gas: tcg,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
extcodesize_gas: 20,
extcodecopy_base_gas: 20,
extcodehash_gas: 400,
balance_gas: 20,
suicide_gas: 0,
suicide_to_new_account_cost: 0,
sub_gas_cap_divisor: None,
no_empty: false,
kill_empty: false,
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip1283: false,
eip1706: false,
keep_unsigned_nonce: false,
wasm: None,
}
}
/// Returns wasm schedule
///
/// May panic if there is no wasm schedule
pub fn wasm(&self) -> &WasmCosts {
// *** Prefer PANIC here instead of silently breaking consensus! ***
self.wasm.as_ref().expect("Wasm schedule expected to exist while checking wasm contract. Misconfigured client?")
}
/// Returns wasm schedule
///
/// May panic if there is no wasm schedule
pub fn wasm(&self) -> &WasmCosts {
// *** Prefer PANIC here instead of silently breaking consensus! ***
self.wasm.as_ref().expect(
"Wasm schedule expected to exist while checking wasm contract. Misconfigured client?",
)
}
}
impl Default for Schedule {
fn default() -> Self {
Schedule::new_frontier()
}
fn default() -> Self {
Schedule::new_frontier()
}
}
#[test]
#[cfg(test)]
fn schedule_evm_assumptions() {
let s1 = Schedule::new_frontier();
let s2 = Schedule::new_homestead();
let s1 = Schedule::new_frontier();
let s2 = Schedule::new_homestead();
// To optimize division we assume 2**9 for quad_coeff_div
assert_eq!(s1.quad_coeff_div, 512);
assert_eq!(s2.quad_coeff_div, 512);
// To optimize division we assume 2**9 for quad_coeff_div
assert_eq!(s1.quad_coeff_div, 512);
assert_eq!(s2.quad_coeff_div, 512);
}

View File

@@ -14,39 +14,47 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::collections::{HashMap, HashSet};
use ethereum_types::{U256, H256, Address};
use bytes::Bytes;
use {
CallType, Schedule, EnvInfo,
ReturnData, Ext, ContractCreateResult, MessageCallResult,
CreateContractAddress, Result, GasLeft,
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use hash::keccak;
use bytes::Bytes;
use error::TrapKind;
use ethereum_types::{Address, H256, U256};
use hash::keccak;
use CallType;
use ContractCreateResult;
use CreateContractAddress;
use EnvInfo;
use Ext;
use GasLeft;
use MessageCallResult;
use Result;
use ReturnData;
use Schedule;
pub struct FakeLogEntry {
pub topics: Vec<H256>,
pub data: Bytes
pub topics: Vec<H256>,
pub data: Bytes,
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum FakeCallType {
Call, Create
Call,
Create,
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct FakeCall {
pub call_type: FakeCallType,
pub create_scheme: Option<CreateContractAddress>,
pub gas: U256,
pub sender_address: Option<Address>,
pub receive_address: Option<Address>,
pub value: Option<U256>,
pub data: Bytes,
pub code_address: Option<Address>,
pub call_type: FakeCallType,
pub create_scheme: Option<CreateContractAddress>,
pub gas: U256,
pub sender_address: Option<Address>,
pub receive_address: Option<Address>,
pub value: Option<U256>,
pub data: Bytes,
pub code_address: Option<Address>,
}
/// Fake externalities test structure.
@@ -54,211 +62,211 @@ pub struct FakeCall {
/// Can't do recursive calls.
#[derive(Default)]
pub struct FakeExt {
pub store: HashMap<H256, H256>,
pub suicides: HashSet<Address>,
pub calls: HashSet<FakeCall>,
pub sstore_clears: i128,
pub depth: usize,
pub blockhashes: HashMap<U256, H256>,
pub codes: HashMap<Address, Arc<Bytes>>,
pub logs: Vec<FakeLogEntry>,
pub info: EnvInfo,
pub schedule: Schedule,
pub balances: HashMap<Address, U256>,
pub tracing: bool,
pub is_static: bool,
pub store: HashMap<H256, H256>,
pub suicides: HashSet<Address>,
pub calls: HashSet<FakeCall>,
pub sstore_clears: i128,
pub depth: usize,
pub blockhashes: HashMap<U256, H256>,
pub codes: HashMap<Address, Arc<Bytes>>,
pub logs: Vec<FakeLogEntry>,
pub info: EnvInfo,
pub schedule: Schedule,
pub balances: HashMap<Address, U256>,
pub tracing: bool,
pub is_static: bool,
chain_id: u64,
chain_id: u64,
}
// similar to the normal `finalize` function, but ignoring NeedsReturn.
pub fn test_finalize(res: Result<GasLeft>) -> Result<U256> {
match res {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
Err(e) => Err(e),
}
match res {
Ok(GasLeft::Known(gas)) => Ok(gas),
Ok(GasLeft::NeedsReturn { .. }) => unimplemented!(), // since ret is unimplemented.
Err(e) => Err(e),
}
}
impl FakeExt {
/// New fake externalities
pub fn new() -> Self {
FakeExt::default()
}
/// New fake externalities
pub fn new() -> Self {
FakeExt::default()
}
/// New fake externalities with byzantium schedule rules
pub fn new_byzantium() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_byzantium();
ext
}
/// New fake externalities with byzantium schedule rules
pub fn new_byzantium() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_byzantium();
ext
}
/// New fake externalities with constantinople schedule rules
pub fn new_constantinople() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_constantinople();
ext
}
/// New fake externalities with constantinople schedule rules
pub fn new_constantinople() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_constantinople();
ext
}
/// New fake externalities with Istanbul schedule rules
pub fn new_istanbul() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_istanbul();
ext
}
/// New fake externalities with Istanbul schedule rules
pub fn new_istanbul() -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_istanbul();
ext
}
/// Alter fake externalities to allow wasm
pub fn with_wasm(mut self) -> Self {
self.schedule.wasm = Some(Default::default());
self
}
/// Alter fake externalities to allow wasm
pub fn with_wasm(mut self) -> Self {
self.schedule.wasm = Some(Default::default());
self
}
/// Set chain ID
pub fn with_chain_id(mut self, chain_id: u64) -> Self {
self.chain_id = chain_id;
self
}
/// Set chain ID
pub fn with_chain_id(mut self, chain_id: u64) -> Self {
self.chain_id = chain_id;
self
}
}
impl Ext for FakeExt {
fn initial_storage_at(&self, _key: &H256) -> Result<H256> {
Ok(H256::new())
}
fn initial_storage_at(&self, _key: &H256) -> Result<H256> {
Ok(H256::new())
}
fn storage_at(&self, key: &H256) -> Result<H256> {
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
}
fn storage_at(&self, key: &H256) -> Result<H256> {
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
}
fn set_storage(&mut self, key: H256, value: H256) -> Result<()> {
self.store.insert(key, value);
Ok(())
}
fn set_storage(&mut self, key: H256, value: H256) -> Result<()> {
self.store.insert(key, value);
Ok(())
}
fn exists(&self, address: &Address) -> Result<bool> {
Ok(self.balances.contains_key(address))
}
fn exists(&self, address: &Address) -> Result<bool> {
Ok(self.balances.contains_key(address))
}
fn exists_and_not_null(&self, address: &Address) -> Result<bool> {
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
}
fn exists_and_not_null(&self, address: &Address) -> Result<bool> {
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
}
fn origin_balance(&self) -> Result<U256> {
unimplemented!()
}
fn origin_balance(&self) -> Result<U256> {
unimplemented!()
}
fn balance(&self, address: &Address) -> Result<U256> {
Ok(self.balances[address])
}
fn balance(&self, address: &Address) -> Result<U256> {
Ok(self.balances[address])
}
fn blockhash(&mut self, number: &U256) -> H256 {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}
fn blockhash(&mut self, number: &U256) -> H256 {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}
fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
_trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
self.calls.insert(FakeCall {
call_type: FakeCallType::Create,
create_scheme: Some(address),
gas: *gas,
sender_address: None,
receive_address: None,
value: Some(*value),
data: code.to_vec(),
code_address: None
});
// TODO: support traps in testing.
Ok(ContractCreateResult::Failed)
}
fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
_trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
self.calls.insert(FakeCall {
call_type: FakeCallType::Create,
create_scheme: Some(address),
gas: *gas,
sender_address: None,
receive_address: None,
value: Some(*value),
data: code.to_vec(),
code_address: None,
});
// TODO: support traps in testing.
Ok(ContractCreateResult::Failed)
}
fn call(
&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
_call_type: CallType,
_trap: bool,
) -> ::std::result::Result<MessageCallResult, TrapKind> {
self.calls.insert(FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: *gas,
sender_address: Some(sender_address.clone()),
receive_address: Some(receive_address.clone()),
value: value,
data: data.to_vec(),
code_address: Some(code_address.clone())
});
// TODO: support traps in testing.
Ok(MessageCallResult::Success(*gas, ReturnData::empty()))
}
fn call(
&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
_call_type: CallType,
_trap: bool,
) -> ::std::result::Result<MessageCallResult, TrapKind> {
self.calls.insert(FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: *gas,
sender_address: Some(sender_address.clone()),
receive_address: Some(receive_address.clone()),
value: value,
data: data.to_vec(),
code_address: Some(code_address.clone()),
});
// TODO: support traps in testing.
Ok(MessageCallResult::Success(*gas, ReturnData::empty()))
}
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>> {
Ok(self.codes.get(address).cloned())
}
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>> {
Ok(self.codes.get(address).cloned())
}
fn extcodesize(&self, address: &Address) -> Result<Option<usize>> {
Ok(self.codes.get(address).map(|c| c.len()))
}
fn extcodesize(&self, address: &Address) -> Result<Option<usize>> {
Ok(self.codes.get(address).map(|c| c.len()))
}
fn extcodehash(&self, address: &Address) -> Result<Option<H256>> {
Ok(self.codes.get(address).map(|c| keccak(c.as_ref())))
}
fn extcodehash(&self, address: &Address) -> Result<Option<H256>> {
Ok(self.codes.get(address).map(|c| keccak(c.as_ref())))
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()> {
self.logs.push(FakeLogEntry {
topics,
data: data.to_vec()
});
Ok(())
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()> {
self.logs.push(FakeLogEntry {
topics,
data: data.to_vec(),
});
Ok(())
}
fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> Result<U256> {
unimplemented!();
}
fn ret(self, _gas: &U256, _data: &ReturnData, _apply_state: bool) -> Result<U256> {
unimplemented!();
}
fn suicide(&mut self, refund_address: &Address) -> Result<()> {
self.suicides.insert(refund_address.clone());
Ok(())
}
fn suicide(&mut self, refund_address: &Address) -> Result<()> {
self.suicides.insert(refund_address.clone());
Ok(())
}
fn schedule(&self) -> &Schedule {
&self.schedule
}
fn schedule(&self) -> &Schedule {
&self.schedule
}
fn env_info(&self) -> &EnvInfo {
&self.info
}
fn env_info(&self) -> &EnvInfo {
&self.info
}
fn chain_id(&self) -> u64 {
self.chain_id
}
fn chain_id(&self) -> u64 {
self.chain_id
}
fn depth(&self) -> usize {
self.depth
}
fn depth(&self) -> usize {
self.depth
}
fn is_static(&self) -> bool {
self.is_static
}
fn is_static(&self) -> bool {
self.is_static
}
fn add_sstore_refund(&mut self, value: usize) {
self.sstore_clears += value as i128;
}
fn add_sstore_refund(&mut self, value: usize) {
self.sstore_clears += value as i128;
}
fn sub_sstore_refund(&mut self, value: usize) {
self.sstore_clears -= value as i128;
}
fn sub_sstore_refund(&mut self, value: usize) {
self.sstore_clears -= value as i128;
}
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool {
self.tracing
}
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool {
self.tracing
}
}