WASM runtime update (#6467)
* refactor to new parity-wasm * more errors refactoring * final test * update tests * fix merge bugs
This commit is contained in:
parent
375668bc40
commit
ee14a3fb31
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -7,7 +7,7 @@ dependencies = [
|
|||||||
"ethcore-logger 1.8.0",
|
"ethcore-logger 1.8.0",
|
||||||
"ethcore-util 1.8.0",
|
"ethcore-util 1.8.0",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-wasm 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"vm 0.1.0",
|
"vm 0.1.0",
|
||||||
"wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)",
|
"wasm-utils 0.1.0 (git+https://github.com/paritytech/wasm-utils)",
|
||||||
]
|
]
|
||||||
@ -959,7 +959,6 @@ dependencies = [
|
|||||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.2.0",
|
"rlp 0.2.0",
|
||||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2188,7 +2187,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-wasm"
|
name = "parity-wasm"
|
||||||
version = "0.12.1"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -3278,13 +3277,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-utils"
|
name = "wasm-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/wasm-utils#9462bcc0680f0ec2c876abdf75bae981dd4344a5"
|
source = "git+https://github.com/paritytech/wasm-utils#95f9f04d1036c39de5af1c811c6e5dc488fb73d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-wasm 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3500,7 +3500,7 @@ dependencies = [
|
|||||||
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
||||||
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
||||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)" = "<none>"
|
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)" = "<none>"
|
||||||
"checksum parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51104c8b8da5cd0ebe0ab765dfab37bc1927b4a01a3d870b0fe09d9ee65e35ea"
|
"checksum parity-wasm 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "466c01423614bbf89a37b0fc081e1ed3523dfd9064497308ad3f9c7c9f0092bb"
|
||||||
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
||||||
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
||||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||||
|
@ -16,11 +16,10 @@ lazy_static = "0.2"
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
rlp = { path = "../../util/rlp" }
|
rlp = { path = "../../util/rlp" }
|
||||||
vm = { path = "../vm" }
|
vm = { path = "../vm" }
|
||||||
parity-wasm = "0.12"
|
|
||||||
parking_lot = "0.4"
|
|
||||||
ethcore-logger = { path = "../../logger" }
|
ethcore-logger = { path = "../../logger" }
|
||||||
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
||||||
hash = { path = "../../util/hash" }
|
hash = { path = "../../util/hash" }
|
||||||
|
parking_lot = "0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
|
@ -23,7 +23,6 @@ extern crate ethcore_util as util;
|
|||||||
extern crate ethcore_bigint as bigint;
|
extern crate ethcore_bigint as bigint;
|
||||||
extern crate ethjson;
|
extern crate ethjson;
|
||||||
extern crate rlp;
|
extern crate rlp;
|
||||||
extern crate parity_wasm;
|
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate wasm_utils;
|
extern crate wasm_utils;
|
||||||
extern crate ethcore_logger;
|
extern crate ethcore_logger;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 519b0b967cffd7d1236ef21698b1e6e415a048e9
|
Subproject commit 5fd27564f1ab49b25bb419bfc0cc68137e1f12f2
|
@ -8,7 +8,7 @@ byteorder = "1.0"
|
|||||||
ethcore-util = { path = "../../util" }
|
ethcore-util = { path = "../../util" }
|
||||||
ethcore-bigint = { path = "../../util/bigint" }
|
ethcore-bigint = { path = "../../util/bigint" }
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
parity-wasm = "0.12"
|
parity-wasm = "0.14"
|
||||||
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
||||||
vm = { path = "../vm" }
|
vm = { path = "../vm" }
|
||||||
ethcore-logger = { path = "../../logger" }
|
ethcore-logger = { path = "../../logger" }
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
use parity_wasm::elements::ValueType::*;
|
use parity_wasm::elements::ValueType::*;
|
||||||
use parity_wasm::interpreter::{self, UserFunctionDescriptor};
|
use parity_wasm::interpreter::{self, UserFunctionDescriptor};
|
||||||
use parity_wasm::interpreter::UserFunctionDescriptor::*;
|
use parity_wasm::interpreter::UserFunctionDescriptor::*;
|
||||||
use super::runtime::Runtime;
|
use super::runtime::{Runtime, UserTrap};
|
||||||
|
|
||||||
pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
||||||
Static(
|
Static(
|
||||||
@ -87,6 +87,41 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
|||||||
&[I32; 3],
|
&[I32; 3],
|
||||||
Some(I32),
|
Some(I32),
|
||||||
),
|
),
|
||||||
|
Static(
|
||||||
|
"_panic",
|
||||||
|
&[I32; 2],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_blockhash",
|
||||||
|
&[I32; 3],
|
||||||
|
Some(I32),
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_coinbase",
|
||||||
|
&[I32],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_timestamp",
|
||||||
|
&[],
|
||||||
|
Some(I32),
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_blocknumber",
|
||||||
|
&[],
|
||||||
|
Some(I32),
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_difficulty",
|
||||||
|
&[I32],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Static(
|
||||||
|
"_gaslimit",
|
||||||
|
&[I32],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
|
||||||
// TODO: Get rid of it also somehow?
|
// TODO: Get rid of it also somehow?
|
||||||
Static(
|
Static(
|
||||||
@ -102,9 +137,10 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> {
|
pub fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserDefinedElements<'a, UserTrap> {
|
||||||
interpreter::UserFunctions {
|
interpreter::UserDefinedElements {
|
||||||
executor: runtime,
|
executor: Some(runtime),
|
||||||
|
globals: ::std::collections::HashMap::new(),
|
||||||
functions: ::std::borrow::Cow::from(SIGNATURES),
|
functions: ::std::borrow::Cow::from(SIGNATURES),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -39,21 +39,41 @@ use parity_wasm::{interpreter, elements};
|
|||||||
use parity_wasm::interpreter::ModuleInstanceInterface;
|
use parity_wasm::interpreter::ModuleInstanceInterface;
|
||||||
|
|
||||||
use vm::{GasLeft, ReturnData, ActionParams};
|
use vm::{GasLeft, ReturnData, ActionParams};
|
||||||
use self::runtime::{Runtime, RuntimeContext};
|
use self::runtime::{Runtime, RuntimeContext, UserTrap};
|
||||||
|
|
||||||
pub use self::runtime::Error as RuntimeError;
|
pub use self::runtime::InterpreterError;
|
||||||
|
|
||||||
const DEFAULT_RESULT_BUFFER: usize = 1024;
|
const DEFAULT_RESULT_BUFFER: usize = 1024;
|
||||||
|
|
||||||
|
/// Wrapped interpreter error
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Error(InterpreterError);
|
||||||
|
|
||||||
|
impl From<InterpreterError> for Error {
|
||||||
|
fn from(e: InterpreterError) -> Self {
|
||||||
|
Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for vm::Error {
|
||||||
|
fn from(e: Error) -> Self {
|
||||||
|
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UserTrap> for vm::Error {
|
||||||
|
fn from(e: UserTrap) -> Self { e.into() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Wasm interpreter instance
|
/// Wasm interpreter instance
|
||||||
pub struct WasmInterpreter {
|
pub struct WasmInterpreter {
|
||||||
program: interpreter::ProgramInstance,
|
program: runtime::InterpreterProgramInstance,
|
||||||
result: Vec<u8>,
|
result: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmInterpreter {
|
impl WasmInterpreter {
|
||||||
/// New wasm interpreter instance
|
/// New wasm interpreter instance
|
||||||
pub fn new() -> Result<WasmInterpreter, RuntimeError> {
|
pub fn new() -> Result<WasmInterpreter, Error> {
|
||||||
Ok(WasmInterpreter {
|
Ok(WasmInterpreter {
|
||||||
program: interpreter::ProgramInstance::new()?,
|
program: interpreter::ProgramInstance::new()?,
|
||||||
result: Vec::with_capacity(DEFAULT_RESULT_BUFFER),
|
result: Vec::with_capacity(DEFAULT_RESULT_BUFFER),
|
||||||
@ -109,7 +129,7 @@ impl vm::Vm for WasmInterpreter {
|
|||||||
params.value.value(),
|
params.value.value(),
|
||||||
params.data.unwrap_or(Vec::with_capacity(0)),
|
params.data.unwrap_or(Vec::with_capacity(0)),
|
||||||
)
|
)
|
||||||
)?;
|
).map_err(|e| Error(e))?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let execution_params = runtime.execution_params()
|
let execution_params = runtime.execution_params()
|
||||||
@ -118,27 +138,30 @@ impl vm::Vm for WasmInterpreter {
|
|||||||
let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals))
|
let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
trace!(target: "wasm", "Error adding contract module: {:?}", err);
|
trace!(target: "wasm", "Error adding contract module: {:?}", err);
|
||||||
vm::Error::from(RuntimeError::Interpreter(err))
|
vm::Error::from(Error(err))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
module_instance.execute_export("_call", execution_params)
|
match module_instance.execute_export("_call", execution_params) {
|
||||||
.map_err(|err| {
|
Ok(_) => { },
|
||||||
|
Err(interpreter::Error::User(UserTrap::Suicide)) => { },
|
||||||
|
Err(err) => {
|
||||||
trace!(target: "wasm", "Error executing contract: {:?}", err);
|
trace!(target: "wasm", "Error executing contract: {:?}", err);
|
||||||
vm::Error::from(RuntimeError::Interpreter(err))
|
return Err(vm::Error::from(Error(err)))
|
||||||
})?;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = result::WasmResult::new(d_ptr);
|
let result = result::WasmResult::new(d_ptr);
|
||||||
if result.peek_empty(&*runtime.memory())? {
|
if result.peek_empty(&*runtime.memory()).map_err(|e| Error(e))? {
|
||||||
trace!(target: "wasm", "Contract execution result is empty.");
|
trace!(target: "wasm", "Contract execution result is empty.");
|
||||||
Ok(GasLeft::Known(runtime.gas_left()?.into()))
|
Ok(GasLeft::Known(runtime.gas_left()?.into()))
|
||||||
} else {
|
} else {
|
||||||
self.result.clear();
|
self.result.clear();
|
||||||
// todo: use memory views to avoid copy
|
// todo: use memory views to avoid copy
|
||||||
self.result.extend(result.pop(&*runtime.memory())?);
|
self.result.extend(result.pop(&*runtime.memory()).map_err(|e| Error(e.into()))?);
|
||||||
let len = self.result.len();
|
let len = self.result.len();
|
||||||
Ok(GasLeft::NeedsReturn {
|
Ok(GasLeft::NeedsReturn {
|
||||||
gas_left: runtime.gas_left()?.into(),
|
gas_left: runtime.gas_left().map_err(|e| Error(e.into()))?.into(),
|
||||||
data: ReturnData::new(
|
data: ReturnData::new(
|
||||||
::std::mem::replace(&mut self.result, Vec::with_capacity(DEFAULT_RESULT_BUFFER)),
|
::std::mem::replace(&mut self.result, Vec::with_capacity(DEFAULT_RESULT_BUFFER)),
|
||||||
0,
|
0,
|
||||||
@ -149,9 +172,3 @@ impl vm::Vm for WasmInterpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<runtime::Error> for vm::Error {
|
|
||||||
fn from(err: runtime::Error) -> vm::Error {
|
|
||||||
vm::Error::Wasm(format!("WASM runtime-error: {:?}", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
//! Wasm bound-checked ptr
|
//! Wasm bound-checked ptr
|
||||||
|
|
||||||
use parity_wasm::interpreter;
|
use super::runtime::{InterpreterMemoryInstance, InterpreterError, UserTrap};
|
||||||
|
|
||||||
/// Bound-checked wrapper for webassembly memory
|
/// Bound-checked wrapper for webassembly memory
|
||||||
pub struct WasmPtr(u32);
|
pub struct WasmPtr(u32);
|
||||||
|
|
||||||
/// Error in bound check
|
/// Error in bound check
|
||||||
@ -28,15 +28,21 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<u32> for WasmPtr {
|
impl From<u32> for WasmPtr {
|
||||||
fn from(raw: u32) -> Self {
|
fn from(raw: u32) -> Self {
|
||||||
WasmPtr(raw)
|
WasmPtr(raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Error> for InterpreterError {
|
||||||
|
fn from(_e: Error) -> Self {
|
||||||
|
UserTrap::MemoryAccessViolation.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WasmPtr {
|
impl WasmPtr {
|
||||||
// todo: use memory view when they are on
|
// todo: use memory view when they are on
|
||||||
/// Check memory range and return data with given length starting from the current pointer value
|
/// 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> {
|
pub fn slice(&self, len: u32, mem: &InterpreterMemoryInstance) -> Result<Vec<u8>, Error> {
|
||||||
mem.get(self.0, len as usize).map_err(|_| Error::AccessViolation)
|
mem.get(self.0, len as usize).map_err(|_| Error::AccessViolation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,10 +18,8 @@
|
|||||||
|
|
||||||
use byteorder::{LittleEndian, ByteOrder};
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
|
|
||||||
use parity_wasm::interpreter;
|
|
||||||
|
|
||||||
use super::ptr::WasmPtr;
|
use super::ptr::WasmPtr;
|
||||||
use super::runtime::Error as RuntimeError;
|
use super::runtime::{InterpreterError, InterpreterMemoryInstance};
|
||||||
|
|
||||||
/// Wrapper for wasm contract call result
|
/// Wrapper for wasm contract call result
|
||||||
pub struct WasmResult {
|
pub struct WasmResult {
|
||||||
@ -35,13 +33,13 @@ impl WasmResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the result contains any data
|
/// Check if the result contains any data
|
||||||
pub fn peek_empty(&self, mem: &interpreter::MemoryInstance) -> Result<bool, RuntimeError> {
|
pub fn peek_empty(&self, mem: &InterpreterMemoryInstance) -> Result<bool, InterpreterError> {
|
||||||
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
|
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
|
||||||
Ok(result_len == 0)
|
Ok(result_len == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the result ptr and return the actual data from wasm linear memory
|
/// Consume the result ptr and return the actual data from wasm linear memory
|
||||||
pub fn pop(self, mem: &interpreter::MemoryInstance) -> Result<Vec<u8>, RuntimeError> {
|
pub fn pop(self, mem: &InterpreterMemoryInstance) -> Result<Vec<u8>, InterpreterError> {
|
||||||
let result_ptr = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[8..12]);
|
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]);
|
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
|
||||||
trace!(target: "wasm", "contract result: {} bytes at @{}", result_len, result_ptr);
|
trace!(target: "wasm", "contract result: {} bytes at @{}", result_len, result_ptr);
|
||||||
|
@ -30,31 +30,68 @@ use vm::CallType;
|
|||||||
use super::ptr::{WasmPtr, Error as PtrError};
|
use super::ptr::{WasmPtr, Error as PtrError};
|
||||||
use super::call_args::CallArgs;
|
use super::call_args::CallArgs;
|
||||||
|
|
||||||
/// Wasm runtime error
|
/// User trap in native code
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Error {
|
pub enum UserTrap {
|
||||||
/// Storage error
|
/// Storage read error
|
||||||
Storage,
|
StorageReadError,
|
||||||
/// Allocator error
|
/// Storage update error
|
||||||
Allocator,
|
StorageUpdateError,
|
||||||
/// Invalid gas state during the call
|
|
||||||
InvalidGasState,
|
|
||||||
/// Memory access violation
|
/// Memory access violation
|
||||||
AccessViolation,
|
MemoryAccessViolation,
|
||||||
/// Interpreter runtime error
|
/// Native code resulted in suicide
|
||||||
Interpreter(interpreter::Error),
|
Suicide,
|
||||||
|
/// Suicide was requested but coudn't complete
|
||||||
|
SuicideAbort,
|
||||||
|
/// Invalid gas state inside interpreter
|
||||||
|
InvalidGasState,
|
||||||
|
/// Query of the balance resulted in an error
|
||||||
|
BalanceQueryError,
|
||||||
|
/// Failed allocation
|
||||||
|
AllocationFailed,
|
||||||
|
/// Gas limit reached
|
||||||
|
GasLimit,
|
||||||
|
/// Unknown runtime function
|
||||||
|
Unknown,
|
||||||
|
/// Passed string had invalid utf-8 encoding
|
||||||
|
BadUtf8,
|
||||||
|
/// Other error in native code
|
||||||
|
Other,
|
||||||
|
/// Panic with message
|
||||||
|
Panic(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<interpreter::Error> for Error {
|
impl ::std::fmt::Display for UserTrap {
|
||||||
fn from(err: interpreter::Error) -> Self {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||||
Error::Interpreter(err)
|
match *self {
|
||||||
|
UserTrap::StorageReadError => write!(f, "Storage read error"),
|
||||||
|
UserTrap::StorageUpdateError => write!(f, "Storage update error"),
|
||||||
|
UserTrap::MemoryAccessViolation => write!(f, "Memory access violation"),
|
||||||
|
UserTrap::SuicideAbort => write!(f, "Attempt to suicide resulted in an error"),
|
||||||
|
UserTrap::InvalidGasState => write!(f, "Invalid gas state"),
|
||||||
|
UserTrap::BalanceQueryError => write!(f, "Balance query resulted in an error"),
|
||||||
|
UserTrap::Suicide => write!(f, "Suicide result"),
|
||||||
|
UserTrap::Unknown => write!(f, "Unknown runtime function invoked"),
|
||||||
|
UserTrap::AllocationFailed => write!(f, "Memory allocation failed (OOM)"),
|
||||||
|
UserTrap::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"),
|
||||||
|
UserTrap::GasLimit => write!(f, "Invocation resulted in gas limit violated"),
|
||||||
|
UserTrap::Other => write!(f, "Other unspecified error"),
|
||||||
|
UserTrap::Panic(ref msg) => write!(f, "Panic: {}", msg),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PtrError> for Error {
|
impl interpreter::UserError for UserTrap { }
|
||||||
|
|
||||||
|
pub type InterpreterError = interpreter::Error<UserTrap>;
|
||||||
|
pub type InterpreterMemoryInstance = interpreter::MemoryInstance<UserTrap>;
|
||||||
|
pub type InterpreterProgramInstance = interpreter::ProgramInstance<UserTrap>;
|
||||||
|
pub type InterpreterCallerContext<'a> = interpreter::CallerContext<'a, UserTrap>;
|
||||||
|
|
||||||
|
impl From<PtrError> for UserTrap {
|
||||||
fn from(err: PtrError) -> Self {
|
fn from(err: PtrError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
PtrError::AccessViolation => Error::AccessViolation,
|
PtrError::AccessViolation => UserTrap::MemoryAccessViolation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,20 +116,20 @@ pub struct Runtime<'a, 'b> {
|
|||||||
gas_limit: u64,
|
gas_limit: u64,
|
||||||
dynamic_top: u32,
|
dynamic_top: u32,
|
||||||
ext: &'a mut vm::Ext,
|
ext: &'a mut vm::Ext,
|
||||||
memory: Arc<interpreter::MemoryInstance>,
|
memory: Arc<InterpreterMemoryInstance>,
|
||||||
context: RuntimeContext,
|
context: RuntimeContext,
|
||||||
instance: &'b interpreter::ProgramInstance,
|
instance: &'b InterpreterProgramInstance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Runtime<'a, 'b> {
|
impl<'a, 'b> Runtime<'a, 'b> {
|
||||||
/// New runtime for wasm contract with specified params
|
/// New runtime for wasm contract with specified params
|
||||||
pub fn with_params<'c, 'd>(
|
pub fn with_params<'c, 'd>(
|
||||||
ext: &'c mut vm::Ext,
|
ext: &'c mut vm::Ext,
|
||||||
memory: Arc<interpreter::MemoryInstance>,
|
memory: Arc<InterpreterMemoryInstance>,
|
||||||
stack_space: u32,
|
stack_space: u32,
|
||||||
gas_limit: u64,
|
gas_limit: u64,
|
||||||
context: RuntimeContext,
|
context: RuntimeContext,
|
||||||
program_instance: &'d interpreter::ProgramInstance,
|
program_instance: &'d InterpreterProgramInstance,
|
||||||
) -> Runtime<'c, 'd> {
|
) -> Runtime<'c, 'd> {
|
||||||
Runtime {
|
Runtime {
|
||||||
gas_counter: 0,
|
gas_counter: 0,
|
||||||
@ -106,30 +143,28 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write to the storage from wasm memory
|
/// Write to the storage from wasm memory
|
||||||
pub fn storage_write(&mut self, context: interpreter::CallerContext)
|
pub fn storage_write(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let mut context = context;
|
let mut context = context;
|
||||||
let val = self.pop_h256(&mut context)?;
|
let val = self.pop_h256(&mut context)?;
|
||||||
let key = self.pop_h256(&mut context)?;
|
let key = self.pop_h256(&mut context)?;
|
||||||
trace!(target: "wasm", "storage_write: value {} at @{}", &val, &key);
|
trace!(target: "wasm", "storage_write: value {} at @{}", &val, &key);
|
||||||
|
|
||||||
self.ext.set_storage(key, val)
|
self.ext.set_storage(key, val).map_err(|_| UserTrap::StorageUpdateError)?;
|
||||||
.map_err(|_| interpreter::Error::Trap("Storage update error".to_owned()))?;
|
|
||||||
|
|
||||||
Ok(Some(0i32.into()))
|
Ok(Some(0i32.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read from the storage to wasm memory
|
/// Read from the storage to wasm memory
|
||||||
pub fn storage_read(&mut self, context: interpreter::CallerContext)
|
pub fn storage_read(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let mut context = context;
|
let mut context = context;
|
||||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||||
let key = self.pop_h256(&mut context)?;
|
let key = self.pop_h256(&mut context)?;
|
||||||
|
|
||||||
let val = self.ext.storage_at(&key)
|
let val = self.ext.storage_at(&key).map_err(|_| UserTrap::StorageReadError)?;
|
||||||
.map_err(|_| interpreter::Error::Trap("Storage read error".to_owned()))?;
|
|
||||||
|
|
||||||
self.memory.set(val_ptr as u32, &*val)?;
|
self.memory.set(val_ptr as u32, &*val)?;
|
||||||
|
|
||||||
@ -137,21 +172,21 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pass suicide to state runtime
|
/// Pass suicide to state runtime
|
||||||
pub fn suicide(&mut self, context: interpreter::CallerContext)
|
pub fn suicide(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let mut context = context;
|
let mut context = context;
|
||||||
let refund_address = self.pop_address(&mut context)?;
|
let refund_address = self.pop_address(&mut context)?;
|
||||||
|
|
||||||
self.ext.suicide(&refund_address)
|
self.ext.suicide(&refund_address).map_err(|_| UserTrap::SuicideAbort)?;
|
||||||
.map_err(|_| interpreter::Error::Trap("Suicide error".to_owned()))?;
|
|
||||||
|
|
||||||
Ok(None)
|
// We send trap to interpreter so it should abort further execution
|
||||||
|
Err(UserTrap::Suicide.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke create in the state runtime
|
/// Invoke create in the state runtime
|
||||||
pub fn create(&mut self, context: interpreter::CallerContext)
|
pub fn create(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// method signature:
|
// method signature:
|
||||||
@ -172,7 +207,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
let code = self.memory.get(code_ptr, code_len as usize)?;
|
let code = self.memory.get(code_ptr, code_len as usize)?;
|
||||||
|
|
||||||
let gas_left = self.gas_left()
|
let gas_left = self.gas_left()
|
||||||
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?
|
.map_err(|_| UserTrap::InvalidGasState)?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
match self.ext.create(&gas_left, &endowment, &code, vm::CreateContractAddress::FromSenderAndCodeHash) {
|
match self.ext.create(&gas_left, &endowment, &code, vm::CreateContractAddress::FromSenderAndCodeHash) {
|
||||||
@ -189,8 +224,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&mut self, context: interpreter::CallerContext)
|
pub fn call(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// method signature:
|
// method signature:
|
||||||
@ -207,8 +242,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn call_code(&mut self, context: interpreter::CallerContext)
|
fn call_code(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// signature (same as static call):
|
// signature (same as static call):
|
||||||
@ -227,9 +262,9 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
use_val: bool,
|
use_val: bool,
|
||||||
call_type: CallType,
|
call_type: CallType,
|
||||||
context: interpreter::CallerContext,
|
context: InterpreterCallerContext,
|
||||||
)
|
)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
|
|
||||||
trace!(target: "wasm", "runtime: call code");
|
trace!(target: "wasm", "runtime: call code");
|
||||||
@ -255,7 +290,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
|
|
||||||
if let Some(ref val) = val {
|
if let Some(ref val) = val {
|
||||||
let address_balance = self.ext.balance(&self.context.address)
|
let address_balance = self.ext.balance(&self.context.address)
|
||||||
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?;
|
.map_err(|_| UserTrap::BalanceQueryError)?;
|
||||||
|
|
||||||
if &address_balance < val {
|
if &address_balance < val {
|
||||||
trace!(target: "wasm", "runtime: call failed due to balance check");
|
trace!(target: "wasm", "runtime: call failed due to balance check");
|
||||||
@ -266,7 +301,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
let mut result = Vec::with_capacity(result_alloc_len as usize);
|
let mut result = Vec::with_capacity(result_alloc_len as usize);
|
||||||
result.resize(result_alloc_len as usize, 0);
|
result.resize(result_alloc_len as usize, 0);
|
||||||
let gas = self.gas_left()
|
let gas = self.gas_left()
|
||||||
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?
|
.map_err(|_| UserTrap::InvalidGasState)?
|
||||||
.into();
|
.into();
|
||||||
// todo: optimize to use memory views once it's in
|
// todo: optimize to use memory views once it's in
|
||||||
let payload = self.memory.get(input_ptr, input_len as usize)?;
|
let payload = self.memory.get(input_ptr, input_len as usize)?;
|
||||||
@ -294,8 +329,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_call(&mut self, context: interpreter::CallerContext)
|
pub fn static_call(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
// signature (same as code call):
|
// signature (same as code call):
|
||||||
// fn (
|
// fn (
|
||||||
@ -311,8 +346,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
|
|
||||||
|
|
||||||
/// Allocate memory using the wasm stack params
|
/// Allocate memory using the wasm stack params
|
||||||
pub fn malloc(&mut self, context: interpreter::CallerContext)
|
pub fn malloc(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let amount = context.value_stack.pop_as::<i32>()? as u32;
|
let amount = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
let previous_top = self.dynamic_top;
|
let previous_top = self.dynamic_top;
|
||||||
@ -321,21 +356,21 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate memory in wasm memory instance
|
/// Allocate memory in wasm memory instance
|
||||||
pub fn alloc(&mut self, amount: u32) -> Result<u32, Error> {
|
pub fn alloc(&mut self, amount: u32) -> Result<u32, UserTrap> {
|
||||||
let previous_top = self.dynamic_top;
|
let previous_top = self.dynamic_top;
|
||||||
self.dynamic_top = previous_top + amount;
|
self.dynamic_top = previous_top + amount;
|
||||||
Ok(previous_top.into())
|
Ok(previous_top.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report gas cost with the params passed in wasm stack
|
/// Report gas cost with the params passed in wasm stack
|
||||||
fn gas(&mut self, context: interpreter::CallerContext)
|
fn gas(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let amount = context.value_stack.pop_as::<i32>()? as u64;
|
let amount = context.value_stack.pop_as::<i32>()? as u64;
|
||||||
if self.charge_gas(amount) {
|
if self.charge_gas(amount) {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.gas_limit)))
|
Err(UserTrap::GasLimit.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,50 +385,50 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn h256_at(&self, ptr: WasmPtr) -> Result<H256, interpreter::Error> {
|
fn h256_at(&self, ptr: WasmPtr) -> Result<H256, InterpreterError> {
|
||||||
Ok(H256::from_slice(&ptr.slice(32, &*self.memory)
|
Ok(H256::from_slice(&ptr.slice(32, &*self.memory)
|
||||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?
|
.map_err(|_| UserTrap::MemoryAccessViolation)?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_h256(&self, context: &mut interpreter::CallerContext) -> Result<H256, interpreter::Error> {
|
fn pop_h256(&self, context: &mut InterpreterCallerContext) -> Result<H256, InterpreterError> {
|
||||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
.map_err(|_| UserTrap::MemoryAccessViolation)?;
|
||||||
self.h256_at(ptr)
|
self.h256_at(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_u256(&self, context: &mut interpreter::CallerContext) -> Result<U256, interpreter::Error> {
|
fn pop_u256(&self, context: &mut InterpreterCallerContext) -> Result<U256, InterpreterError> {
|
||||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
.map_err(|_| UserTrap::MemoryAccessViolation)?;
|
||||||
self.h256_at(ptr).map(Into::into)
|
self.h256_at(ptr).map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address_at(&self, ptr: WasmPtr) -> Result<Address, interpreter::Error> {
|
fn address_at(&self, ptr: WasmPtr) -> Result<Address, InterpreterError> {
|
||||||
Ok(Address::from_slice(&ptr.slice(20, &*self.memory)
|
Ok(Address::from_slice(&ptr.slice(20, &*self.memory)
|
||||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?
|
.map_err(|_| UserTrap::MemoryAccessViolation)?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_address(&self, context: &mut interpreter::CallerContext) -> Result<Address, interpreter::Error> {
|
fn pop_address(&self, context: &mut InterpreterCallerContext) -> Result<Address, InterpreterError> {
|
||||||
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
let ptr = WasmPtr::from_i32(context.value_stack.pop_as::<i32>()?)
|
||||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
.map_err(|_| UserTrap::MemoryAccessViolation)?;
|
||||||
self.address_at(ptr)
|
self.address_at(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_trap(&mut self, _context: interpreter::CallerContext)
|
fn unknown_trap(&mut self, _context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, UserTrap>
|
||||||
{
|
{
|
||||||
Err(interpreter::Error::Trap("unknown trap".to_owned()))
|
Err(UserTrap::Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_noop(&mut self,
|
fn user_noop(&mut self,
|
||||||
_context: interpreter::CallerContext
|
_context: InterpreterCallerContext
|
||||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
) -> Result<Option<interpreter::RuntimeValue>, InterpreterError> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write call descriptor to wasm memory
|
/// Write call descriptor to wasm memory
|
||||||
pub fn write_descriptor(&mut self, call_args: CallArgs) -> Result<WasmPtr, Error> {
|
pub fn write_descriptor(&mut self, call_args: CallArgs) -> Result<WasmPtr, InterpreterError> {
|
||||||
let d_ptr = self.alloc(16)?;
|
let d_ptr = self.alloc(16)?;
|
||||||
|
|
||||||
let args_len = call_args.len();
|
let args_len = call_args.len();
|
||||||
@ -417,14 +452,14 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
Ok(d_ptr.into())
|
Ok(d_ptr.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_log(&mut self, context: interpreter::CallerContext)
|
fn debug_log(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
|
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
let msg_ptr = 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)?)
|
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()))?;
|
.map_err(|_| UserTrap::BadUtf8)?;
|
||||||
|
|
||||||
trace!(target: "wasm", "Contract debug message: {}", msg);
|
trace!(target: "wasm", "Contract debug message: {}", msg);
|
||||||
|
|
||||||
@ -432,18 +467,18 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Query current gas left for execution
|
/// Query current gas left for execution
|
||||||
pub fn gas_left(&self) -> Result<u64, Error> {
|
pub fn gas_left(&self) -> Result<u64, UserTrap> {
|
||||||
if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); }
|
if self.gas_counter > self.gas_limit { return Err(UserTrap::InvalidGasState); }
|
||||||
Ok(self.gas_limit - self.gas_counter)
|
Ok(self.gas_limit - self.gas_counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared memory reference
|
/// Shared memory reference
|
||||||
pub fn memory(&self) -> &interpreter::MemoryInstance {
|
pub fn memory(&self) -> &InterpreterMemoryInstance {
|
||||||
&*self.memory
|
&*self.memory
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_copy(&self, context: interpreter::CallerContext)
|
fn mem_copy(&self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let len = context.value_stack.pop_as::<i32>()? as u32;
|
let len = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
let dst = context.value_stack.pop_as::<i32>()? as u32;
|
let dst = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
@ -459,8 +494,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
x >> 24 | x >> 8 & 0xff00 | x << 8 & 0xff0000 | x << 24
|
x >> 24 | x >> 8 & 0xff00 | x << 8 & 0xff0000 | x << 24
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bitswap_i64(&mut self, context: interpreter::CallerContext)
|
fn bitswap_i64(&mut self, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
let x1 = context.value_stack.pop_as::<i32>()?;
|
let x1 = context.value_stack.pop_as::<i32>()?;
|
||||||
let x2 = context.value_stack.pop_as::<i32>()?;
|
let x2 = context.value_stack.pop_as::<i32>()?;
|
||||||
@ -471,13 +506,83 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
self.return_i64(result)
|
self.return_i64(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_i64(&mut self, val: i64) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
fn user_panic(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
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(|_| UserTrap::BadUtf8)?;
|
||||||
|
|
||||||
|
trace!(target: "wasm", "Contract custom panic message: {}", msg);
|
||||||
|
|
||||||
|
Err(UserTrap::Panic(msg).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_hash(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let block_hi = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let block_lo = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
|
||||||
|
let block_num = (block_hi as u64) << 32 | block_lo as u64;
|
||||||
|
|
||||||
|
trace!("Requesting block hash for block #{}", block_num);
|
||||||
|
let hash = self.ext.blockhash(&U256::from(block_num));
|
||||||
|
|
||||||
|
self.memory.set(return_ptr, &*hash)?;
|
||||||
|
|
||||||
|
Ok(Some(0i32.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coinbase(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
self.memory.set(return_ptr, &*self.ext.env_info().author)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timestamp(&mut self, _context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let timestamp = self.ext.env_info().timestamp as i64;
|
||||||
|
self.return_i64(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_number(&mut self, _context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let block_number: u64 = self.ext.env_info().number.into();
|
||||||
|
self.return_i64(block_number as i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn difficulty(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let difficulty: H256 = self.ext.env_info().difficulty.into();
|
||||||
|
self.memory.set(return_ptr, &*difficulty)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ext_gas_limit(&mut self, context: InterpreterCallerContext)
|
||||||
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
|
{
|
||||||
|
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||||
|
let gas_limit: H256 = self.ext.env_info().gas_limit.into();
|
||||||
|
self.memory.set(return_ptr, &*gas_limit)?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_i64(&mut self, val: i64) -> Result<Option<interpreter::RuntimeValue>, InterpreterError> {
|
||||||
let uval = val as u64;
|
let uval = val as u64;
|
||||||
let hi = (uval >> 32) as i32;
|
let hi = (uval >> 32) as i32;
|
||||||
let lo = (uval << 32 >> 32) as i32;
|
let lo = (uval << 32 >> 32) as i32;
|
||||||
|
|
||||||
let target = self.instance.module("contract")
|
let target = self.instance.module("contract").ok_or(UserTrap::Other)?;
|
||||||
.ok_or(interpreter::Error::Trap("Error locating main execution entry".to_owned()))?;
|
|
||||||
target.execute_export(
|
target.execute_export(
|
||||||
"setTempRet0",
|
"setTempRet0",
|
||||||
self.execution_params().add_argument(
|
self.execution_params().add_argument(
|
||||||
@ -489,7 +594,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execution_params(&mut self) -> interpreter::ExecutionParams {
|
pub fn execution_params(&mut self) -> interpreter::ExecutionParams<UserTrap> {
|
||||||
use super::env;
|
use super::env;
|
||||||
|
|
||||||
let env_instance = self.instance.module("env")
|
let env_instance = self.instance.module("env")
|
||||||
@ -505,9 +610,9 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> interpreter::UserFunctionExecutor for Runtime<'a, 'b> {
|
impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
||||||
fn execute(&mut self, name: &str, context: interpreter::CallerContext)
|
fn execute(&mut self, name: &str, context: InterpreterCallerContext)
|
||||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||||
{
|
{
|
||||||
match name {
|
match name {
|
||||||
"_malloc" => {
|
"_malloc" => {
|
||||||
@ -551,10 +656,31 @@ impl<'a, 'b> interpreter::UserFunctionExecutor for Runtime<'a, 'b> {
|
|||||||
"_llvm_bswap_i64" => {
|
"_llvm_bswap_i64" => {
|
||||||
self.bitswap_i64(context)
|
self.bitswap_i64(context)
|
||||||
},
|
},
|
||||||
|
"_panic" => {
|
||||||
|
self.user_panic(context)
|
||||||
|
},
|
||||||
|
"_blockhash" => {
|
||||||
|
self.block_hash(context)
|
||||||
|
},
|
||||||
|
"_coinbase" => {
|
||||||
|
self.coinbase(context)
|
||||||
|
},
|
||||||
|
"_timestamp" => {
|
||||||
|
self.timestamp(context)
|
||||||
|
},
|
||||||
|
"_blocknumber" => {
|
||||||
|
self.block_number(context)
|
||||||
|
},
|
||||||
|
"_difficulty" => {
|
||||||
|
self.difficulty(context)
|
||||||
|
},
|
||||||
|
"_gaslimit" => {
|
||||||
|
self.ext_gas_limit(context)
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
||||||
self.user_trap(context)
|
Ok(self.unknown_trap(context)?)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::collections::HashMap;
|
||||||
use byteorder::{LittleEndian, ByteOrder};
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
use bigint::prelude::U256;
|
use bigint::prelude::U256;
|
||||||
use bigint::hash::H256;
|
use bigint::hash::H256;
|
||||||
@ -87,7 +88,7 @@ fn logger() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
println!("ext.store: {:?}", ext.store);
|
println!("ext.store: {:?}", ext.store);
|
||||||
assert_eq!(gas_left, U256::from(99327));
|
assert_eq!(gas_left, U256::from(99529));
|
||||||
let address_val: H256 = address.into();
|
let address_val: H256 = address.into();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
||||||
@ -138,7 +139,7 @@ fn identity() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(99_672));
|
assert_eq!(gas_left, U256::from(99_762));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Address::from_slice(&result),
|
Address::from_slice(&result),
|
||||||
@ -172,7 +173,7 @@ fn dispersion() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(99_270));
|
assert_eq!(gas_left, U256::from(99_360));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -201,7 +202,7 @@ fn suicide_not() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(99_578));
|
assert_eq!(gas_left, U256::from(99_668));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -235,7 +236,7 @@ fn suicide() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(99_621));
|
assert_eq!(gas_left, U256::from(99_699));
|
||||||
assert!(ext.suicides.contains(&refund));
|
assert!(ext.suicides.contains(&refund));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ fn create() {
|
|||||||
assert!(ext.calls.contains(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Create,
|
call_type: FakeCallType::Create,
|
||||||
gas: U256::from(99_674),
|
gas: U256::from(99_734),
|
||||||
sender_address: None,
|
sender_address: None,
|
||||||
receive_address: None,
|
receive_address: None,
|
||||||
value: Some(1_000_000_000.into()),
|
value: Some(1_000_000_000.into()),
|
||||||
@ -274,7 +275,7 @@ fn create() {
|
|||||||
code_address: None,
|
code_address: None,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert_eq!(gas_left, U256::from(99_596));
|
assert_eq!(gas_left, U256::from(99_686));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -308,7 +309,7 @@ fn call_code() {
|
|||||||
assert!(ext.calls.contains(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
gas: U256::from(99_069),
|
gas: U256::from(99_129),
|
||||||
sender_address: Some(sender),
|
sender_address: Some(sender),
|
||||||
receive_address: Some(receiver),
|
receive_address: Some(receiver),
|
||||||
value: None,
|
value: None,
|
||||||
@ -316,7 +317,7 @@ fn call_code() {
|
|||||||
code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()),
|
code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()),
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert_eq!(gas_left, U256::from(94144));
|
assert_eq!(gas_left, U256::from(94262));
|
||||||
|
|
||||||
// siphash result
|
// siphash result
|
||||||
let res = LittleEndian::read_u32(&result[..]);
|
let res = LittleEndian::read_u32(&result[..]);
|
||||||
@ -353,7 +354,7 @@ fn call_static() {
|
|||||||
assert!(ext.calls.contains(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
gas: U256::from(99_069),
|
gas: U256::from(99_129),
|
||||||
sender_address: Some(sender),
|
sender_address: Some(sender),
|
||||||
receive_address: Some(receiver),
|
receive_address: Some(receiver),
|
||||||
value: None,
|
value: None,
|
||||||
@ -361,7 +362,7 @@ fn call_static() {
|
|||||||
code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
|
code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert_eq!(gas_left, U256::from(94144));
|
assert_eq!(gas_left, U256::from(94262));
|
||||||
|
|
||||||
// siphash result
|
// siphash result
|
||||||
let res = LittleEndian::read_u32(&result[..]);
|
let res = LittleEndian::read_u32(&result[..]);
|
||||||
@ -387,7 +388,7 @@ fn realloc() {
|
|||||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert_eq!(gas_left, U256::from(99432));
|
assert_eq!(gas_left, U256::from(99522));
|
||||||
assert_eq!(result, vec![0u8; 2]);
|
assert_eq!(result, vec![0u8; 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,12 +414,15 @@ fn storage_read() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(99682));
|
assert_eq!(gas_left, U256::from(99800));
|
||||||
assert_eq!(Address::from(&result[12..32]), address);
|
assert_eq!(Address::from(&result[12..32]), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reqrep_test {
|
macro_rules! reqrep_test {
|
||||||
($name: expr, $input: expr) => {
|
($name: expr, $input: expr) => {
|
||||||
|
reqrep_test!($name, $input, vm::EnvInfo::default(), HashMap::new())
|
||||||
|
};
|
||||||
|
($name: expr, $input: expr, $info: expr, $block_hashes: expr) => {
|
||||||
{
|
{
|
||||||
::ethcore_logger::init_log();
|
::ethcore_logger::init_log();
|
||||||
let code = load_sample!($name);
|
let code = load_sample!($name);
|
||||||
@ -428,9 +432,13 @@ macro_rules! reqrep_test {
|
|||||||
params.code = Some(Arc::new(code));
|
params.code = Some(Arc::new(code));
|
||||||
params.data = Some($input);
|
params.data = Some($input);
|
||||||
|
|
||||||
|
let mut fake_ext = FakeExt::new();
|
||||||
|
fake_ext.info = $info;
|
||||||
|
fake_ext.blockhashes = $block_hashes;
|
||||||
|
|
||||||
let (gas_left, result) = {
|
let (gas_left, result) = {
|
||||||
let mut interpreter = wasm_interpreter();
|
let mut interpreter = wasm_interpreter();
|
||||||
let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors");
|
let result = interpreter.exec(params, &mut fake_ext).expect("Interpreter to execute without any errors");
|
||||||
match result {
|
match result {
|
||||||
GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); },
|
GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); },
|
||||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||||
@ -439,7 +447,7 @@ macro_rules! reqrep_test {
|
|||||||
|
|
||||||
(gas_left, result)
|
(gas_left, result)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// math_* tests check the ability of wasm contract to perform big integer operations
|
// math_* tests check the ability of wasm contract to perform big integer operations
|
||||||
@ -464,7 +472,7 @@ fn math_add() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(98087));
|
assert_eq!(gas_left, U256::from(98177));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
@ -486,7 +494,7 @@ fn math_mul() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(97236));
|
assert_eq!(gas_left, U256::from(97326));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
@ -508,7 +516,7 @@ fn math_sub() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(98131));
|
assert_eq!(gas_left, U256::from(98221));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
@ -529,9 +537,97 @@ fn math_div() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(91420));
|
assert_eq!(gas_left, U256::from(91510));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
U256::from_dec_str("1125000").unwrap(),
|
U256::from_dec_str("1125000").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test checks the ability of wasm contract to invoke
|
||||||
|
// varios blockchain runtime methods
|
||||||
|
#[test]
|
||||||
|
fn externs() {
|
||||||
|
let (gas_left, result) = reqrep_test!(
|
||||||
|
"externs.wasm",
|
||||||
|
Vec::new(),
|
||||||
|
vm::EnvInfo {
|
||||||
|
number: 0x9999999999u64.into(),
|
||||||
|
author: "efefefefefefefefefefefefefefefefefefefef".parse().unwrap(),
|
||||||
|
timestamp: 0x8888888888u64.into(),
|
||||||
|
difficulty: H256::from("0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0d1d2d3d4d5d6d7d8d9dadbdcdddedfd").into(),
|
||||||
|
gas_limit: 0x777777777777u64.into(),
|
||||||
|
last_hashes: Default::default(),
|
||||||
|
gas_used: 0.into(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let mut hashes = HashMap::new();
|
||||||
|
hashes.insert(
|
||||||
|
U256::from(0),
|
||||||
|
H256::from("9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d")
|
||||||
|
);
|
||||||
|
hashes.insert(
|
||||||
|
U256::from(1),
|
||||||
|
H256::from("7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b")
|
||||||
|
);
|
||||||
|
hashes
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[0..64].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,
|
||||||
|
0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
|
||||||
|
],
|
||||||
|
"Block hashes requested and returned do not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[64..84].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef,
|
||||||
|
],
|
||||||
|
"Coinbase requested and returned does not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[84..92].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00
|
||||||
|
],
|
||||||
|
"Timestamp requested and returned does not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[92..100].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0x99, 0x99, 0x99, 0x99, 0x99, 0x00, 0x00, 0x00
|
||||||
|
],
|
||||||
|
"Block number requested and returned does not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[100..132].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f,
|
||||||
|
0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
|
||||||
|
0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d,
|
||||||
|
0x8d, 0x9d, 0xad, 0xbd, 0xcd, 0xdd, 0xed, 0xfd,
|
||||||
|
],
|
||||||
|
"Difficulty requested and returned does not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result[132..164].to_vec(),
|
||||||
|
&vec![
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
|
||||||
|
],
|
||||||
|
"Gas limit requested and returned does not match"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(97588));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user