Decouple virtual machines (#6184)
* work in progress for splitting vms * evm working * Evm -> Vm * wasm converted * ethcore working * test fixes
This commit is contained in:
@@ -1,121 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Evm input params.
|
||||
use util::{Address, Bytes, U256};
|
||||
use util::hash::{H256};
|
||||
use util::sha3::{Hashable, SHA3_EMPTY};
|
||||
use ethjson;
|
||||
|
||||
use {CallType};
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
impl ActionValue {
|
||||
/// 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 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,
|
||||
|
||||
}
|
||||
|
||||
impl Default for ActionParams {
|
||||
/// Returns default ActionParams initialized with zeros
|
||||
fn default() -> ActionParams {
|
||||
ActionParams {
|
||||
code_address: Address::new(),
|
||||
code_hash: Some(SHA3_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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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((&*t.code).sha3()),
|
||||
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?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ extern crate test;
|
||||
use self::test::{Bencher, black_box};
|
||||
|
||||
use util::*;
|
||||
use evm::action_params::ActionParams;
|
||||
use vm::ActionParams;
|
||||
use evm::{self, Factory, VMType};
|
||||
use evm::tests::FakeExt;
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
//! EVM call types.
|
||||
|
||||
use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp};
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for CallType {
|
||||
fn decode(rlp: &UntrustedRlp) -> 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;
|
||||
|
||||
#[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();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_encode_and_decode_call_type() {
|
||||
let original = CallType::Call;
|
||||
let encoded = encode(&original);
|
||||
let decoded = decode(&encoded);
|
||||
assert_eq!(original, decoded);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Environment information for transaction execution.
|
||||
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use util::{U256, Address, H256, Hashable};
|
||||
use types::BlockNumber;
|
||||
use ethjson;
|
||||
|
||||
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
|
||||
/// for a block whose number is less than 257.
|
||||
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,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::vm::Env> for EnvInfo {
|
||||
fn from(e: ethjson::vm::Env) -> Self {
|
||||
let number = e.number.into();
|
||||
EnvInfo {
|
||||
number: 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| format!("{}", number - i).as_bytes().sha3()).collect()),
|
||||
gas_used: U256::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use super::*;
|
||||
use util::{U256, Address};
|
||||
use ethjson;
|
||||
|
||||
#[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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_be_created_as_default() {
|
||||
let default_env_info = EnvInfo::default();
|
||||
|
||||
assert_eq!(default_env_info.difficulty, 0.into());
|
||||
}
|
||||
}
|
||||
@@ -17,142 +17,8 @@
|
||||
//! Evm interface.
|
||||
|
||||
use std::{ops, cmp, fmt};
|
||||
use util::{U128, U256, U512, trie};
|
||||
use action_params::ActionParams;
|
||||
use {Ext};
|
||||
|
||||
use super::wasm;
|
||||
|
||||
/// Evm 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),
|
||||
}
|
||||
|
||||
impl From<Box<trie::TrieError>> for Error {
|
||||
fn from(err: Box<trie::TrieError>) -> Self {
|
||||
Error::Internal(format!("Internal error: {}", err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wasm::RuntimeError> for Error {
|
||||
fn from(err: wasm::RuntimeError) -> Self {
|
||||
Error::Wasm(format!("Runtime 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A specialized version of Result over EVM errors.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for ReturnData {
|
||||
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: mem,
|
||||
offset: offset,
|
||||
size: 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
|
||||
},
|
||||
}
|
||||
use util::{U128, U256, U512};
|
||||
use vm::{Ext, Result, ReturnData, GasLeft, Error};
|
||||
|
||||
/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
|
||||
/// a return instruction.
|
||||
@@ -281,15 +147,6 @@ impl CostType for usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evm interface
|
||||
pub trait Evm {
|
||||
/// 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(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::U256;
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Interface for Evm externalities.
|
||||
|
||||
use util::*;
|
||||
use call_type::CallType;
|
||||
use env_info::EnvInfo;
|
||||
use schedule::Schedule;
|
||||
use evm::{self, ReturnData};
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// Specifies how an address is calculated for a new contract.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CreateContractAddress {
|
||||
/// Address is calculated from nonce and sender. Pre EIP-86 (Metropolis)
|
||||
FromSenderAndNonce,
|
||||
/// Address is calculated from code hash. Default since EIP-86
|
||||
FromCodeHash,
|
||||
/// Address is calculated from code hash and sender. Used by CREATE_P2SH instruction.
|
||||
FromSenderAndCodeHash,
|
||||
}
|
||||
|
||||
/// Externalities interface for EVMs
|
||||
pub trait Ext {
|
||||
/// Returns a value for given key.
|
||||
fn storage_at(&self, key: &H256) -> evm::Result<H256>;
|
||||
|
||||
/// Stores a value for given key.
|
||||
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()>;
|
||||
|
||||
/// Determine whether an account exists.
|
||||
fn exists(&self, address: &Address) -> evm::Result<bool>;
|
||||
|
||||
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
||||
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool>;
|
||||
|
||||
/// Balance of the origin account.
|
||||
fn origin_balance(&self) -> evm::Result<U256>;
|
||||
|
||||
/// Returns address balance.
|
||||
fn balance(&self, address: &Address) -> evm::Result<U256>;
|
||||
|
||||
/// 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 succesfull.
|
||||
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult;
|
||||
|
||||
/// Message call.
|
||||
///
|
||||
/// Returns Err, if we run out of gas.
|
||||
/// Otherwise returns call_result which contains gas left
|
||||
/// and true if subcall was successfull.
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
fn call(&mut self,
|
||||
gas: &U256,
|
||||
sender_address: &Address,
|
||||
receive_address: &Address,
|
||||
value: Option<U256>,
|
||||
data: &[u8],
|
||||
code_address: &Address,
|
||||
output: &mut [u8],
|
||||
call_type: CallType
|
||||
) -> MessageCallResult;
|
||||
|
||||
/// Returns code at given address
|
||||
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>>;
|
||||
|
||||
/// Returns code size at given address
|
||||
fn extcodesize(&self, address: &Address) -> evm::Result<usize>;
|
||||
|
||||
/// Creates log entry with given topics and data
|
||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::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) -> evm::Result<U256>;
|
||||
|
||||
/// Should be called when contract commits suicide.
|
||||
/// Address to which funds should be refunded.
|
||||
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> ;
|
||||
|
||||
/// Returns schedule.
|
||||
fn schedule(&self) -> &Schedule;
|
||||
|
||||
/// Returns environment info.
|
||||
fn env_info(&self) -> &EnvInfo;
|
||||
|
||||
/// 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 count by 1.
|
||||
fn inc_sstore_clears(&mut self);
|
||||
|
||||
/// Decide if any more operations should be traced. Passthrough for the VM trace.
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
|
||||
|
||||
/// Prepare to trace an operation. Passthrough for the VM trace.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Evm factory.
|
||||
//!
|
||||
use std::sync::Arc;
|
||||
use evm::Evm;
|
||||
use vm::Vm;
|
||||
use util::U256;
|
||||
use super::interpreter::SharedCache;
|
||||
use super::vmtype::VMType;
|
||||
@@ -33,7 +33,7 @@ impl Factory {
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(feature = "jit")]
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Vm> {
|
||||
match self.evm {
|
||||
VMType::Jit => {
|
||||
Box::new(super::jit::JitEvm::default())
|
||||
@@ -49,7 +49,7 @@ impl Factory {
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(not(feature = "jit"))]
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Vm> {
|
||||
match self.evm {
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
use util::*;
|
||||
use super::u256_to_address;
|
||||
|
||||
use {evm, ext};
|
||||
use {evm, vm};
|
||||
use instructions::{self, Instruction, InstructionInfo};
|
||||
use interpreter::stack::Stack;
|
||||
use schedule::Schedule;
|
||||
use vm::Schedule;
|
||||
|
||||
macro_rules! overflowing {
|
||||
($x: expr) => {{
|
||||
let (v, overflow) = $x;
|
||||
if overflow { return Err(evm::Error::OutOfGas); }
|
||||
if overflow { return Err(vm::Error::OutOfGas); }
|
||||
v
|
||||
}}
|
||||
}
|
||||
@@ -59,16 +59,16 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> {
|
||||
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
|
||||
match &self.current_gas < gas_cost {
|
||||
true => Err(evm::Error::OutOfGas),
|
||||
true => Err(vm::Error::OutOfGas),
|
||||
false => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
|
||||
/// and that we `requested` some.
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> evm::Result<Gas> {
|
||||
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> vm::Result<Gas> {
|
||||
// Try converting requested gas to `Gas` (`U256/u64`)
|
||||
// but in EIP150 even if we request more we should never fail from OOG
|
||||
let requested = requested.map(Gas::from_u256);
|
||||
@@ -107,12 +107,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
/// it will be the amount of gas that the current context provides to the child context.
|
||||
pub fn requirements(
|
||||
&mut self,
|
||||
ext: &ext::Ext,
|
||||
ext: &vm::Ext,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
stack: &Stack<U256>,
|
||||
current_mem_size: usize,
|
||||
) -> evm::Result<InstructionRequirements<Gas>> {
|
||||
) -> vm::Result<InstructionRequirements<Gas>> {
|
||||
let schedule = ext.schedule();
|
||||
let tier = instructions::get_tier_idx(info.tier);
|
||||
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
|
||||
@@ -291,7 +291,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
})
|
||||
}
|
||||
|
||||
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
|
||||
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> vm::Result<(Gas, Gas, usize)> {
|
||||
let gas_for_mem = |mem_size: Gas| {
|
||||
let s = mem_size >> 5;
|
||||
// s * memory_gas + s * s / quad_coeff_div
|
||||
@@ -319,12 +319,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
|
||||
|
||||
#[inline]
|
||||
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
|
||||
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> vm::Result<Gas> {
|
||||
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
|
||||
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> vm::Result<Gas> {
|
||||
if size.is_zero() {
|
||||
return Ok(Gas::from(0));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::U256;
|
||||
use {ReturnData};
|
||||
use vm::ReturnData;
|
||||
|
||||
const MAX_RETURN_WASTE_BYTES: usize = 16384;
|
||||
|
||||
|
||||
@@ -23,17 +23,21 @@ mod stack;
|
||||
mod memory;
|
||||
mod shared_cache;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use vm::{
|
||||
self, ActionParams, ActionValue, CallType, MessageCallResult,
|
||||
ContractCreateResult, CreateContractAddress, ReturnData, GasLeft
|
||||
};
|
||||
|
||||
use evm::CostType;
|
||||
use instructions::{self, Instruction, InstructionInfo};
|
||||
|
||||
use self::gasometer::Gasometer;
|
||||
use self::stack::{Stack, VecStack};
|
||||
use self::memory::Memory;
|
||||
pub use self::shared_cache::SharedCache;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use call_type::CallType;
|
||||
use instructions::{self, Instruction, InstructionInfo};
|
||||
use evm::{self, GasLeft, CostType, ReturnData};
|
||||
use ext::{self, MessageCallResult, ContractCreateResult, CreateContractAddress};
|
||||
use bit_set::BitSet;
|
||||
|
||||
use util::*;
|
||||
@@ -107,8 +111,8 @@ pub struct Interpreter<Cost: CostType> {
|
||||
_type: PhantomData<Cost>,
|
||||
}
|
||||
|
||||
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut ext::Ext) -> evm::Result<GasLeft> {
|
||||
impl<Cost: CostType> vm::Vm for Interpreter<Cost> {
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
|
||||
self.mem.clear();
|
||||
|
||||
let mut informant = informant::EvmInformant::new(ext.depth());
|
||||
@@ -205,7 +209,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_instruction(&self, ext: &ext::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
|
||||
fn verify_instruction(&self, ext: &vm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> vm::Result<()> {
|
||||
let schedule = ext.schedule();
|
||||
|
||||
if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) ||
|
||||
@@ -214,25 +218,25 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
|
||||
(instruction == instructions::REVERT && !schedule.have_revert) {
|
||||
|
||||
return Err(evm::Error::BadInstruction {
|
||||
return Err(vm::Error::BadInstruction {
|
||||
instruction: instruction
|
||||
});
|
||||
}
|
||||
|
||||
if info.tier == instructions::GasPriceTier::Invalid {
|
||||
return Err(evm::Error::BadInstruction {
|
||||
return Err(vm::Error::BadInstruction {
|
||||
instruction: instruction
|
||||
});
|
||||
}
|
||||
|
||||
if !stack.has(info.args) {
|
||||
Err(evm::Error::StackUnderflow {
|
||||
Err(vm::Error::StackUnderflow {
|
||||
instruction: info.name,
|
||||
wanted: info.args,
|
||||
on_stack: stack.size()
|
||||
})
|
||||
} else if stack.size() - info.args + info.ret > schedule.stack_limit {
|
||||
Err(evm::Error::OutOfStack {
|
||||
Err(vm::Error::OutOfStack {
|
||||
instruction: info.name,
|
||||
wanted: info.ret - info.args,
|
||||
limit: schedule.stack_limit
|
||||
@@ -272,12 +276,12 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
&mut self,
|
||||
gas: Cost,
|
||||
params: &ActionParams,
|
||||
ext: &mut ext::Ext,
|
||||
ext: &mut vm::Ext,
|
||||
instruction: Instruction,
|
||||
code: &mut CodeReader,
|
||||
stack: &mut Stack<U256>,
|
||||
provided: Option<Cost>
|
||||
) -> evm::Result<InstructionResult<Cost>> {
|
||||
) -> vm::Result<InstructionResult<Cost>> {
|
||||
match instruction {
|
||||
instructions::JUMP => {
|
||||
let jump = stack.pop_back();
|
||||
@@ -593,13 +597,13 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> evm::Result<usize> {
|
||||
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> vm::Result<usize> {
|
||||
let jump = jump_u.low_u64() as usize;
|
||||
|
||||
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
|
||||
Ok(jump)
|
||||
} else {
|
||||
Err(evm::Error::BadJumpDestination {
|
||||
Err(vm::Error::BadJumpDestination {
|
||||
destination: jump
|
||||
})
|
||||
}
|
||||
@@ -617,7 +621,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack<U256>) -> evm::Result<()> {
|
||||
fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack<U256>) -> vm::Result<()> {
|
||||
match instruction {
|
||||
instructions::DUP1...instructions::DUP16 => {
|
||||
let position = instructions::get_dup_position(instruction);
|
||||
@@ -822,7 +826,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(evm::Error::BadInstruction {
|
||||
return Err(vm::Error::BadInstruction {
|
||||
instruction: instruction
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use util::*;
|
||||
use evmjit;
|
||||
use evm::{self, GasLeft};
|
||||
use evm::CallType;
|
||||
use vm::{self, Vm};
|
||||
|
||||
/// Should be used to convert jit types to ethcore
|
||||
trait FromJit<T>: Sized {
|
||||
@@ -318,7 +319,7 @@ pub struct JitEvm {
|
||||
context: Option<evmjit::ContextHandle>,
|
||||
}
|
||||
|
||||
impl evm::Evm for JitEvm {
|
||||
impl vm::Vm for JitEvm {
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
|
||||
// Dirty hack. This is unsafe, but we interact with ffi, so it's justified.
|
||||
let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) };
|
||||
@@ -370,8 +371,8 @@ impl evm::Evm for JitEvm {
|
||||
ext.suicide(&Address::from_jit(&context.suicide_refund_address()));
|
||||
Ok(GasLeft::Known(U256::from(context.gas_left())))
|
||||
},
|
||||
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
|
||||
_err => Err(evm::Error::Internal)
|
||||
evmjit::ReturnCode::OutOfGas => Err(vm::Error::OutOfGas),
|
||||
_err => Err(vm::Error::Internal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ extern crate ethjson;
|
||||
extern crate rlp;
|
||||
extern crate parity_wasm;
|
||||
extern crate wasm_utils;
|
||||
extern crate vm;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(feature = "jit")]
|
||||
@@ -37,14 +37,8 @@ extern crate evmjit;
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
|
||||
pub mod action_params;
|
||||
pub mod call_type;
|
||||
pub mod env_info;
|
||||
pub mod ext;
|
||||
pub mod evm;
|
||||
pub mod interpreter;
|
||||
pub mod schedule;
|
||||
pub mod wasm;
|
||||
|
||||
#[macro_use]
|
||||
pub mod factory;
|
||||
@@ -59,12 +53,12 @@ mod tests;
|
||||
#[cfg(all(feature="benches", test))]
|
||||
mod benches;
|
||||
|
||||
pub use self::action_params::ActionParams;
|
||||
pub use self::call_type::CallType;
|
||||
pub use self::env_info::EnvInfo;
|
||||
pub use self::evm::{Evm, Error, Finalize, FinalizationResult, GasLeft, Result, CostType, ReturnData};
|
||||
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
|
||||
pub use vm::{
|
||||
Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext,
|
||||
ContractCreateResult, MessageCallResult, CreateContractAddress,
|
||||
GasLeft, ReturnData
|
||||
};
|
||||
pub use self::evm::{Finalize, FinalizationResult, CostType};
|
||||
pub use self::instructions::{InstructionInfo, INSTRUCTIONS, push_bytes};
|
||||
pub use self::vmtype::VMType;
|
||||
pub use self::factory::Factory;
|
||||
pub use self::schedule::{Schedule, CleanDustMode};
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Cost schedule and other parameterisations for the EVM.
|
||||
|
||||
/// Definition of the cost schedule and other parameterisations for the EVM.
|
||||
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 CREATE_P2SH instruction
|
||||
pub have_create2: bool,
|
||||
/// Does it have a REVERT instruction
|
||||
pub have_revert: 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,
|
||||
/// Aditional 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 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,
|
||||
/// Kill basic accounts below this balance if touched.
|
||||
pub kill_dust: CleanDustMode,
|
||||
}
|
||||
|
||||
/// Dust accounts cleanup mode.
|
||||
#[derive(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,
|
||||
}
|
||||
|
||||
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 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,
|
||||
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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule for the Metropolis of the Ethereum main net.
|
||||
pub fn new_metropolis() -> 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.blockhash_gas = 350;
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn schedule_evm_assumptions() {
|
||||
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);
|
||||
}
|
||||
@@ -17,12 +17,12 @@
|
||||
use std::fmt::Debug;
|
||||
use rustc_hex::FromHex;
|
||||
use util::*;
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use env_info::EnvInfo;
|
||||
use call_type::CallType;
|
||||
use schedule::Schedule;
|
||||
use evm::{self, GasLeft, ReturnData};
|
||||
use ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
|
||||
use evm::{self, GasLeft};
|
||||
use vm::{
|
||||
self, CallType, Schedule, EnvInfo, ActionParams, ActionValue,
|
||||
ReturnData, Ext, ContractCreateResult, MessageCallResult,
|
||||
CreateContractAddress,
|
||||
};
|
||||
use factory::Factory;
|
||||
use vmtype::VMType;
|
||||
|
||||
@@ -66,7 +66,7 @@ pub struct FakeExt {
|
||||
}
|
||||
|
||||
// similar to the normal `finalize` function, but ignoring NeedsReturn.
|
||||
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
|
||||
fn test_finalize(res: Result<GasLeft, vm::Error>) -> Result<U256, vm::Error> {
|
||||
match res {
|
||||
Ok(GasLeft::Known(gas)) => Ok(gas),
|
||||
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
|
||||
@@ -80,35 +80,29 @@ impl FakeExt {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Schedule {
|
||||
fn default() -> Self {
|
||||
Schedule::new_frontier()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ext for FakeExt {
|
||||
fn storage_at(&self, key: &H256) -> evm::Result<H256> {
|
||||
fn storage_at(&self, key: &H256) -> vm::Result<H256> {
|
||||
Ok(self.store.get(key).unwrap_or(&H256::new()).clone())
|
||||
}
|
||||
|
||||
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()> {
|
||||
fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> {
|
||||
self.store.insert(key, value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exists(&self, address: &Address) -> evm::Result<bool> {
|
||||
fn exists(&self, address: &Address) -> vm::Result<bool> {
|
||||
Ok(self.balances.contains_key(address))
|
||||
}
|
||||
|
||||
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool> {
|
||||
fn exists_and_not_null(&self, address: &Address) -> vm::Result<bool> {
|
||||
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
|
||||
}
|
||||
|
||||
fn origin_balance(&self) -> evm::Result<U256> {
|
||||
fn origin_balance(&self) -> vm::Result<U256> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address) -> evm::Result<U256> {
|
||||
fn balance(&self, address: &Address) -> vm::Result<U256> {
|
||||
Ok(self.balances[address])
|
||||
}
|
||||
|
||||
@@ -152,15 +146,15 @@ impl Ext for FakeExt {
|
||||
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||
}
|
||||
|
||||
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>> {
|
||||
fn extcode(&self, address: &Address) -> vm::Result<Arc<Bytes>> {
|
||||
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
|
||||
}
|
||||
|
||||
fn extcodesize(&self, address: &Address) -> evm::Result<usize> {
|
||||
fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
|
||||
Ok(self.codes.get(address).map_or(0, |c| c.len()))
|
||||
}
|
||||
|
||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()> {
|
||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
|
||||
self.logs.push(FakeLogEntry {
|
||||
topics: topics,
|
||||
data: data.to_vec()
|
||||
@@ -168,11 +162,11 @@ impl Ext for FakeExt {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
|
||||
fn ret(self, _gas: &U256, _data: &ReturnData) -> vm::Result<U256> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> {
|
||||
fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> {
|
||||
self.suicides.insert(refund_address.clone());
|
||||
Ok(())
|
||||
}
|
||||
@@ -211,7 +205,7 @@ fn test_stack_underflow() {
|
||||
};
|
||||
|
||||
match err {
|
||||
evm::Error::StackUnderflow {wanted, on_stack, ..} => {
|
||||
vm::Error::StackUnderflow {wanted, on_stack, ..} => {
|
||||
assert_eq!(wanted, 2);
|
||||
assert_eq!(on_stack, 0);
|
||||
}
|
||||
@@ -849,7 +843,7 @@ fn test_badinstruction_int() {
|
||||
};
|
||||
|
||||
match err {
|
||||
evm::Error::BadInstruction { instruction: 0xaf } => (),
|
||||
vm::Error::BadInstruction { instruction: 0xaf } => (),
|
||||
_ => assert!(false, "Expected bad instruction")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm evm call arguments helper
|
||||
|
||||
use util::{U256, H160};
|
||||
|
||||
/// Input part of the wasm call descriptor
|
||||
pub struct CallArgs {
|
||||
/// Receiver of the transaction
|
||||
pub address: [u8; 20],
|
||||
|
||||
/// Sender of the transaction
|
||||
pub sender: [u8; 20],
|
||||
|
||||
/// Original transaction initiator
|
||||
pub origin: [u8; 20],
|
||||
|
||||
/// Transfer value
|
||||
pub value: [u8; 32],
|
||||
|
||||
/// call/create params
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CallArgs {
|
||||
/// New contract call payload with known parameters
|
||||
pub fn new(address: H160, sender: H160, origin: H160, value: U256, data: Vec<u8>) -> Self {
|
||||
let mut descriptor = CallArgs {
|
||||
address: [0u8; 20],
|
||||
sender: [0u8; 20],
|
||||
origin: [0u8; 20],
|
||||
value: [0u8; 32],
|
||||
data: data,
|
||||
};
|
||||
|
||||
descriptor.address.copy_from_slice(&*address);
|
||||
descriptor.sender.copy_from_slice(&*sender);
|
||||
descriptor.origin.copy_from_slice(&*origin);
|
||||
value.to_big_endian(&mut descriptor.value);
|
||||
|
||||
descriptor
|
||||
}
|
||||
|
||||
/// Total call payload length in linear memory
|
||||
pub fn len(&self) -> u32 {
|
||||
self.data.len() as u32 + 92
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm env module bindings
|
||||
|
||||
use parity_wasm::elements::ValueType::*;
|
||||
use parity_wasm::interpreter::UserFunctionDescriptor;
|
||||
use parity_wasm::interpreter::UserFunctionDescriptor::*;
|
||||
|
||||
pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
||||
Static(
|
||||
"_storage_read",
|
||||
&[I32; 2],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"_storage_write",
|
||||
&[I32; 2],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"_malloc",
|
||||
&[I32],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"_free",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"gas",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_debug",
|
||||
&[I32; 2],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_suicide",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_create",
|
||||
&[I32; 4],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"abort",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_abort",
|
||||
&[],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"invoke_vii",
|
||||
&[I32; 3],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"invoke_vi",
|
||||
&[I32; 2],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"invoke_v",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"invoke_iii",
|
||||
&[I32; 3],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"___resumeException",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_rust_begin_unwind",
|
||||
&[I32; 4],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"___cxa_find_matching_catch_2",
|
||||
&[],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"___gxx_personality_v0",
|
||||
&[I32; 6],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"_emscripten_memcpy_big",
|
||||
&[I32; 3],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"___syscall140",
|
||||
&[I32; 2],
|
||||
Some(I32)
|
||||
),
|
||||
Static(
|
||||
"___syscall146",
|
||||
&[I32; 2],
|
||||
Some(I32)
|
||||
),
|
||||
Static(
|
||||
"___syscall54",
|
||||
&[I32; 2],
|
||||
Some(I32)
|
||||
),
|
||||
Static(
|
||||
"___syscall6",
|
||||
&[I32; 2],
|
||||
Some(I32)
|
||||
),
|
||||
Static(
|
||||
"_llvm_trap",
|
||||
&[I32; 0],
|
||||
None
|
||||
),
|
||||
Static(
|
||||
"abortOnCannotGrowMemory",
|
||||
&[I32; 0],
|
||||
Some(I32)
|
||||
),
|
||||
Static(
|
||||
"___setErrNo",
|
||||
&[I32; 1],
|
||||
None
|
||||
),
|
||||
];
|
||||
@@ -1,159 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm Interpreter
|
||||
|
||||
mod runtime;
|
||||
mod ptr;
|
||||
mod call_args;
|
||||
mod result;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod env;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;
|
||||
|
||||
use parity_wasm::{interpreter, elements};
|
||||
use parity_wasm::interpreter::ModuleInstanceInterface;
|
||||
use wasm_utils;
|
||||
|
||||
use evm::{self, GasLeft, ReturnData};
|
||||
use action_params::ActionParams;
|
||||
use self::runtime::Runtime;
|
||||
|
||||
pub use self::runtime::Error as RuntimeError;
|
||||
|
||||
const DEFAULT_RESULT_BUFFER: usize = 1024;
|
||||
|
||||
/// Wasm interpreter instance
|
||||
pub struct WasmInterpreter {
|
||||
program: interpreter::ProgramInstance,
|
||||
result: Vec<u8>,
|
||||
}
|
||||
|
||||
impl WasmInterpreter {
|
||||
/// New wasm interpreter instance
|
||||
pub fn new() -> Result<WasmInterpreter, RuntimeError> {
|
||||
Ok(WasmInterpreter {
|
||||
program: interpreter::ProgramInstance::new()?,
|
||||
result: Vec::with_capacity(DEFAULT_RESULT_BUFFER),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl evm::Evm for WasmInterpreter {
|
||||
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut ::ext::Ext) -> evm::Result<GasLeft> {
|
||||
use parity_wasm::elements::Deserialize;
|
||||
|
||||
let code = params.code.expect("exec is only called on contract with code; qed");
|
||||
|
||||
trace!(target: "wasm", "Started wasm interpreter with code.len={:?}", code.len());
|
||||
|
||||
let env_instance = self.program.module("env")
|
||||
// prefer explicit panic here
|
||||
.expect("Wasm program to contain env module");
|
||||
|
||||
let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0))
|
||||
// prefer explicit panic here
|
||||
.expect("Linear memory to exist in wasm runtime");
|
||||
|
||||
if params.gas > ::std::u64::MAX.into() {
|
||||
return Err(evm::Error::Wasm("Wasm interpreter cannot run contracts with gas >= 2^64".to_owned()));
|
||||
}
|
||||
|
||||
let mut runtime = Runtime::with_params(
|
||||
ext,
|
||||
env_memory,
|
||||
DEFAULT_STACK_SPACE,
|
||||
params.gas.low_u64(),
|
||||
);
|
||||
|
||||
let mut cursor = ::std::io::Cursor::new(&*code);
|
||||
|
||||
let contract_module = wasm_utils::inject_gas_counter(
|
||||
elements::Module::deserialize(
|
||||
&mut cursor
|
||||
).map_err(|err| {
|
||||
evm::Error::Wasm(format!("Error deserializing contract code ({:?})", err))
|
||||
})?
|
||||
);
|
||||
|
||||
let d_ptr = runtime.write_descriptor(
|
||||
call_args::CallArgs::new(
|
||||
params.address,
|
||||
params.sender,
|
||||
params.origin,
|
||||
params.value.value(),
|
||||
params.data.unwrap_or(Vec::with_capacity(0)),
|
||||
)
|
||||
)?;
|
||||
|
||||
{
|
||||
let execution_params = interpreter::ExecutionParams::with_external(
|
||||
"env".into(),
|
||||
Arc::new(
|
||||
interpreter::env_native_module(env_instance, native_bindings(&mut runtime))
|
||||
.map_err(|err| {
|
||||
// todo: prefer explicit panic here also?
|
||||
evm::Error::Wasm(format!("Error instantiating native bindings: {:?}", err))
|
||||
})?
|
||||
)
|
||||
).add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32));
|
||||
|
||||
let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals))
|
||||
.map_err(|err| {
|
||||
trace!(target: "wasm", "Error adding contract module: {:?}", err);
|
||||
evm::Error::from(RuntimeError::Interpreter(err))
|
||||
})?;
|
||||
|
||||
module_instance.execute_export("_call", execution_params)
|
||||
.map_err(|err| {
|
||||
trace!(target: "wasm", "Error executing contract: {:?}", err);
|
||||
evm::Error::from(RuntimeError::Interpreter(err))
|
||||
})?;
|
||||
}
|
||||
|
||||
let result = result::WasmResult::new(d_ptr);
|
||||
if result.peek_empty(&*runtime.memory())? {
|
||||
trace!(target: "wasm", "Contract execution result is empty.");
|
||||
Ok(GasLeft::Known(runtime.gas_left()?.into()))
|
||||
} else {
|
||||
self.result.clear();
|
||||
// todo: use memory views to avoid copy
|
||||
self.result.extend(result.pop(&*runtime.memory())?);
|
||||
let len = self.result.len();
|
||||
Ok(GasLeft::NeedsReturn {
|
||||
gas_left: runtime.gas_left()?.into(),
|
||||
data: ReturnData::new(
|
||||
::std::mem::replace(&mut self.result, Vec::with_capacity(DEFAULT_RESULT_BUFFER)),
|
||||
0,
|
||||
len,
|
||||
),
|
||||
apply_state: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> {
|
||||
interpreter::UserFunctions {
|
||||
executor: runtime,
|
||||
functions: ::std::borrow::Cow::from(env::SIGNATURES),
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm bound-checked ptr
|
||||
|
||||
use parity_wasm::interpreter;
|
||||
|
||||
/// Bound-checked wrapper for webassembly memory
|
||||
pub struct WasmPtr(u32);
|
||||
|
||||
/// Error in bound check
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
AccessViolation,
|
||||
}
|
||||
|
||||
impl From<u32> for WasmPtr {
|
||||
fn from(raw: u32) -> Self {
|
||||
WasmPtr(raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmPtr {
|
||||
// todo: use memory view when they are on
|
||||
/// Check memory range and return data with given length starting from the current pointer value
|
||||
pub fn slice(&self, len: u32, mem: &interpreter::MemoryInstance) -> Result<Vec<u8>, Error> {
|
||||
mem.get(self.0, len as usize).map_err(|_| Error::AccessViolation)
|
||||
}
|
||||
|
||||
// todo: maybe 2gb limit can be enhanced
|
||||
/// Convert i32 from wasm stack to the wrapped pointer
|
||||
pub fn from_i32(raw_ptr: i32) -> Result<Self, Error> {
|
||||
if raw_ptr < 0 { return Err(Error::AccessViolation); }
|
||||
Ok(WasmPtr(raw_ptr as u32))
|
||||
}
|
||||
|
||||
/// Return pointer raw value
|
||||
pub fn as_raw(&self) -> u32 { self.0 }
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm evm results helper
|
||||
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
use parity_wasm::interpreter;
|
||||
|
||||
use super::ptr::WasmPtr;
|
||||
use super::runtime::Error as RuntimeError;
|
||||
|
||||
/// Wrapper for wasm contract call result
|
||||
pub struct WasmResult {
|
||||
ptr: WasmPtr,
|
||||
}
|
||||
|
||||
impl WasmResult {
|
||||
/// New call result from given ptr
|
||||
pub fn new(descriptor_ptr: WasmPtr) -> WasmResult {
|
||||
WasmResult { ptr: descriptor_ptr }
|
||||
}
|
||||
|
||||
/// Check if the result contains any data
|
||||
pub fn peek_empty(&self, mem: &interpreter::MemoryInstance) -> Result<bool, RuntimeError> {
|
||||
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
|
||||
Ok(result_len == 0)
|
||||
}
|
||||
|
||||
/// Consume the result ptr and return the actual data from wasm linear memory
|
||||
pub fn pop(self, mem: &interpreter::MemoryInstance) -> Result<Vec<u8>, RuntimeError> {
|
||||
let result_ptr = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[8..12]);
|
||||
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
|
||||
trace!(target: "wasm", "contract result: {} bytes at @{}", result_len, result_ptr);
|
||||
|
||||
Ok(mem.get(result_ptr, result_len as usize)?)
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm evm program runtime intstance
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
use ext;
|
||||
|
||||
use parity_wasm::interpreter;
|
||||
use util::{Address, H256, U256};
|
||||
|
||||
use super::ptr::{WasmPtr, Error as PtrError};
|
||||
use super::call_args::CallArgs;
|
||||
|
||||
/// Wasm runtime error
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Storage error
|
||||
Storage,
|
||||
/// Allocator error
|
||||
Allocator,
|
||||
/// Invalid gas state during the call
|
||||
InvalidGasState,
|
||||
/// Memory access violation
|
||||
AccessViolation,
|
||||
/// Interpreter runtime error
|
||||
Interpreter(interpreter::Error),
|
||||
}
|
||||
|
||||
impl From<interpreter::Error> for Error {
|
||||
fn from(err: interpreter::Error) -> Self {
|
||||
Error::Interpreter(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PtrError> for Error {
|
||||
fn from(err: PtrError) -> Self {
|
||||
match err {
|
||||
PtrError::AccessViolation => Error::AccessViolation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime enviroment data for wasm contract execution
|
||||
pub struct Runtime<'a> {
|
||||
gas_counter: u64,
|
||||
gas_limit: u64,
|
||||
dynamic_top: u32,
|
||||
ext: &'a mut ext::Ext,
|
||||
memory: Arc<interpreter::MemoryInstance>,
|
||||
}
|
||||
|
||||
impl<'a> Runtime<'a> {
|
||||
/// New runtime for wasm contract with specified params
|
||||
pub fn with_params<'b>(
|
||||
ext: &'b mut ext::Ext,
|
||||
memory: Arc<interpreter::MemoryInstance>,
|
||||
stack_space: u32,
|
||||
gas_limit: u64,
|
||||
) -> Runtime<'b> {
|
||||
Runtime {
|
||||
gas_counter: 0,
|
||||
gas_limit: gas_limit,
|
||||
dynamic_top: stack_space,
|
||||
memory: memory,
|
||||
ext: ext,
|
||||
}
|
||||
}
|
||||
|
||||
/// Write to the storage from wasm memory
|
||||
pub fn storage_write(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let mut context = context;
|
||||
let val = self.pop_h256(&mut context)?;
|
||||
let key = self.pop_h256(&mut context)?;
|
||||
trace!(target: "wasm", "storage_write: value {} at @{}", &val, &key);
|
||||
|
||||
self.ext.set_storage(key, val)
|
||||
.map_err(|_| interpreter::Error::Trap("Storage update error".to_owned()))?;
|
||||
|
||||
Ok(Some(0i32.into()))
|
||||
}
|
||||
|
||||
/// Read from the storage to wasm memory
|
||||
pub fn storage_read(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let mut context = context;
|
||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
let key = self.pop_h256(&mut context)?;
|
||||
|
||||
let val = self.ext.storage_at(&key)
|
||||
.map_err(|_| interpreter::Error::Trap("Storage read error".to_owned()))?;
|
||||
|
||||
self.memory.set(val_ptr as u32, &*val)?;
|
||||
|
||||
Ok(Some(0.into()))
|
||||
}
|
||||
|
||||
/// Pass suicide to state runtime
|
||||
pub fn suicide(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let mut context = context;
|
||||
let refund_address = self.pop_address(&mut context)?;
|
||||
|
||||
self.ext.suicide(&refund_address)
|
||||
.map_err(|_| interpreter::Error::Trap("Suicide error".to_owned()))?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Invoke create in the state runtime
|
||||
pub fn create(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
//
|
||||
// method signature:
|
||||
// fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
|
||||
//
|
||||
|
||||
trace!(target: "wasm", "runtime: create contract");
|
||||
let mut context = context;
|
||||
let result_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
|
||||
let code_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||
trace!(target: "wasm", " code_len: {:?}", code_len);
|
||||
let code_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
|
||||
let endowment = self.pop_u256(&mut context)?;
|
||||
trace!(target: "wasm", " val: {:?}", endowment);
|
||||
|
||||
let code = self.memory.get(code_ptr, code_len as usize)?;
|
||||
|
||||
let gas_left = self.gas_left()
|
||||
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?
|
||||
.into();
|
||||
|
||||
match self.ext.create(&gas_left, &endowment, &code, ext::CreateContractAddress::FromSenderAndCodeHash) {
|
||||
ext::ContractCreateResult::Created(address, gas_left) => {
|
||||
self.memory.set(result_ptr, &*address)?;
|
||||
self.gas_counter = self.gas_limit - gas_left.low_u64();
|
||||
trace!(target: "wasm", "runtime: create contract success (@{:?})", address);
|
||||
Ok(Some(0i32.into()))
|
||||
},
|
||||
ext::ContractCreateResult::Failed => {
|
||||
trace!(target: "wasm", "runtime: create contract fail");
|
||||
Ok(Some((-1i32).into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate memory using the wasm stack params
|
||||
pub fn malloc(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let amount = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let previous_top = self.dynamic_top;
|
||||
self.dynamic_top = previous_top + amount;
|
||||
Ok(Some((previous_top as i32).into()))
|
||||
}
|
||||
|
||||
/// Allocate memory in wasm memory instance
|
||||
pub fn alloc(&mut self, amount: u32) -> Result<u32, Error> {
|
||||
let previous_top = self.dynamic_top;
|
||||
self.dynamic_top = previous_top + amount;
|
||||
Ok(previous_top.into())
|
||||
}
|
||||
|
||||
/// Report gas cost with the params passed in wasm stack
|
||||
fn gas(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let amount = context.value_stack.pop_as::<i32>()? as u64;
|
||||
if self.charge_gas(amount) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.gas_limit)))
|
||||
}
|
||||
}
|
||||
|
||||
fn charge_gas(&mut self, amount: u64) -> bool {
|
||||
let prev = self.gas_counter;
|
||||
if prev + amount > self.gas_limit {
|
||||
// exceeds gas
|
||||
false
|
||||
} else {
|
||||
self.gas_counter = prev + amount;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn h256_at(&self, ptr: WasmPtr) -> Result<H256, interpreter::Error> {
|
||||
Ok(H256::from_slice(&ptr.slice(32, &*self.memory)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?
|
||||
))
|
||||
}
|
||||
|
||||
fn pop_h256(&self, context: &mut interpreter::CallerContext) -> Result<H256, interpreter::Error> {
|
||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
self.h256_at(ptr)
|
||||
}
|
||||
|
||||
fn pop_u256(&self, context: &mut interpreter::CallerContext) -> Result<U256, interpreter::Error> {
|
||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
self.h256_at(ptr).map(Into::into)
|
||||
}
|
||||
|
||||
fn address_at(&self, ptr: WasmPtr) -> Result<Address, interpreter::Error> {
|
||||
Ok(Address::from_slice(&ptr.slice(20, &*self.memory)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?
|
||||
))
|
||||
}
|
||||
|
||||
fn pop_address(&self, context: &mut interpreter::CallerContext) -> Result<Address, interpreter::Error> {
|
||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
self.address_at(ptr)
|
||||
}
|
||||
|
||||
fn user_trap(&mut self, _context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
Err(interpreter::Error::Trap("unknown trap".to_owned()))
|
||||
}
|
||||
|
||||
fn user_noop(&mut self,
|
||||
_context: interpreter::CallerContext
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Write call descriptor to wasm memory
|
||||
pub fn write_descriptor(&mut self, call_args: CallArgs) -> Result<WasmPtr, Error> {
|
||||
let d_ptr = self.alloc(16)?;
|
||||
|
||||
let args_len = call_args.len();
|
||||
let args_ptr = self.alloc(args_len)?;
|
||||
|
||||
// write call descriptor
|
||||
// call descriptor is [args_ptr, args_len, return_ptr, return_len]
|
||||
// all are 4 byte length, last 2 are zeroed
|
||||
let mut d_buf = [0u8; 16];
|
||||
LittleEndian::write_u32(&mut d_buf[0..4], args_ptr);
|
||||
LittleEndian::write_u32(&mut d_buf[4..8], args_len);
|
||||
self.memory.set(d_ptr, &d_buf)?;
|
||||
|
||||
// write call args to memory
|
||||
self.memory.set(args_ptr, &call_args.address)?;
|
||||
self.memory.set(args_ptr+20, &call_args.sender)?;
|
||||
self.memory.set(args_ptr+40, &call_args.origin)?;
|
||||
self.memory.set(args_ptr+60, &call_args.value)?;
|
||||
self.memory.set(args_ptr+92, &call_args.data)?;
|
||||
|
||||
Ok(d_ptr.into())
|
||||
}
|
||||
|
||||
fn debug_log(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let msg_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
|
||||
let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Debug log utf-8 decoding error".to_owned()))?;
|
||||
|
||||
trace!(target: "wasm", "Contract debug message: {}", msg);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Query current gas left for execution
|
||||
pub fn gas_left(&self) -> Result<u64, Error> {
|
||||
if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); }
|
||||
Ok(self.gas_limit - self.gas_counter)
|
||||
}
|
||||
|
||||
/// Shared memory reference
|
||||
pub fn memory(&self) -> &interpreter::MemoryInstance {
|
||||
&*self.memory
|
||||
}
|
||||
|
||||
fn mem_copy(&self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let len = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let dst = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let src = context.value_stack.pop_as::<i32>()? as u32;
|
||||
|
||||
let mem = self.memory().get(src, len as usize)?;
|
||||
self.memory().set(dst, &mem)?;
|
||||
|
||||
Ok(Some(0i32.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
|
||||
fn execute(&mut self, name: &str, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
match name {
|
||||
"_malloc" => {
|
||||
self.malloc(context)
|
||||
},
|
||||
"_free" => {
|
||||
// Since it is arena allocator, free does nothing
|
||||
// todo: update if changed
|
||||
self.user_noop(context)
|
||||
},
|
||||
"_storage_read" => {
|
||||
self.storage_read(context)
|
||||
},
|
||||
"_storage_write" => {
|
||||
self.storage_write(context)
|
||||
},
|
||||
"_suicide" => {
|
||||
self.suicide(context)
|
||||
},
|
||||
"_create" => {
|
||||
self.create(context)
|
||||
},
|
||||
"_debug" => {
|
||||
self.debug_log(context)
|
||||
},
|
||||
"gas" => {
|
||||
self.gas(context)
|
||||
},
|
||||
"_emscripten_memcpy_big" => {
|
||||
self.mem_copy(context)
|
||||
},
|
||||
_ => {
|
||||
trace!("Unknown env func: '{}'", name);
|
||||
self.user_trap(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::super::tests::{FakeExt, FakeCall, FakeCallType};
|
||||
use super::WasmInterpreter;
|
||||
use evm::{self, Evm, GasLeft};
|
||||
use action_params::{ActionParams, ActionValue};
|
||||
use util::{U256, H256, Address};
|
||||
|
||||
macro_rules! load_sample {
|
||||
($name: expr) => {
|
||||
include_bytes!(concat!("../../../res/wasm-tests/compiled/", $name)).to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn test_finalize(res: Result<GasLeft, evm::Error>) -> Result<U256, evm::Error> {
|
||||
match res {
|
||||
Ok(GasLeft::Known(gas)) => Ok(gas),
|
||||
Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented.
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn wasm_interpreter() -> WasmInterpreter {
|
||||
WasmInterpreter::new().expect("wasm interpreter to create without errors")
|
||||
}
|
||||
|
||||
/// Empty contract does almost nothing except producing 1 (one) local node debug log message
|
||||
#[test]
|
||||
fn empty() {
|
||||
let code = load_sample!("empty.wasm");
|
||||
let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = address.clone();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
test_finalize(interpreter.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_996));
|
||||
}
|
||||
|
||||
// This test checks if the contract deserializes payload header properly.
|
||||
// Contract is provided with receiver(address), sender, origin and transaction value
|
||||
// logger.wasm writes all these provided fixed header fields to some arbitrary storage keys.
|
||||
#[test]
|
||||
fn logger() {
|
||||
let code = load_sample!("logger.wasm");
|
||||
let address: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap();
|
||||
let sender: Address = "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d".parse().unwrap();
|
||||
let origin: Address = "0102030405060708090a0b0c0d0e0f1011121314".parse().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = address.clone();
|
||||
params.sender = sender.clone();
|
||||
params.origin = origin.clone();
|
||||
params.gas = U256::from(100_000);
|
||||
params.value = ActionValue::transfer(1_000_000_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
test_finalize(interpreter.exec(params, &mut ext)).unwrap()
|
||||
};
|
||||
|
||||
println!("ext.store: {:?}", ext.store);
|
||||
assert_eq!(gas_left, U256::from(99581));
|
||||
let address_val: H256 = address.into();
|
||||
assert_eq!(
|
||||
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
||||
&address_val,
|
||||
"Logger sets 0x01 key to the provided address"
|
||||
);
|
||||
let sender_val: H256 = sender.into();
|
||||
assert_eq!(
|
||||
ext.store.get(&"0200000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
||||
&sender_val,
|
||||
"Logger sets 0x02 key to the provided sender"
|
||||
);
|
||||
let origin_val: H256 = origin.into();
|
||||
assert_eq!(
|
||||
ext.store.get(&"0300000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
||||
&origin_val,
|
||||
"Logger sets 0x03 key to the provided origin"
|
||||
);
|
||||
assert_eq!(
|
||||
U256::from(ext.store.get(&"0400000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist")),
|
||||
U256::from(1_000_000_000),
|
||||
"Logger sets 0x04 key to the trasferred value"
|
||||
);
|
||||
}
|
||||
|
||||
// This test checks if the contract can allocate memory and pass pointer to the result stream properly.
|
||||
// 1. Contract is being provided with the call descriptor ptr
|
||||
// 2. Descriptor ptr is 16 byte length
|
||||
// 3. The last 8 bytes of call descriptor is the space for the contract to fill [result_ptr[4], result_len[4]]
|
||||
// if it has any result.
|
||||
#[test]
|
||||
fn identity() {
|
||||
let code = load_sample!("identity.wasm");
|
||||
let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.sender = sender.clone();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let (gas_left, result) = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(_) => { panic!("Identity contract should return payload"); },
|
||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_689));
|
||||
|
||||
assert_eq!(
|
||||
Address::from_slice(&result),
|
||||
sender,
|
||||
"Idenity test contract does not return the sender passed"
|
||||
);
|
||||
}
|
||||
|
||||
// Dispersion test sends byte array and expect the contract to 'disperse' the original elements with
|
||||
// their modulo 19 dopant.
|
||||
// The result is always twice as long as the input.
|
||||
// This also tests byte-perfect memory allocation and in/out ptr lifecycle.
|
||||
#[test]
|
||||
fn dispersion() {
|
||||
let code = load_sample!("dispersion.wasm");
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
params.data = Some(vec![
|
||||
0u8, 125, 197, 255, 19
|
||||
]);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let (gas_left, result) = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); },
|
||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_402));
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suicide_not() {
|
||||
let code = load_sample!("suicidal.wasm");
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
params.data = Some(vec![
|
||||
0u8
|
||||
]);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let (gas_left, result) = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); },
|
||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_703));
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![0u8]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suicide() {
|
||||
let code = load_sample!("suicidal.wasm");
|
||||
|
||||
let refund: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
|
||||
let mut args = vec![127u8];
|
||||
args.extend(refund.to_vec());
|
||||
params.data = Some(args);
|
||||
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(gas) => gas,
|
||||
GasLeft::NeedsReturn { .. } => {
|
||||
panic!("Suicidal contract should not return anything when had killed itself");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_747));
|
||||
assert!(ext.suicides.contains(&refund));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(load_sample!("creator.wasm")));
|
||||
params.data = Some(vec![0u8, 2, 4, 8, 16, 32, 64, 128]);
|
||||
params.value = ActionValue::transfer(1_000_000_000);
|
||||
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let gas_left = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(gas) => gas,
|
||||
GasLeft::NeedsReturn { .. } => {
|
||||
panic!("Create contract should not return anthing because ext always fails on creation");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
trace!(target: "wasm", "fake_calls: {:?}", &ext.calls);
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Create,
|
||||
gas: U256::from(99_778),
|
||||
sender_address: None,
|
||||
receive_address: None,
|
||||
value: Some(1_000_000_000.into()),
|
||||
data: vec![0u8, 2, 4, 8, 16, 32, 64, 128],
|
||||
code_address: None,
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(99_768));
|
||||
}
|
||||
|
||||
// Realloc test
|
||||
#[test]
|
||||
fn realloc() {
|
||||
let code = load_sample!("realloc.wasm");
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.gas = U256::from(100_000);
|
||||
params.code = Some(Arc::new(code));
|
||||
params.data = Some(vec![0u8]);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let (gas_left, result) = {
|
||||
let mut interpreter = wasm_interpreter();
|
||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||
match result {
|
||||
GasLeft::Known(_) => { panic!("Realloc should return payload"); },
|
||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||
}
|
||||
};
|
||||
assert_eq!(gas_left, U256::from(98326));
|
||||
assert_eq!(result, vec![0u8; 2]);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user