Merge branch 'master' into gav
This commit is contained in:
commit
4ca8fde530
@ -12,7 +12,7 @@ env_logger = "0.3"
|
|||||||
ethcore-util = { path = "../ethcore-util" }
|
ethcore-util = { path = "../ethcore-util" }
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
flate2 = "0.2"
|
flate2 = "0.2"
|
||||||
rocksdb = "0.2.1"
|
rocksdb = "0.2"
|
||||||
heapsize = "0.2.0"
|
heapsize = "0.2.0"
|
||||||
rust-crypto = "0.2.34"
|
rust-crypto = "0.2.34"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
@ -20,4 +20,5 @@ time = "0.1"
|
|||||||
evmjit = { path = "rust-evmjit", optional = true }
|
evmjit = { path = "rust-evmjit", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["jit"]
|
||||||
jit = ["evmjit"]
|
jit = ["evmjit"]
|
||||||
|
@ -3,6 +3,8 @@ name = "evmjit"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["dylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2.2"
|
|
||||||
tiny-keccak = "1.0"
|
tiny-keccak = "1.0"
|
||||||
|
@ -7,27 +7,52 @@
|
|||||||
//! use evmjit::*;
|
//! use evmjit::*;
|
||||||
//!
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty());
|
//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), ExtHandle::empty());
|
||||||
//! assert_eq!(context.exec(), ReturnCode::Stop);
|
//! assert_eq!(context.exec(), ReturnCode::Stop);
|
||||||
//! }
|
//! }
|
||||||
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//!
|
||||||
|
//! To verify that c abi is "imported" correctly, run:
|
||||||
|
//!
|
||||||
|
//! ```bash
|
||||||
|
//! nm your_executable -g | grep ext
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! It should give the following output:
|
||||||
|
//!
|
||||||
|
//! ```bash
|
||||||
|
//! 00000001000779e0 T _ext_balance
|
||||||
|
//! 0000000100077a10 T _ext_blockhash
|
||||||
|
//! 0000000100077a90 T _ext_call
|
||||||
|
//! 0000000100077a40 T _ext_create
|
||||||
|
//! 0000000100077b50 T _ext_extcode
|
||||||
|
//! 0000000100077b80 T _ext_log
|
||||||
|
//! 0000000100077b20 T _ext_sha3
|
||||||
|
//! 0000000100077980 T _ext_sload
|
||||||
|
//! 00000001000779b0 T _ext_sstore
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
extern crate libc;
|
|
||||||
extern crate tiny_keccak;
|
extern crate tiny_keccak;
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use self::ffi::*;
|
use self::ffi::*;
|
||||||
|
|
||||||
pub use self::ffi::JitReturnCode as ReturnCode;
|
pub use self::ffi::JitReturnCode as ReturnCode;
|
||||||
|
pub use self::ffi::JitI256 as I256;
|
||||||
|
pub use self::ffi::JitH256 as H256;
|
||||||
|
|
||||||
/// Component oriented safe handle to `JitRuntimeData`.
|
/// Takes care of proper initialization and destruction of `RuntimeData`.
|
||||||
|
///
|
||||||
|
/// This handle must be used to create runtime data,
|
||||||
|
/// cause underneath it's a `C++` structure. Incombatible with rust
|
||||||
|
/// structs.
|
||||||
pub struct RuntimeDataHandle {
|
pub struct RuntimeDataHandle {
|
||||||
runtime_data: *mut JitRuntimeData
|
runtime_data: *mut JitRuntimeData
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeDataHandle {
|
impl RuntimeDataHandle {
|
||||||
/// Creates new handle.
|
/// Creates new `RuntimeData` handle.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
RuntimeDataHandle {
|
RuntimeDataHandle {
|
||||||
runtime_data: unsafe { evmjit_create_runtime_data() }
|
runtime_data: unsafe { evmjit_create_runtime_data() }
|
||||||
@ -51,26 +76,61 @@ impl Drop for RuntimeDataHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safe handle for jit context.
|
impl Deref for RuntimeDataHandle {
|
||||||
|
type Target = JitRuntimeData;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.runtime_data()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for RuntimeDataHandle {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.mut_runtime_data()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes care of proper initialization and destruction of jit context.
|
||||||
|
///
|
||||||
|
/// This handle must be used to create context,
|
||||||
|
/// cause underneath it's a `C++` structure. Incombatible with rust
|
||||||
|
/// structs.
|
||||||
pub struct ContextHandle {
|
pub struct ContextHandle {
|
||||||
context: *mut JitContext,
|
context: *mut JitContext,
|
||||||
_data_handle: RuntimeDataHandle
|
data_handle: RuntimeDataHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextHandle {
|
impl ContextHandle {
|
||||||
/// Creates new context handle.
|
/// Creates new context handle.
|
||||||
pub fn new(mut data_handle: RuntimeDataHandle, mut env: EnvHandle) -> Self {
|
///
|
||||||
let context = unsafe { evmjit_create_context(data_handle.mut_runtime_data(), &mut env) };
|
/// This function is unsafe cause ext lifetime is not considered
|
||||||
ContextHandle {
|
/// We also can't make ExtHandle a member of `ContextHandle` structure,
|
||||||
context: context,
|
/// cause this would be a move operation or it would require a template
|
||||||
_data_handle: data_handle
|
/// lifetime to a reference. Both solutions are not possible.
|
||||||
}
|
pub unsafe fn new(data_handle: RuntimeDataHandle, ext: &mut ExtHandle) -> Self {
|
||||||
|
let mut handle = ContextHandle {
|
||||||
|
context: std::mem::uninitialized(),
|
||||||
|
data_handle: data_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
handle.context = evmjit_create_context(handle.data_handle.mut_runtime_data(), ext);
|
||||||
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes context.
|
/// Executes context.
|
||||||
pub fn exec(&mut self) -> JitReturnCode {
|
pub fn exec(&mut self) -> JitReturnCode {
|
||||||
unsafe { evmjit_exec(self.context) }
|
unsafe { evmjit_exec(self.context) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns output data.
|
||||||
|
pub fn output_data(&self) -> &[u8] {
|
||||||
|
unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns gas left.
|
||||||
|
pub fn gas_left(&self) -> u64 {
|
||||||
|
self.data_handle.gas as u64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ContextHandle {
|
impl Drop for ContextHandle {
|
||||||
@ -79,76 +139,76 @@ impl Drop for ContextHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Component oriented wrapper around jit env c interface.
|
/// Component oriented wrapper around jit ext c interface.
|
||||||
pub trait Env {
|
pub trait Ext {
|
||||||
fn sload(&self, index: *const JitI256, out_value: *mut JitI256);
|
fn sload(&self, index: *const JitI256, out_value: *mut JitI256);
|
||||||
fn sstore(&mut self, index: *const JitI256, value: *const JitI256);
|
fn sstore(&mut self, index: *const JitI256, value: *const JitI256);
|
||||||
fn balance(&self, address: *const JitI256, out_value: *mut JitI256);
|
fn balance(&self, address: *const JitH256, out_value: *mut JitI256);
|
||||||
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitI256);
|
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitH256);
|
||||||
|
|
||||||
fn create(&mut self,
|
fn create(&mut self,
|
||||||
io_gas: *mut u64,
|
io_gas: *mut u64,
|
||||||
endowment: *const JitI256,
|
endowment: *const JitI256,
|
||||||
init_beg: *const u8,
|
init_beg: *const u8,
|
||||||
init_size: *const u64,
|
init_size: u64,
|
||||||
address: *mut JitI256);
|
address: *mut JitH256);
|
||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
io_gas: *mut u64,
|
io_gas: *mut u64,
|
||||||
call_gas: *const u64,
|
call_gas: u64,
|
||||||
receive_address: *const JitI256,
|
receive_address: *const JitH256,
|
||||||
value: *const JitI256,
|
value: *const JitI256,
|
||||||
in_beg: *const u8,
|
in_beg: *const u8,
|
||||||
in_size: *const u64,
|
in_size: u64,
|
||||||
out_beg: *mut u8,
|
out_beg: *mut u8,
|
||||||
out_size: *mut u64,
|
out_size: u64,
|
||||||
code_address: JitI256) -> bool;
|
code_address: *const JitH256) -> bool;
|
||||||
|
|
||||||
fn log(&mut self,
|
fn log(&mut self,
|
||||||
beg: *const u8,
|
beg: *const u8,
|
||||||
size: *const u64,
|
size: u64,
|
||||||
topic1: *const JitI256,
|
topic1: *const JitH256,
|
||||||
topic2: *const JitI256,
|
topic2: *const JitH256,
|
||||||
topic3: *const JitI256,
|
topic3: *const JitH256,
|
||||||
topic4: *const JitI256);
|
topic4: *const JitH256);
|
||||||
|
|
||||||
fn extcode(&self, address: *const JitI256, size: *mut u64) -> *const u8;
|
fn extcode(&self, address: *const JitH256, size: *mut u64) -> *const u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// C abi compatible wrapper for jit env implementers.
|
/// C abi compatible wrapper for jit ext implementers.
|
||||||
pub struct EnvHandle {
|
pub struct ExtHandle {
|
||||||
env_impl: Option<Box<Env>>
|
ext_impl: Option<Box<Ext>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnvHandle {
|
impl ExtHandle {
|
||||||
/// Creates new environment wrapper for given implementation
|
/// Creates new extironment wrapper for given implementation
|
||||||
pub fn new<T>(env_impl: T) -> Self where T: Env + 'static {
|
pub fn new<T>(ext_impl: T) -> Self where T: Ext + 'static {
|
||||||
EnvHandle { env_impl: Some(Box::new(env_impl)) }
|
ExtHandle { ext_impl: Some(Box::new(ext_impl)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates empty environment.
|
/// Creates empty extironment.
|
||||||
/// It can be used to for any operations.
|
/// It can be used to for any operations.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
EnvHandle { env_impl: None }
|
ExtHandle { ext_impl: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for EnvHandle {
|
impl Deref for ExtHandle {
|
||||||
type Target = Box<Env>;
|
type Target = Box<Ext>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match self.env_impl {
|
match self.ext_impl {
|
||||||
Some(ref env) => env,
|
Some(ref ext) => ext,
|
||||||
None => { panic!(); }
|
None => { panic!("Handle is empty!"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for EnvHandle {
|
impl DerefMut for ExtHandle {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
match self.env_impl {
|
match self.ext_impl {
|
||||||
Some(ref mut env) => env,
|
Some(ref mut ext) => ext,
|
||||||
None => { panic!(); }
|
None => { panic!("Handle is empty!"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +216,7 @@ impl DerefMut for EnvHandle {
|
|||||||
/// ffi functions
|
/// ffi functions
|
||||||
pub mod ffi {
|
pub mod ffi {
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use libc;
|
use std::mem;
|
||||||
use tiny_keccak::Keccak;
|
use tiny_keccak::Keccak;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -178,17 +238,50 @@ pub mod ffi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
/// Signed 256 bit integer.
|
/// Signed 256 bit integer.
|
||||||
pub struct JitI256 {
|
pub struct JitI256 {
|
||||||
pub words: [u64; 4]
|
pub words: [u64; 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
/// Jit Hash
|
||||||
|
pub struct JitH256 {
|
||||||
|
pub words: [u64; 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JitH256> for JitI256 {
|
||||||
|
fn from(mut hash: JitH256) -> JitI256 {
|
||||||
|
unsafe {
|
||||||
|
{
|
||||||
|
let bytes: &mut [u8] = slice::from_raw_parts_mut(hash.words.as_mut_ptr() as *mut u8, 32);
|
||||||
|
bytes.reverse();
|
||||||
|
}
|
||||||
|
mem::transmute(hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JitI256> for JitH256 {
|
||||||
|
fn from(mut i: JitI256) -> JitH256 {
|
||||||
|
unsafe {
|
||||||
|
{
|
||||||
|
let bytes: &mut [u8] = slice::from_raw_parts_mut(i.words.as_mut_ptr() as *mut u8, 32);
|
||||||
|
bytes.reverse();
|
||||||
|
}
|
||||||
|
mem::transmute(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
/// Jit runtime data.
|
/// Jit runtime data.
|
||||||
pub struct JitRuntimeData {
|
pub struct JitRuntimeData {
|
||||||
pub gas: i64,
|
pub gas: i64,
|
||||||
pub gas_price: i64,
|
pub gas_price: i64,
|
||||||
pub call_data: *const libc::c_char,
|
pub call_data: *const u8,
|
||||||
pub call_data_size: u64,
|
pub call_data_size: u64,
|
||||||
pub address: JitI256,
|
pub address: JitI256,
|
||||||
pub caller: JitI256,
|
pub caller: JitI256,
|
||||||
@ -199,88 +292,88 @@ pub mod ffi {
|
|||||||
pub gas_limit: JitI256,
|
pub gas_limit: JitI256,
|
||||||
pub number: u64,
|
pub number: u64,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub code: *const libc::c_char,
|
pub code: *const u8,
|
||||||
pub code_size: u64,
|
pub code_size: u64,
|
||||||
pub code_hash: JitI256
|
pub code_hash: JitI256
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_sload(env: *const EnvHandle, index: *const JitI256, out_value: *mut JitI256) {
|
pub unsafe extern "C" fn env_sload(ext: *const ExtHandle, index: *const JitI256, out_value: *mut JitI256) {
|
||||||
let env = &*env;
|
let ext = &*ext;
|
||||||
env.sload(index, out_value);
|
ext.sload(index, out_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_sstore(env: *mut EnvHandle, index: *const JitI256, value: *const JitI256) {
|
pub unsafe extern "C" fn env_sstore(ext: *mut ExtHandle, index: *mut JitI256, value: *mut JitI256) {
|
||||||
let env = &mut *env;
|
let ext = &mut *ext;
|
||||||
env.sstore(index, value);
|
ext.sstore(index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_balance(env: *const EnvHandle, address: *const JitI256, out_value: *mut JitI256) {
|
pub unsafe extern "C" fn env_balance(ext: *const ExtHandle, address: *const JitH256, out_value: *mut JitI256) {
|
||||||
let env = &*env;
|
let ext = &*ext;
|
||||||
env.balance(address, out_value);
|
ext.balance(address, out_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_blockhash(env: *const EnvHandle, number: *const JitI256, out_hash: *mut JitI256) {
|
pub unsafe extern "C" fn env_blockhash(ext: *const ExtHandle, number: *const JitI256, out_hash: *mut JitH256) {
|
||||||
let env = &*env;
|
let ext = &*ext;
|
||||||
env.blockhash(number, out_hash);
|
ext.blockhash(number, out_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_create(env: *mut EnvHandle,
|
pub unsafe extern "C" fn env_create(ext: *mut ExtHandle,
|
||||||
io_gas: *mut u64,
|
io_gas: *mut u64,
|
||||||
endowment: *const JitI256,
|
endowment: *const JitI256,
|
||||||
init_beg: *const u8,
|
init_beg: *const u8,
|
||||||
init_size: *const u64,
|
init_size: u64,
|
||||||
address: *mut JitI256) {
|
address: *mut JitH256) {
|
||||||
let env = &mut *env;
|
let ext = &mut *ext;
|
||||||
env.create(io_gas, endowment, init_beg, init_size, address);
|
ext.create(io_gas, endowment, init_beg, init_size, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_call(env: *mut EnvHandle,
|
pub unsafe extern "C" fn env_call(ext: *mut ExtHandle,
|
||||||
io_gas: *mut u64,
|
io_gas: *mut u64,
|
||||||
call_gas: *const u64,
|
call_gas: u64,
|
||||||
receive_address: *const JitI256,
|
receive_address: *const JitH256,
|
||||||
value: *const JitI256,
|
value: *const JitI256,
|
||||||
in_beg: *const u8,
|
in_beg: *const u8,
|
||||||
in_size: *const u64,
|
in_size: u64,
|
||||||
out_beg: *mut u8,
|
out_beg: *mut u8,
|
||||||
out_size: *mut u64,
|
out_size: u64,
|
||||||
code_address: JitI256) -> bool {
|
code_address: *const JitH256) -> bool {
|
||||||
let env = &mut *env;
|
let ext = &mut *ext;
|
||||||
env.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
|
ext.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_sha3(begin: *const u8, size: *const u64, out_hash: *mut JitI256) {
|
pub unsafe extern "C" fn env_sha3(begin: *const u8, size: u64, out_hash: *mut JitH256) {
|
||||||
let out_hash = &mut *out_hash;
|
let out_hash = &mut *out_hash;
|
||||||
let input = slice::from_raw_parts(begin, *size as usize);
|
let input = slice::from_raw_parts(begin, size as usize);
|
||||||
let outlen = out_hash.words.len() * 8;
|
let outlen = out_hash.words.len() * 8;
|
||||||
let output = slice::from_raw_parts_mut(out_hash.words.as_mut_ptr() as *mut u8, outlen);
|
let output = slice::from_raw_parts_mut(out_hash.words.as_mut_ptr() as *mut u8, outlen);
|
||||||
let mut sha3 = Keccak::new_sha3_256();
|
let mut sha3 = Keccak::new_keccak256();
|
||||||
sha3.update(input);
|
sha3.update(input);
|
||||||
sha3.finalize(output);
|
sha3.finalize(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_extcode(env: *const EnvHandle, address: *const JitI256, size: *mut u64) -> *const u8 {
|
pub unsafe extern "C" fn env_extcode(ext: *const ExtHandle, address: *const JitH256, size: *mut u64) -> *const u8 {
|
||||||
let env = &*env;
|
let ext = &*ext;
|
||||||
env.extcode(address, size)
|
ext.extcode(address, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn env_log(env: *mut EnvHandle,
|
pub unsafe extern "C" fn env_log(ext: *mut ExtHandle,
|
||||||
beg: *const u8,
|
beg: *const u8,
|
||||||
size: *const u64,
|
size: u64,
|
||||||
topic1: *const JitI256,
|
topic1: *const JitH256,
|
||||||
topic2: *const JitI256,
|
topic2: *const JitH256,
|
||||||
topic3: *const JitI256,
|
topic3: *const JitH256,
|
||||||
topic4: *const JitI256) {
|
topic4: *const JitH256) {
|
||||||
let env = &mut *env;
|
let ext = &mut *ext;
|
||||||
env.log(beg, size, topic1, topic2, topic3, topic4);
|
ext.log(beg, size, topic1, topic2, topic3, topic4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -292,11 +385,11 @@ pub mod ffi {
|
|||||||
pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode;
|
pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtHandle is not a C type, so we need to allow "improper_ctypes"
|
||||||
#[link(name="evmjit")]
|
#[link(name="evmjit")]
|
||||||
// EnvHandle does not have to by a C type
|
|
||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn evmjit_create_context(data: *mut JitRuntimeData, env: *mut EnvHandle) -> *mut JitContext;
|
pub fn evmjit_create_context(data: *mut JitRuntimeData, ext: *mut ExtHandle) -> *mut JitContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +397,7 @@ pub mod ffi {
|
|||||||
fn ffi_test() {
|
fn ffi_test() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let data = evmjit_create_runtime_data();
|
let data = evmjit_create_runtime_data();
|
||||||
let context = evmjit_create_context(data, &mut EnvHandle::empty());
|
let context = evmjit_create_context(data, &mut ExtHandle::empty());
|
||||||
|
|
||||||
let code = evmjit_exec(context);
|
let code = evmjit_exec(context);
|
||||||
assert_eq!(code, JitReturnCode::Stop);
|
assert_eq!(code, JitReturnCode::Stop);
|
||||||
@ -316,6 +409,16 @@ fn ffi_test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handle_test() {
|
fn handle_test() {
|
||||||
let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty());
|
unsafe {
|
||||||
|
let mut context = ContextHandle::new(RuntimeDataHandle::new(), &mut ExtHandle::empty());
|
||||||
assert_eq!(context.exec(), ReturnCode::Stop);
|
assert_eq!(context.exec(), ReturnCode::Stop);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hash_to_int() {
|
||||||
|
let h = H256 { words:[0x0123456789abcdef, 0, 0, 0] };
|
||||||
|
let i = I256::from(h);
|
||||||
|
assert_eq!([0u64, 0, 0, 0xefcdab8967452301], i.words);
|
||||||
|
assert_eq!(H256::from(i).words, h.words);
|
||||||
|
}
|
||||||
|
42
src/action_params.rs
Normal file
42
src/action_params.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//! Evm input params.
|
||||||
|
use util::hash::*;
|
||||||
|
use util::uint::*;
|
||||||
|
use util::bytes::*;
|
||||||
|
|
||||||
|
// 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 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: U256,
|
||||||
|
/// Code being executed.
|
||||||
|
pub code: Bytes,
|
||||||
|
/// Input data.
|
||||||
|
pub data: Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionParams {
|
||||||
|
pub fn new() -> ActionParams {
|
||||||
|
ActionParams {
|
||||||
|
address: Address::new(),
|
||||||
|
sender: Address::new(),
|
||||||
|
origin: Address::new(),
|
||||||
|
gas: U256::zero(),
|
||||||
|
gas_price: U256::zero(),
|
||||||
|
value: U256::zero(),
|
||||||
|
code: vec![],
|
||||||
|
data: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -142,7 +142,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
/// that the header itself is actually valid.
|
/// that the header itself is actually valid.
|
||||||
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
|
||||||
if self.block.uncles.len() >= self.engine.maximum_uncle_count() {
|
if self.block.uncles.len() >= self.engine.maximum_uncle_count() {
|
||||||
return Err(BlockError::TooManyUncles);
|
return Err(BlockError::TooManyUncles(OutOfBounds{min: 0, max: self.engine.maximum_uncle_count(), found: self.block.uncles.len()}));
|
||||||
}
|
}
|
||||||
// TODO: check number
|
// TODO: check number
|
||||||
// TODO: check not a direct ancestor (use last_hashes for that)
|
// TODO: check not a direct ancestor (use last_hashes for that)
|
||||||
@ -170,12 +170,12 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
|
|||||||
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
|
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
|
||||||
let env_info = self.env_info();
|
let env_info = self.env_info();
|
||||||
match self.block.state.apply(&env_info, self.engine, &t) {
|
match self.block.state.apply(&env_info, self.engine, &t) {
|
||||||
Ok(x) => {
|
Ok(receipt) => {
|
||||||
self.block.archive_set.insert(h.unwrap_or_else(||t.hash()));
|
self.block.archive_set.insert(h.unwrap_or_else(||t.hash()));
|
||||||
self.block.archive.push(Entry { transaction: t, receipt: x.receipt });
|
self.block.archive.push(Entry { transaction: t, receipt: receipt });
|
||||||
Ok(&self.block.archive.last().unwrap().receipt)
|
Ok(&self.block.archive.last().unwrap().receipt)
|
||||||
}
|
}
|
||||||
Err(x) => Err(x)
|
Err(x) => Err(From::from(x))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use error::*;
|
|||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
|
use queue::BlockQueue;
|
||||||
|
|
||||||
/// General block status
|
/// General block status
|
||||||
pub enum BlockStatus {
|
pub enum BlockStatus {
|
||||||
@ -18,9 +19,6 @@ pub enum BlockStatus {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of import block operation.
|
|
||||||
pub type ImportResult = Result<(), ImportError>;
|
|
||||||
|
|
||||||
/// Information about the blockchain gthered together.
|
/// Information about the blockchain gthered together.
|
||||||
pub struct BlockChainInfo {
|
pub struct BlockChainInfo {
|
||||||
/// Blockchain difficulty.
|
/// Blockchain difficulty.
|
||||||
@ -95,28 +93,30 @@ pub trait BlockChainClient : Sync {
|
|||||||
|
|
||||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
chain: Arc<BlockChain>,
|
chain: Arc<RwLock<BlockChain>>,
|
||||||
_engine: Arc<Box<Engine>>,
|
_engine: Arc<Box<Engine>>,
|
||||||
|
queue: BlockQueue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn new(spec: Spec, path: &Path) -> Result<Client, Error> {
|
pub fn new(spec: Spec, path: &Path) -> Result<Client, Error> {
|
||||||
let chain = Arc::new(BlockChain::new(&spec.genesis_block(), path));
|
let chain = Arc::new(RwLock::new(BlockChain::new(&spec.genesis_block(), path)));
|
||||||
let engine = Arc::new(try!(spec.to_engine()));
|
let engine = Arc::new(try!(spec.to_engine()));
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
chain: chain.clone(),
|
chain: chain.clone(),
|
||||||
_engine: engine,
|
_engine: engine.clone(),
|
||||||
|
queue: BlockQueue::new(chain.clone(), engine.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockChainClient for Client {
|
impl BlockChainClient for Client {
|
||||||
fn block_header(&self, hash: &H256) -> Option<Bytes> {
|
fn block_header(&self, hash: &H256) -> Option<Bytes> {
|
||||||
self.chain.block(hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())
|
self.chain.read().unwrap().block(hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_body(&self, hash: &H256) -> Option<Bytes> {
|
fn block_body(&self, hash: &H256) -> Option<Bytes> {
|
||||||
self.chain.block(hash).map(|bytes| {
|
self.chain.read().unwrap().block(hash).map(|bytes| {
|
||||||
let rlp = Rlp::new(&bytes);
|
let rlp = Rlp::new(&bytes);
|
||||||
let mut body = RlpStream::new();
|
let mut body = RlpStream::new();
|
||||||
body.append_raw(rlp.at(1).as_raw(), 1);
|
body.append_raw(rlp.at(1).as_raw(), 1);
|
||||||
@ -126,34 +126,34 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block(&self, hash: &H256) -> Option<Bytes> {
|
fn block(&self, hash: &H256) -> Option<Bytes> {
|
||||||
self.chain.block(hash)
|
self.chain.read().unwrap().block(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_status(&self, hash: &H256) -> BlockStatus {
|
fn block_status(&self, hash: &H256) -> BlockStatus {
|
||||||
if self.chain.is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
|
if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
|
||||||
self.chain.block_hash(n).and_then(|h| self.block_header(&h))
|
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_body_at(&self, n: BlockNumber) -> Option<Bytes> {
|
fn block_body_at(&self, n: BlockNumber) -> Option<Bytes> {
|
||||||
self.chain.block_hash(n).and_then(|h| self.block_body(&h))
|
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_body(&h))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_at(&self, n: BlockNumber) -> Option<Bytes> {
|
fn block_at(&self, n: BlockNumber) -> Option<Bytes> {
|
||||||
self.chain.block_hash(n).and_then(|h| self.block(&h))
|
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block(&h))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_status_at(&self, n: BlockNumber) -> BlockStatus {
|
fn block_status_at(&self, n: BlockNumber) -> BlockStatus {
|
||||||
match self.chain.block_hash(n) {
|
match self.chain.read().unwrap().block_hash(n) {
|
||||||
Some(h) => self.block_status(&h),
|
Some(h) => self.block_status(&h),
|
||||||
None => BlockStatus::Unknown
|
None => BlockStatus::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||||
self.chain.tree_route(from.clone(), to.clone())
|
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state_data(&self, _hash: &H256) -> Option<Bytes> {
|
fn state_data(&self, _hash: &H256) -> Option<Bytes> {
|
||||||
@ -165,17 +165,7 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
|
fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
|
||||||
//TODO: verify block
|
self.queue.import_block(bytes)
|
||||||
{
|
|
||||||
let block = BlockView::new(bytes);
|
|
||||||
let header = block.header_view();
|
|
||||||
let hash = header.sha3();
|
|
||||||
if self.chain.is_known(&hash) {
|
|
||||||
return Err(ImportError::AlreadyInChain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.chain.insert_block(bytes);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_status(&self) -> BlockQueueStatus {
|
fn queue_status(&self) -> BlockQueueStatus {
|
||||||
@ -188,12 +178,13 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
|
let chain = self.chain.read().unwrap();
|
||||||
BlockChainInfo {
|
BlockChainInfo {
|
||||||
total_difficulty: self.chain.best_block_total_difficulty(),
|
total_difficulty: chain.best_block_total_difficulty(),
|
||||||
pending_total_difficulty: self.chain.best_block_total_difficulty(),
|
pending_total_difficulty: chain.best_block_total_difficulty(),
|
||||||
genesis_hash: self.chain.genesis_hash(),
|
genesis_hash: chain.genesis_hash(),
|
||||||
best_block_hash: self.chain.best_block_hash(),
|
best_block_hash: chain.best_block_hash(),
|
||||||
best_block_number: From::from(self.chain.best_block_number())
|
best_block_number: From::from(chain.best_block_number())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ pub use util::*;
|
|||||||
pub use basic_types::*;
|
pub use basic_types::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use env_info::*;
|
pub use env_info::*;
|
||||||
pub use evm_schedule::*;
|
|
||||||
pub use views::*;
|
pub use views::*;
|
||||||
pub use builtin::*;
|
pub use builtin::*;
|
||||||
pub use header::*;
|
pub use header::*;
|
||||||
@ -10,3 +9,4 @@ pub use account::*;
|
|||||||
pub use transaction::*;
|
pub use transaction::*;
|
||||||
pub use log_entry::*;
|
pub use log_entry::*;
|
||||||
pub use receipt::*;
|
pub use receipt::*;
|
||||||
|
pub use action_params::*;
|
@ -1,6 +1,7 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use block::Block;
|
use block::Block;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
|
use evm::Schedule;
|
||||||
|
|
||||||
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
|
||||||
/// Provides hooks into each of the major parts of block import.
|
/// Provides hooks into each of the major parts of block import.
|
||||||
@ -22,7 +23,7 @@ pub trait Engine : Sync + Send {
|
|||||||
fn spec(&self) -> &Spec;
|
fn spec(&self) -> &Spec;
|
||||||
|
|
||||||
/// Get the EVM schedule for the given `env_info`.
|
/// Get the EVM schedule for the given `env_info`.
|
||||||
fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule;
|
fn schedule(&self, env_info: &EnvInfo) -> Schedule;
|
||||||
|
|
||||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||||
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||||
@ -33,11 +34,18 @@ pub trait Engine : Sync + Send {
|
|||||||
fn on_new_block(&self, _block: &mut Block) {}
|
fn on_new_block(&self, _block: &mut Block) {}
|
||||||
fn on_close_block(&self, _block: &mut Block) {}
|
fn on_close_block(&self, _block: &mut Block) {}
|
||||||
|
|
||||||
/// Verify that `header` is valid.
|
// TODO: consider including State in the params for verification functions.
|
||||||
/// `parent` (the parent header) and `block` (the header's full block) may be provided for additional
|
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
|
||||||
/// checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||||
// TODO: consider including State in the params.
|
fn verify_block_basic(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||||
fn verify_block(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
|
||||||
|
/// Phase 2 verification. Perform costly checks such as transaction signatures. `block` (the header's full block)
|
||||||
|
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||||
|
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||||
|
|
||||||
|
/// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block)
|
||||||
|
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
|
||||||
|
fn verify_block_final(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
|
||||||
|
|
||||||
/// Additional verification for transactions in blocks.
|
/// Additional verification for transactions in blocks.
|
||||||
// TODO: Add flags for which bits of the transaction to check.
|
// TODO: Add flags for which bits of the transaction to check.
|
||||||
|
@ -22,3 +22,17 @@ pub struct EnvInfo {
|
|||||||
/// The gas used.
|
/// The gas used.
|
||||||
pub gas_used: U256,
|
pub gas_used: U256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EnvInfo {
|
||||||
|
pub fn new() -> EnvInfo {
|
||||||
|
EnvInfo {
|
||||||
|
number: 0,
|
||||||
|
author: Address::new(),
|
||||||
|
timestamp: 0,
|
||||||
|
difficulty: U256::zero(),
|
||||||
|
gas_limit: U256::zero(),
|
||||||
|
last_hashes: vec![],
|
||||||
|
gas_used: U256::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
57
src/error.rs
57
src/error.rs
@ -1,6 +1,7 @@
|
|||||||
//! General error types for use in ethcore.
|
//! General error types for use in ethcore.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
|
use header::BlockNumber;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mismatch<T: fmt::Debug> {
|
pub struct Mismatch<T: fmt::Debug> {
|
||||||
@ -15,27 +16,73 @@ pub struct OutOfBounds<T: fmt::Debug> {
|
|||||||
pub found: T,
|
pub found: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Result of executing the transaction.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum ExecutionError {
|
||||||
|
/// Returned when block (gas_used + gas) > gas_limit.
|
||||||
|
///
|
||||||
|
/// If gas =< gas_limit, upstream may try to execute the transaction
|
||||||
|
/// in next block.
|
||||||
|
BlockGasLimitReached { gas_limit: U256, gas_used: U256, gas: U256 },
|
||||||
|
/// Returned when transaction nonce does not match state nonce.
|
||||||
|
InvalidNonce { expected: U256, is: U256 },
|
||||||
|
/// Returned when cost of transaction (value + gas_price * gas) exceeds
|
||||||
|
/// current sender balance.
|
||||||
|
NotEnoughCash { required: U256, is: U256 },
|
||||||
|
/// Returned when internal evm error occurs.
|
||||||
|
Internal
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BlockError {
|
pub enum BlockError {
|
||||||
TooManyUncles,
|
TooManyUncles(OutOfBounds<usize>),
|
||||||
UncleWrongGeneration,
|
UncleWrongGeneration,
|
||||||
ExtraDataOutOfBounds(OutOfBounds<usize>),
|
ExtraDataOutOfBounds(OutOfBounds<usize>),
|
||||||
InvalidSealArity(Mismatch<usize>),
|
InvalidSealArity(Mismatch<usize>),
|
||||||
|
TooMuchGasUsed(OutOfBounds<U256>),
|
||||||
|
InvalidUnclesHash(Mismatch<H256>),
|
||||||
|
UncleTooOld(OutOfBounds<BlockNumber>),
|
||||||
|
UncleIsBrother(OutOfBounds<BlockNumber>),
|
||||||
|
UncleInChain(H256),
|
||||||
|
UncleParentNotInChain(H256),
|
||||||
|
InvalidStateRoot,
|
||||||
|
InvalidGasUsed,
|
||||||
|
InvalidTransactionsRoot(Mismatch<H256>),
|
||||||
|
InvalidDifficulty(Mismatch<U256>),
|
||||||
|
InvalidGasLimit(OutOfBounds<U256>),
|
||||||
|
InvalidReceiptsStateRoot,
|
||||||
|
InvalidTimestamp(OutOfBounds<u64>),
|
||||||
|
InvalidLogBloom,
|
||||||
|
InvalidBlockNonce,
|
||||||
|
InvalidParentHash(Mismatch<H256>),
|
||||||
|
InvalidNumber(OutOfBounds<BlockNumber>),
|
||||||
|
UnknownParent(H256),
|
||||||
|
UnknownUncleParent(H256),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ImportError {
|
pub enum ImportError {
|
||||||
Bad(BlockError),
|
Bad(Error),
|
||||||
AlreadyInChain,
|
AlreadyInChain,
|
||||||
AlreadyQueued,
|
AlreadyQueued,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Error> for ImportError {
|
||||||
|
fn from(err: Error) -> ImportError {
|
||||||
|
ImportError::Bad(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of import block operation.
|
||||||
|
pub type ImportResult = Result<(), ImportError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// General error type which should be capable of representing all errors in ethcore.
|
/// General error type which should be capable of representing all errors in ethcore.
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Util(UtilError),
|
Util(UtilError),
|
||||||
Block(BlockError),
|
Block(BlockError),
|
||||||
UnknownEngineName(String),
|
UnknownEngineName(String),
|
||||||
|
Execution(ExecutionError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BlockError> for Error {
|
impl From<BlockError> for Error {
|
||||||
@ -44,6 +91,12 @@ impl From<BlockError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ExecutionError> for Error {
|
||||||
|
fn from(err: ExecutionError) -> Error {
|
||||||
|
Error::Execution(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
|
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
|
||||||
/*#![feature(concat_idents)]
|
/*#![feature(concat_idents)]
|
||||||
macro_rules! assimilate {
|
macro_rules! assimilate {
|
||||||
|
@ -2,6 +2,7 @@ use common::*;
|
|||||||
use block::*;
|
use block::*;
|
||||||
use spec::*;
|
use spec::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
|
use evm::Schedule;
|
||||||
|
|
||||||
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
|
||||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
/// mainnet chains in the Olympic, Frontier and Homestead eras.
|
||||||
@ -26,7 +27,7 @@ impl Engine for Ethash {
|
|||||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||||
fn spec(&self) -> &Spec { &self.spec }
|
fn spec(&self) -> &Spec { &self.spec }
|
||||||
fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() }
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
|
||||||
|
|
||||||
/// Apply the block reward on finalisation of the block.
|
/// Apply the block reward on finalisation of the block.
|
||||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||||
@ -43,6 +44,83 @@ impl Engine for Ethash {
|
|||||||
fields.state.add_balance(u.author(), &(reward * U256::from((8 + u.number() - current_number) / 8)));
|
fields.state.add_balance(u.author(), &(reward * U256::from((8 + u.number() - current_number) / 8)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
if header.difficulty < min_difficulty {
|
||||||
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: min_difficulty, found: header.difficulty })))
|
||||||
|
}
|
||||||
|
let min_gas_limit = decode(self.spec().engine_params.get("minGasLimit").unwrap());
|
||||||
|
if header.gas_limit < min_gas_limit {
|
||||||
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas_limit, max: From::from(0), found: header.gas_limit })));
|
||||||
|
}
|
||||||
|
let maximum_extra_data_size = self.maximum_extra_data_size();
|
||||||
|
if header.number != 0 && header.extra_data.len() > maximum_extra_data_size {
|
||||||
|
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: 0, max: maximum_extra_data_size, found: header.extra_data.len() })));
|
||||||
|
}
|
||||||
|
// TODO: Verify seal (quick)
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_unordered(&self, _header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// TODO: Verify seal (full)
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_block_final(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||||
|
// Check difficulty is correct given the two timestamps.
|
||||||
|
let expected_difficulty = self.calculate_difficuty(header, parent);
|
||||||
|
if header.difficulty != expected_difficulty {
|
||||||
|
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty })))
|
||||||
|
}
|
||||||
|
let gas_limit_divisor = decode(self.spec().engine_params.get("gasLimitBoundDivisor").unwrap());
|
||||||
|
let min_gas = parent.gas_limit - parent.gas_limit / gas_limit_divisor;
|
||||||
|
let max_gas = parent.gas_limit + parent.gas_limit / gas_limit_divisor;
|
||||||
|
if header.gas_limit <= min_gas || header.gas_limit >= max_gas {
|
||||||
|
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: min_gas, max: max_gas, found: header.gas_limit })));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> result::Result<(), Error> { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ethash {
|
||||||
|
fn calculate_difficuty(&self, header: &Header, parent: &Header) -> U256 {
|
||||||
|
const EXP_DIFF_PERIOD: u64 = 100000;
|
||||||
|
if header.number == 0 {
|
||||||
|
panic!("Can't calculate genesis block difficulty");
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
|
||||||
|
let difficulty_bound_divisor = decode(self.spec().engine_params.get("difficultyBoundDivisor").unwrap());
|
||||||
|
let duration_limit: u64 = decode(self.spec().engine_params.get("durationLimit").unwrap());
|
||||||
|
let frontier_limit = decode(self.spec().engine_params.get("frontierCompatibilityModeLimit").unwrap());
|
||||||
|
let mut target = if header.number < frontier_limit {
|
||||||
|
if header.timestamp >= parent.timestamp + duration_limit {
|
||||||
|
parent.difficulty - (parent.difficulty / difficulty_bound_divisor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent.difficulty + (parent.difficulty / difficulty_bound_divisor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let diff_inc = (header.timestamp - parent.timestamp) / 10;
|
||||||
|
if diff_inc <= 1 {
|
||||||
|
parent.difficulty + parent.difficulty / From::from(2048) * From::from(1 - diff_inc)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parent.difficulty - parent.difficulty / From::from(2048) * From::from(max(diff_inc - 1, 99))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
target = max(min_difficulty, target);
|
||||||
|
let period = ((parent.number + 1) / EXP_DIFF_PERIOD) as usize;
|
||||||
|
if period > 1 {
|
||||||
|
target = max(min_difficulty, target + (U256::from(1) << (period - 2)));
|
||||||
|
}
|
||||||
|
target
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -57,3 +135,5 @@ fn on_close_block() {
|
|||||||
let b = b.close();
|
let b = b.close();
|
||||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: difficulty test
|
||||||
|
29
src/evm/evm.rs
Normal file
29
src/evm/evm.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//! Evm interface.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use evm::Ext;
|
||||||
|
|
||||||
|
/// Evm errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
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,
|
||||||
|
/// Returned on evm internal error. Should never be ignored during development.
|
||||||
|
/// Likely to cause consensus issues.
|
||||||
|
Internal,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evm result.
|
||||||
|
///
|
||||||
|
/// Returns gas_left if execution is successfull, otherwise error.
|
||||||
|
pub type Result = result::Result<U256, Error>;
|
||||||
|
|
||||||
|
/// Evm interface.
|
||||||
|
pub trait Evm {
|
||||||
|
/// This function should be used to execute transaction.
|
||||||
|
fn exec(&self, params: &ActionParams, ext: &mut Ext) -> Result;
|
||||||
|
}
|
58
src/evm/ext.rs
Normal file
58
src/evm/ext.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
//! Interface for Evm externalities.
|
||||||
|
|
||||||
|
use util::hash::*;
|
||||||
|
use util::uint::*;
|
||||||
|
use util::bytes::*;
|
||||||
|
use evm::Schedule;
|
||||||
|
use evm::Error;
|
||||||
|
|
||||||
|
// TODO: replace all u64 with u256
|
||||||
|
pub trait Ext {
|
||||||
|
/// Returns a value for given key.
|
||||||
|
fn sload(&self, key: &H256) -> H256;
|
||||||
|
|
||||||
|
/// Stores a value for given key.
|
||||||
|
fn sstore(&mut self, key: H256, value: H256);
|
||||||
|
|
||||||
|
/// Returns address balance.
|
||||||
|
fn balance(&self, address: &Address) -> U256;
|
||||||
|
|
||||||
|
/// Returns the hash of one of the 256 most recent complete blocks.
|
||||||
|
fn blockhash(&self, number: &U256) -> H256;
|
||||||
|
|
||||||
|
/// Creates new contract.
|
||||||
|
///
|
||||||
|
/// If contract creation is successfull, return gas_left and contract address,
|
||||||
|
/// If depth is too big or transfer value exceeds balance, return None
|
||||||
|
/// Otherwise return appropriate `Error`.
|
||||||
|
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), Error>;
|
||||||
|
|
||||||
|
/// Message call.
|
||||||
|
///
|
||||||
|
/// If call is successfull, returns gas left.
|
||||||
|
/// otherwise `Error`.
|
||||||
|
fn call(&mut self,
|
||||||
|
gas: u64,
|
||||||
|
call_gas: u64,
|
||||||
|
receive_address: &Address,
|
||||||
|
value: &U256,
|
||||||
|
data: &[u8],
|
||||||
|
code_address: &Address,
|
||||||
|
output: &mut [u8]) -> Result<u64, Error>;
|
||||||
|
|
||||||
|
/// Returns code at given address
|
||||||
|
fn extcode(&self, address: &Address) -> Vec<u8>;
|
||||||
|
|
||||||
|
/// Creates log entry with given topics and data
|
||||||
|
fn log(&mut self, topics: Vec<H256>, data: Bytes);
|
||||||
|
|
||||||
|
/// Should be called when transaction calls `RETURN` opcode.
|
||||||
|
/// Returns gas_left if cost of returning the data is not too high.
|
||||||
|
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, Error>;
|
||||||
|
|
||||||
|
/// Should be called when contract commits suicide.
|
||||||
|
fn suicide(&mut self);
|
||||||
|
|
||||||
|
/// Returns schedule.
|
||||||
|
fn schedule(&self) -> &Schedule;
|
||||||
|
}
|
25
src/evm/factory.rs
Normal file
25
src/evm/factory.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Evm factory.
|
||||||
|
|
||||||
|
use evm::Evm;
|
||||||
|
|
||||||
|
/// Evm factory. Creates appropriate Evm.
|
||||||
|
pub struct Factory;
|
||||||
|
|
||||||
|
impl Factory {
|
||||||
|
/// Returns jit vm
|
||||||
|
#[cfg(feature = "jit")]
|
||||||
|
pub fn create() -> Box<Evm> {
|
||||||
|
Box::new(super::jit::JitEvm)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns native rust evm
|
||||||
|
#[cfg(not(feature = "jit"))]
|
||||||
|
pub fn create() -> Box<Evm> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_vm() {
|
||||||
|
let _vm = Factory::create();
|
||||||
|
}
|
715
src/evm/jit.rs
Normal file
715
src/evm/jit.rs
Normal file
@ -0,0 +1,715 @@
|
|||||||
|
//! Just in time compiler execution environment.
|
||||||
|
use common::*;
|
||||||
|
use evmjit;
|
||||||
|
use evm;
|
||||||
|
|
||||||
|
/// Ethcore representation of evmjit runtime data.
|
||||||
|
pub struct RuntimeData {
|
||||||
|
pub gas: U256,
|
||||||
|
pub gas_price: U256,
|
||||||
|
pub call_data: Vec<u8>,
|
||||||
|
pub address: Address,
|
||||||
|
pub caller: Address,
|
||||||
|
pub origin: Address,
|
||||||
|
pub call_value: U256,
|
||||||
|
pub coinbase: Address,
|
||||||
|
pub difficulty: U256,
|
||||||
|
pub gas_limit: U256,
|
||||||
|
pub number: u64,
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub code: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeData {
|
||||||
|
pub fn new() -> RuntimeData {
|
||||||
|
RuntimeData {
|
||||||
|
gas: U256::zero(),
|
||||||
|
gas_price: U256::zero(),
|
||||||
|
call_data: vec![],
|
||||||
|
address: Address::new(),
|
||||||
|
caller: Address::new(),
|
||||||
|
origin: Address::new(),
|
||||||
|
call_value: U256::zero(),
|
||||||
|
coinbase: Address::new(),
|
||||||
|
difficulty: U256::zero(),
|
||||||
|
gas_limit: U256::zero(),
|
||||||
|
number: 0,
|
||||||
|
timestamp: 0,
|
||||||
|
code: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should be used to convert jit types to ethcore
|
||||||
|
trait FromJit<T>: Sized {
|
||||||
|
fn from_jit(input: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should be used to covert ethcore types to jit
|
||||||
|
trait IntoJit<T> {
|
||||||
|
fn into_jit(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromJit<&'a evmjit::I256> for U256 {
|
||||||
|
fn from_jit(input: &'a evmjit::I256) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let mut res: U256 = mem::uninitialized();
|
||||||
|
ptr::copy(input.words.as_ptr(), res.0.as_mut_ptr(), 4);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromJit<&'a evmjit::I256> for H256 {
|
||||||
|
fn from_jit(input: &'a evmjit::I256) -> Self {
|
||||||
|
let u = U256::from_jit(input);
|
||||||
|
H256::from(&u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromJit<&'a evmjit::I256> for Address {
|
||||||
|
fn from_jit(input: &'a evmjit::I256) -> Self {
|
||||||
|
Address::from(H256::from_jit(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromJit<&'a evmjit::H256> for H256 {
|
||||||
|
fn from_jit(input: &'a evmjit::H256) -> Self {
|
||||||
|
H256::from_jit(&evmjit::I256::from(input.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromJit<&'a evmjit::H256> for Address {
|
||||||
|
fn from_jit(input: &'a evmjit::H256) -> Self {
|
||||||
|
Address::from(H256::from_jit(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::I256> for U256 {
|
||||||
|
fn into_jit(self) -> evmjit::I256 {
|
||||||
|
unsafe {
|
||||||
|
let mut res: evmjit::I256 = mem::uninitialized();
|
||||||
|
ptr::copy(self.0.as_ptr(), res.words.as_mut_ptr(), 4);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::I256> for H256 {
|
||||||
|
fn into_jit(self) -> evmjit::I256 {
|
||||||
|
let mut ret = [0; 4];
|
||||||
|
for i in 0..self.bytes().len() {
|
||||||
|
let rev = self.bytes().len() - 1 - i;
|
||||||
|
let pos = rev / 8;
|
||||||
|
ret[pos] += (self.bytes()[i] as u64) << (rev % 8) * 8;
|
||||||
|
}
|
||||||
|
evmjit::I256 { words: ret }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::H256> for H256 {
|
||||||
|
fn into_jit(self) -> evmjit::H256 {
|
||||||
|
let i: evmjit::I256 = self.into_jit();
|
||||||
|
From::from(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::I256> for Address {
|
||||||
|
fn into_jit(self) -> evmjit::I256 {
|
||||||
|
H256::from(self).into_jit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::H256> for Address {
|
||||||
|
fn into_jit(self) -> evmjit::H256 {
|
||||||
|
H256::from(self).into_jit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoJit<evmjit::RuntimeDataHandle> for RuntimeData {
|
||||||
|
fn into_jit(self) -> evmjit::RuntimeDataHandle {
|
||||||
|
let mut data = evmjit::RuntimeDataHandle::new();
|
||||||
|
assert!(self.gas <= U256::from(u64::max_value()), "evmjit gas must be lower than 2 ^ 64");
|
||||||
|
assert!(self.gas_price <= U256::from(u64::max_value()), "evmjit gas_price must be lower than 2 ^ 64");
|
||||||
|
data.gas = self.gas.low_u64() as i64;
|
||||||
|
data.gas_price = self.gas_price.low_u64() as i64;
|
||||||
|
data.call_data = self.call_data.as_ptr();
|
||||||
|
data.call_data_size = self.call_data.len() as u64;
|
||||||
|
mem::forget(self.call_data);
|
||||||
|
data.address = self.address.into_jit();
|
||||||
|
data.caller = self.caller.into_jit();
|
||||||
|
data.origin = self.origin.into_jit();
|
||||||
|
data.call_value = self.call_value.into_jit();
|
||||||
|
data.coinbase = self.coinbase.into_jit();
|
||||||
|
data.difficulty = self.difficulty.into_jit();
|
||||||
|
data.gas_limit = self.gas_limit.into_jit();
|
||||||
|
data.number = self.number;
|
||||||
|
data.timestamp = self.timestamp as i64;
|
||||||
|
data.code = self.code.as_ptr();
|
||||||
|
data.code_size = self.code.len() as u64;
|
||||||
|
data.code_hash = self.code.sha3().into_jit();
|
||||||
|
mem::forget(self.code);
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Externalities adapter. Maps callbacks from evmjit to externalities trait.
|
||||||
|
///
|
||||||
|
/// Evmjit doesn't have to know about children execution failures.
|
||||||
|
/// This adapter 'catches' them and moves upstream.
|
||||||
|
struct ExtAdapter<'a> {
|
||||||
|
ext: &'a mut evm::Ext,
|
||||||
|
err: &'a mut Option<evm::Error>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExtAdapter<'a> {
|
||||||
|
fn new(ext: &'a mut evm::Ext, err: &'a mut Option<evm::Error>) -> Self {
|
||||||
|
ExtAdapter {
|
||||||
|
ext: ext,
|
||||||
|
err: err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
||||||
|
fn sload(&self, index: *const evmjit::I256, out_value: *mut evmjit::I256) {
|
||||||
|
unsafe {
|
||||||
|
let i = H256::from_jit(&*index);
|
||||||
|
let o = self.ext.sload(&i);
|
||||||
|
*out_value = o.into_jit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sstore(&mut self, index: *const evmjit::I256, value: *const evmjit::I256) {
|
||||||
|
unsafe {
|
||||||
|
self.ext.sstore(H256::from_jit(&*index), H256::from_jit(&*value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn balance(&self, address: *const evmjit::H256, out_value: *mut evmjit::I256) {
|
||||||
|
unsafe {
|
||||||
|
let a = Address::from_jit(&*address);
|
||||||
|
let o = self.ext.balance(&a);
|
||||||
|
*out_value = o.into_jit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockhash(&self, number: *const evmjit::I256, out_hash: *mut evmjit::H256) {
|
||||||
|
unsafe {
|
||||||
|
let n = U256::from_jit(&*number);
|
||||||
|
let o = self.ext.blockhash(&n);
|
||||||
|
*out_hash = o.into_jit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(&mut self,
|
||||||
|
io_gas: *mut u64,
|
||||||
|
endowment: *const evmjit::I256,
|
||||||
|
init_beg: *const u8,
|
||||||
|
init_size: u64,
|
||||||
|
address: *mut evmjit::H256) {
|
||||||
|
unsafe {
|
||||||
|
match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
|
||||||
|
Ok((gas_left, opt)) => {
|
||||||
|
*io_gas = gas_left;
|
||||||
|
if let Some(addr) = opt {
|
||||||
|
*address = addr.into_jit();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err @ evm::Error::OutOfGas) => {
|
||||||
|
*self.err = Some(err);
|
||||||
|
// hack to propagate `OutOfGas` to evmjit and stop
|
||||||
|
// the execution immediately.
|
||||||
|
// Works, cause evmjit uses i64, not u64
|
||||||
|
*io_gas = -1i64 as u64
|
||||||
|
},
|
||||||
|
Err(err) => *self.err = Some(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self,
|
||||||
|
io_gas: *mut u64,
|
||||||
|
call_gas: u64,
|
||||||
|
receive_address: *const evmjit::H256,
|
||||||
|
value: *const evmjit::I256,
|
||||||
|
in_beg: *const u8,
|
||||||
|
in_size: u64,
|
||||||
|
out_beg: *mut u8,
|
||||||
|
out_size: u64,
|
||||||
|
code_address: *const evmjit::H256) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let res = self.ext.call(*io_gas,
|
||||||
|
call_gas,
|
||||||
|
&Address::from_jit(&*receive_address),
|
||||||
|
&U256::from_jit(&*value),
|
||||||
|
slice::from_raw_parts(in_beg, in_size as usize),
|
||||||
|
&Address::from_jit(&*code_address),
|
||||||
|
slice::from_raw_parts_mut(out_beg, out_size as usize));
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(gas_left) => {
|
||||||
|
*io_gas = gas_left;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Err(err @ evm::Error::OutOfGas) => {
|
||||||
|
*self.err = Some(err);
|
||||||
|
// hack to propagate `OutOfGas` to evmjit and stop
|
||||||
|
// the execution immediately.
|
||||||
|
// Works, cause evmjit uses i64, not u64
|
||||||
|
*io_gas = -1i64 as u64;
|
||||||
|
false
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
*self.err = Some(err);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self,
|
||||||
|
beg: *const u8,
|
||||||
|
size: u64,
|
||||||
|
topic1: *const evmjit::H256,
|
||||||
|
topic2: *const evmjit::H256,
|
||||||
|
topic3: *const evmjit::H256,
|
||||||
|
topic4: *const evmjit::H256) {
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut topics = vec![];
|
||||||
|
if !topic1.is_null() {
|
||||||
|
topics.push(H256::from_jit(&*topic1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !topic2.is_null() {
|
||||||
|
topics.push(H256::from_jit(&*topic2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !topic3.is_null() {
|
||||||
|
topics.push(H256::from_jit(&*topic3));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !topic4.is_null() {
|
||||||
|
topics.push(H256::from_jit(&*topic4));
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize);
|
||||||
|
self.ext.log(topics, bytes_ref.to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcode(&self, address: *const evmjit::H256, size: *mut u64) -> *const u8 {
|
||||||
|
unsafe {
|
||||||
|
let code = self.ext.extcode(&Address::from_jit(&*address));
|
||||||
|
*size = code.len() as u64;
|
||||||
|
let ptr = code.as_ptr();
|
||||||
|
mem::forget(code);
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JitEvm;
|
||||||
|
|
||||||
|
impl evm::Evm for JitEvm {
|
||||||
|
fn exec(&self, params: &ActionParams, ext: &mut evm::Ext) -> evm::Result {
|
||||||
|
let mut optional_err = None;
|
||||||
|
// 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, &mut optional_err)) };
|
||||||
|
let mut ext_handle = evmjit::ExtHandle::new(ext_adapter);
|
||||||
|
let mut data = RuntimeData::new();
|
||||||
|
data.gas = params.gas;
|
||||||
|
data.gas_price = params.gas_price;
|
||||||
|
data.call_data = params.data.clone();
|
||||||
|
data.address = params.address.clone();
|
||||||
|
data.caller = params.sender.clone();
|
||||||
|
data.origin = params.origin.clone();
|
||||||
|
data.call_value = params.value;
|
||||||
|
data.code = params.code.clone();
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
data.coinbase = Address::new();
|
||||||
|
data.difficulty = U256::zero();
|
||||||
|
data.gas_limit = U256::zero();
|
||||||
|
data.number = 0;
|
||||||
|
data.timestamp = 0;
|
||||||
|
|
||||||
|
let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) };
|
||||||
|
let res = context.exec();
|
||||||
|
|
||||||
|
// check in adapter if execution of children contracts failed.
|
||||||
|
if let Some(err) = optional_err {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
match res {
|
||||||
|
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
|
||||||
|
evmjit::ReturnCode::Return => ext.ret(context.gas_left(), context.output_data()).map(|gas_left| U256::from(gas_left)),
|
||||||
|
evmjit::ReturnCode::Suicide => {
|
||||||
|
// what if there is a suicide and we run out of gas just after?
|
||||||
|
ext.suicide();
|
||||||
|
Ok(U256::from(context.gas_left()))
|
||||||
|
},
|
||||||
|
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
|
||||||
|
_err => Err(evm::Error::Internal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use common::*;
|
||||||
|
use evm::jit::{FromJit, IntoJit};
|
||||||
|
use evm::{Evm,Schedule};
|
||||||
|
use executive::*;
|
||||||
|
use state::*;
|
||||||
|
use engine::*;
|
||||||
|
use spec::*;
|
||||||
|
|
||||||
|
struct TestEngine;
|
||||||
|
|
||||||
|
impl TestEngine {
|
||||||
|
fn new() -> Self { TestEngine }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for TestEngine {
|
||||||
|
fn name(&self) -> &str { "TestEngine" }
|
||||||
|
fn spec(&self) -> &Spec { unimplemented!() }
|
||||||
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_and_from_u256() {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
let u = U256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap();
|
||||||
|
let j = u.into_jit();
|
||||||
|
let u2 = U256::from_jit(&j);
|
||||||
|
assert_eq!(u, u2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_and_from_h256() {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
let h = H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap();
|
||||||
|
let j: ::evmjit::I256 = h.clone().into_jit();
|
||||||
|
let h2 = H256::from_jit(&j);
|
||||||
|
assert_eq!(h, h2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_and_from_address() {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
let a = Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap();
|
||||||
|
let j: ::evmjit::I256 = a.clone().into_jit();
|
||||||
|
let a2 = Address::from_jit(&j);
|
||||||
|
assert_eq!(a, a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_add() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()),
|
||||||
|
H256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_sha3_0() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "6000600020600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()),
|
||||||
|
H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_sha3_1() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "6005600420600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()),
|
||||||
|
H256::from_str("c41589e7559804ea4a2080dad19d876a024ccb05117835447d72ce08c1d020ec").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_origin() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.origin = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "32600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(Address::from(state.storage_at(&address, &H256::new())), address.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sender() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "32600055".from_hex().unwrap();
|
||||||
|
params.code = "33600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(Address::from(state.storage_at(&address, &H256::new())), address.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extcode_copy0() {
|
||||||
|
// 33 - sender
|
||||||
|
// 3b - extcodesize
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 33 - sender
|
||||||
|
// 3c - extcodecopy
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 51 - load word from memory
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 55 - sstore
|
||||||
|
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
|
let address_code = "333b60006000333c600051600055".from_hex().unwrap();
|
||||||
|
let sender_code = "6005600055".from_hex().unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = sender.clone();
|
||||||
|
params.origin = sender.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = address_code.clone();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.init_code(&address, address_code);
|
||||||
|
state.init_code(&sender, sender_code);
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()),
|
||||||
|
H256::from_str("6005600055000000000000000000000000000000000000000000000000000000").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_balance() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "3331600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.add_balance(&address, &U256::from(0x10));
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0x10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_log() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "60006000a0".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
let logs = substate.logs();
|
||||||
|
assert_eq!(logs.len(), 1);
|
||||||
|
let log = &logs[0];
|
||||||
|
assert_eq!(log.address(), &address);
|
||||||
|
assert_eq!(log.topics().len(), 0);
|
||||||
|
assert_eq!(log.bloom(), H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log_with_one_topic() {
|
||||||
|
// 60 ff - push ff
|
||||||
|
// 60 00 - push 00
|
||||||
|
// 53 - mstore
|
||||||
|
// 33 - sender
|
||||||
|
// 60 20 - push 20
|
||||||
|
// 60 00 - push 0
|
||||||
|
// a1 - log with 1 topic
|
||||||
|
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "60ff6000533360206000a1".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
let logs = substate.logs();
|
||||||
|
assert_eq!(logs.len(), 1);
|
||||||
|
let log = &logs[0];
|
||||||
|
assert_eq!(log.address(), &address);
|
||||||
|
assert_eq!(log.topics().len(), 1);
|
||||||
|
let topic = &log.topics()[0];
|
||||||
|
assert_eq!(topic, &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap());
|
||||||
|
assert_eq!(topic, &H256::from(address.clone()));
|
||||||
|
assert_eq!(log.data(), &"ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_blockhash() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "600040600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let mut info = EnvInfo::new();
|
||||||
|
info.number = 1;
|
||||||
|
info.last_hashes.push(H256::from(address.clone()));
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(address.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calldataload() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "600135600055".from_hex().unwrap();
|
||||||
|
params.data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
let mut info = EnvInfo::new();
|
||||||
|
info.number = 1;
|
||||||
|
info.last_hashes.push(H256::from(address.clone()));
|
||||||
|
let engine = TestEngine::new();
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ext = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::InitContract);
|
||||||
|
let evm = JitEvm;
|
||||||
|
let _res = evm.exec(¶ms, &mut ext);
|
||||||
|
//assert_eq!(evm.exec(¶ms, &mut ext), Result::Stop {});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()), H256::from_str("23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").unwrap());
|
||||||
|
}
|
||||||
|
}
|
13
src/evm/mod.rs
Normal file
13
src/evm/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! Ethereum virtual machine.
|
||||||
|
|
||||||
|
pub mod ext;
|
||||||
|
pub mod evm;
|
||||||
|
pub mod factory;
|
||||||
|
pub mod schedule;
|
||||||
|
#[cfg(feature = "jit" )]
|
||||||
|
mod jit;
|
||||||
|
|
||||||
|
pub use self::evm::{Evm, Error, Result};
|
||||||
|
pub use self::ext::Ext;
|
||||||
|
pub use self::factory::Factory;
|
||||||
|
pub use self::schedule::Schedule;
|
@ -1,5 +1,5 @@
|
|||||||
/// Definition of the cost schedule and other parameterisations for the EVM.
|
/// Definition of the cost schedule and other parameterisations for the EVM.
|
||||||
pub struct EvmSchedule {
|
pub struct Schedule {
|
||||||
pub exceptional_failed_code_deposit: bool,
|
pub exceptional_failed_code_deposit: bool,
|
||||||
pub have_delegate_call: bool,
|
pub have_delegate_call: bool,
|
||||||
pub stack_limit: usize,
|
pub stack_limit: usize,
|
||||||
@ -32,19 +32,19 @@ pub struct EvmSchedule {
|
|||||||
pub copy_gas: usize,
|
pub copy_gas: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvmSchedule {
|
impl Schedule {
|
||||||
/// Schedule for the Frontier-era of the Ethereum main net.
|
/// Schedule for the Frontier-era of the Ethereum main net.
|
||||||
pub fn new_frontier() -> EvmSchedule {
|
pub fn new_frontier() -> Schedule {
|
||||||
Self::new(false, false, 21000)
|
Self::new(false, false, 21000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule for the Homestead-era of the Ethereum main net.
|
/// Schedule for the Homestead-era of the Ethereum main net.
|
||||||
pub fn new_homestead() -> EvmSchedule {
|
pub fn new_homestead() -> Schedule {
|
||||||
Self::new(true, true, 53000)
|
Self::new(true, true, 53000)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> EvmSchedule {
|
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||||
EvmSchedule{
|
Schedule{
|
||||||
exceptional_failed_code_deposit: efcd,
|
exceptional_failed_code_deposit: efcd,
|
||||||
have_delegate_call: hdc,
|
have_delegate_call: hdc,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
563
src/executive.rs
Normal file
563
src/executive.rs
Normal file
@ -0,0 +1,563 @@
|
|||||||
|
//! Transaction Execution environment.
|
||||||
|
use common::*;
|
||||||
|
use state::*;
|
||||||
|
use engine::*;
|
||||||
|
use evm::{self, Schedule, Factory, Ext};
|
||||||
|
|
||||||
|
/// Returns new address created from address and given nonce.
|
||||||
|
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||||
|
let mut stream = RlpStream::new_list(2);
|
||||||
|
stream.append(address);
|
||||||
|
stream.append(nonce);
|
||||||
|
From::from(stream.out().sha3())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State changes which should be applied in finalize,
|
||||||
|
/// after transaction is fully executed.
|
||||||
|
pub struct Substate {
|
||||||
|
/// Any accounts that have suicided.
|
||||||
|
suicides: HashSet<Address>,
|
||||||
|
/// Any logs.
|
||||||
|
logs: Vec<LogEntry>,
|
||||||
|
/// Refund counter of SSTORE nonzero->zero.
|
||||||
|
refunds_count: U256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Substate {
|
||||||
|
/// Creates new substate.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Substate {
|
||||||
|
suicides: HashSet::new(),
|
||||||
|
logs: vec![],
|
||||||
|
refunds_count: U256::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
|
pub fn logs(&self) -> &[LogEntry] {
|
||||||
|
&self.logs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transaction execution result.
|
||||||
|
pub struct Executed {
|
||||||
|
/// Gas paid up front for execution of transaction.
|
||||||
|
pub gas: U256,
|
||||||
|
/// Gas used during execution of transaction.
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Gas refunded after the execution of transaction.
|
||||||
|
/// To get gas that was required up front, add `refunded` and `gas_used`.
|
||||||
|
pub refunded: U256,
|
||||||
|
/// Cumulative gas used in current block so far.
|
||||||
|
///
|
||||||
|
/// cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)
|
||||||
|
///
|
||||||
|
/// where `tn` is current transaction.
|
||||||
|
pub cumulative_gas_used: U256,
|
||||||
|
/// Vector of logs generated by transaction.
|
||||||
|
pub logs: Vec<LogEntry>,
|
||||||
|
|
||||||
|
/// Execution ended running out of gas.
|
||||||
|
pub out_of_gas: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||||
|
|
||||||
|
/// Message-call/contract-creation executor; useful for executing transactions.
|
||||||
|
pub struct Executive<'a> {
|
||||||
|
state: &'a mut State,
|
||||||
|
info: &'a EnvInfo,
|
||||||
|
engine: &'a Engine,
|
||||||
|
depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Executive<'a> {
|
||||||
|
/// Creates new executive with depth equal 0.
|
||||||
|
pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine) -> Self {
|
||||||
|
Executive::new_with_depth(state, info, engine, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populates executive from parent properties. Increments executive depth.
|
||||||
|
fn from_parent(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self {
|
||||||
|
Executive::new_with_depth(state, info, engine, depth + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper constructor. Should be used to create `Executive` with desired depth.
|
||||||
|
/// Private.
|
||||||
|
fn new_with_depth(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize) -> Self {
|
||||||
|
Executive {
|
||||||
|
state: state,
|
||||||
|
info: info,
|
||||||
|
engine: engine,
|
||||||
|
depth: depth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This funtion should be used to execute transaction.
|
||||||
|
pub fn transact(&mut self, t: &Transaction) -> ExecutionResult {
|
||||||
|
// TODO: validate transaction signature ?/ sender
|
||||||
|
|
||||||
|
let sender = t.sender();
|
||||||
|
let nonce = self.state.nonce(&sender);
|
||||||
|
|
||||||
|
// validate transaction nonce
|
||||||
|
if t.nonce != nonce {
|
||||||
|
return Err(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce });
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate if transaction fits into given block
|
||||||
|
if self.info.gas_used + t.gas > self.info.gas_limit {
|
||||||
|
return Err(ExecutionError::BlockGasLimitReached {
|
||||||
|
gas_limit: self.info.gas_limit,
|
||||||
|
gas_used: self.info.gas_used,
|
||||||
|
gas: t.gas
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: we might need bigints here, or at least check overflows.
|
||||||
|
let balance = self.state.balance(&sender);
|
||||||
|
let gas_cost = t.gas * t.gas_price;
|
||||||
|
let total_cost = t.value + gas_cost;
|
||||||
|
|
||||||
|
// avoid unaffordable transactions
|
||||||
|
if balance < total_cost {
|
||||||
|
return Err(ExecutionError::NotEnoughCash { required: total_cost, is: balance });
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: there can be no invalid transactions from this point.
|
||||||
|
self.state.inc_nonce(&sender);
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
let backup = self.state.clone();
|
||||||
|
|
||||||
|
let res = match t.action() {
|
||||||
|
&Action::Create => {
|
||||||
|
let params = ActionParams {
|
||||||
|
address: contract_address(&sender, &nonce),
|
||||||
|
sender: sender.clone(),
|
||||||
|
origin: sender.clone(),
|
||||||
|
gas: t.gas,
|
||||||
|
gas_price: t.gas_price,
|
||||||
|
value: t.value,
|
||||||
|
code: t.data.clone(),
|
||||||
|
data: vec![],
|
||||||
|
};
|
||||||
|
self.create(¶ms, &mut substate)
|
||||||
|
},
|
||||||
|
&Action::Call(ref address) => {
|
||||||
|
let params = ActionParams {
|
||||||
|
address: address.clone(),
|
||||||
|
sender: sender.clone(),
|
||||||
|
origin: sender.clone(),
|
||||||
|
gas: t.gas,
|
||||||
|
gas_price: t.gas_price,
|
||||||
|
value: t.value,
|
||||||
|
code: self.state.code(address).unwrap_or(vec![]),
|
||||||
|
data: t.data.clone(),
|
||||||
|
};
|
||||||
|
self.call(¶ms, &mut substate, &mut [])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// finalize here!
|
||||||
|
self.finalize(t, substate, backup, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls contract function with given contract params.
|
||||||
|
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||||
|
/// Modifies the substate and the output.
|
||||||
|
/// Returns either gas_left or `evm::Error`.
|
||||||
|
fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result {
|
||||||
|
// at first, transfer value to destination
|
||||||
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||||
|
|
||||||
|
if self.engine.is_builtin(¶ms.address) {
|
||||||
|
// if destination is builtin, try to execute it
|
||||||
|
let cost = self.engine.cost_of_builtin(¶ms.address, ¶ms.data);
|
||||||
|
match cost <= params.gas {
|
||||||
|
true => {
|
||||||
|
self.engine.execute_builtin(¶ms.address, ¶ms.data, output);
|
||||||
|
Ok(params.gas - cost)
|
||||||
|
},
|
||||||
|
false => Err(evm::Error::OutOfGas)
|
||||||
|
}
|
||||||
|
} else if params.code.len() > 0 {
|
||||||
|
// if destination is a contract, do normal message call
|
||||||
|
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
|
||||||
|
let evm = Factory::create();
|
||||||
|
evm.exec(¶ms, &mut ext)
|
||||||
|
} else {
|
||||||
|
// otherwise, nothing
|
||||||
|
Ok(params.gas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates contract with given contract params.
|
||||||
|
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||||
|
/// Modifies the substate.
|
||||||
|
fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result {
|
||||||
|
// at first create new contract
|
||||||
|
self.state.new_contract(¶ms.address);
|
||||||
|
// then transfer value to it
|
||||||
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||||
|
|
||||||
|
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
|
||||||
|
let evm = Factory::create();
|
||||||
|
evm.exec(¶ms, &mut ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalizes the transaction (does refunds and suicides).
|
||||||
|
fn finalize(&mut self, t: &Transaction, substate: Substate, backup: State, result: evm::Result) -> ExecutionResult {
|
||||||
|
match result {
|
||||||
|
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
|
||||||
|
Err(evm::Error::OutOfGas) => {
|
||||||
|
*self.state = backup;
|
||||||
|
Ok(Executed {
|
||||||
|
gas: t.gas,
|
||||||
|
gas_used: t.gas,
|
||||||
|
refunded: U256::zero(),
|
||||||
|
cumulative_gas_used: self.info.gas_used + t.gas,
|
||||||
|
logs: vec![],
|
||||||
|
out_of_gas: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Ok(gas_left) => {
|
||||||
|
let schedule = self.engine.schedule(self.info);
|
||||||
|
|
||||||
|
// refunds from SSTORE nonzero -> zero
|
||||||
|
let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.refunds_count;
|
||||||
|
// refunds from contract suicides
|
||||||
|
let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len());
|
||||||
|
|
||||||
|
// real ammount to refund
|
||||||
|
let refund = cmp::min(sstore_refunds + suicide_refunds, (t.gas - gas_left) / U256::from(2)) + gas_left;
|
||||||
|
let refund_value = refund * t.gas_price;
|
||||||
|
self.state.add_balance(&t.sender(), &refund_value);
|
||||||
|
|
||||||
|
// fees earned by author
|
||||||
|
let fees = (t.gas - refund) * t.gas_price;
|
||||||
|
let author = &self.info.author;
|
||||||
|
self.state.add_balance(author, &fees);
|
||||||
|
|
||||||
|
// perform suicides
|
||||||
|
for address in substate.suicides.iter() {
|
||||||
|
self.state.kill_account(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gas_used = t.gas - gas_left;
|
||||||
|
Ok(Executed {
|
||||||
|
gas: t.gas,
|
||||||
|
gas_used: gas_used,
|
||||||
|
refunded: refund,
|
||||||
|
cumulative_gas_used: self.info.gas_used + gas_used,
|
||||||
|
logs: substate.logs,
|
||||||
|
out_of_gas: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Policy for handling output data on `RETURN` opcode.
|
||||||
|
pub enum OutputPolicy<'a> {
|
||||||
|
/// Return reference to fixed sized output.
|
||||||
|
/// Used for message calls.
|
||||||
|
Return(&'a mut [u8]),
|
||||||
|
/// Init new contract as soon as `RETURN` is called.
|
||||||
|
InitContract
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of evm Externalities.
|
||||||
|
pub struct Externalities<'a> {
|
||||||
|
state: &'a mut State,
|
||||||
|
info: &'a EnvInfo,
|
||||||
|
engine: &'a Engine,
|
||||||
|
depth: usize,
|
||||||
|
params: &'a ActionParams,
|
||||||
|
substate: &'a mut Substate,
|
||||||
|
schedule: Schedule,
|
||||||
|
output: OutputPolicy<'a>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Externalities<'a> {
|
||||||
|
/// Basic `Externalities` constructor.
|
||||||
|
pub fn new(state: &'a mut State,
|
||||||
|
info: &'a EnvInfo,
|
||||||
|
engine: &'a Engine,
|
||||||
|
depth: usize,
|
||||||
|
params: &'a ActionParams,
|
||||||
|
substate: &'a mut Substate,
|
||||||
|
output: OutputPolicy<'a>) -> Self {
|
||||||
|
Externalities {
|
||||||
|
state: state,
|
||||||
|
info: info,
|
||||||
|
engine: engine,
|
||||||
|
depth: depth,
|
||||||
|
params: params,
|
||||||
|
substate: substate,
|
||||||
|
schedule: engine.schedule(info),
|
||||||
|
output: output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates `Externalities` from `Executive`.
|
||||||
|
pub fn from_executive(e: &'a mut Executive, params: &'a ActionParams, substate: &'a mut Substate, output: OutputPolicy<'a>) -> Self {
|
||||||
|
Self::new(e.state, e.info, e.engine, e.depth, params, substate, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Ext for Externalities<'a> {
|
||||||
|
fn sload(&self, key: &H256) -> H256 {
|
||||||
|
self.state.storage_at(&self.params.address, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sstore(&mut self, key: H256, value: H256) {
|
||||||
|
// if SSTORE nonzero -> zero, increment refund count
|
||||||
|
if value == H256::new() && self.state.storage_at(&self.params.address, &key) != H256::new() {
|
||||||
|
self.substate.refunds_count = self.substate.refunds_count + U256::one();
|
||||||
|
}
|
||||||
|
self.state.set_storage(&self.params.address, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn balance(&self, address: &Address) -> U256 {
|
||||||
|
self.state.balance(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockhash(&self, number: &U256) -> H256 {
|
||||||
|
match *number < U256::from(self.info.number) {
|
||||||
|
false => H256::from(&U256::zero()),
|
||||||
|
true => {
|
||||||
|
let index = U256::from(self.info.number) - *number - U256::one();
|
||||||
|
self.info.last_hashes[index.low_u32() as usize].clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), evm::Error> {
|
||||||
|
// if balance is insufficient or we are to deep, return
|
||||||
|
if self.state.balance(&self.params.address) < *value && self.depth >= 1024 {
|
||||||
|
return Ok((gas, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new contract address
|
||||||
|
let address = contract_address(&self.params.address, &self.state.nonce(&self.params.address));
|
||||||
|
|
||||||
|
// prepare the params
|
||||||
|
let params = ActionParams {
|
||||||
|
address: address.clone(),
|
||||||
|
sender: self.params.address.clone(),
|
||||||
|
origin: self.params.origin.clone(),
|
||||||
|
gas: U256::from(gas),
|
||||||
|
gas_price: self.params.gas_price.clone(),
|
||||||
|
value: value.clone(),
|
||||||
|
code: code.to_vec(),
|
||||||
|
data: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
||||||
|
ex.state.inc_nonce(&self.params.address);
|
||||||
|
ex.create(¶ms, self.substate).map(|gas_left| (gas_left.low_u64(), Some(address)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<u64, evm::Error> {
|
||||||
|
let mut gas_cost = call_gas;
|
||||||
|
let mut call_gas = call_gas;
|
||||||
|
|
||||||
|
let is_call = receive_address == code_address;
|
||||||
|
if is_call && self.state.code(&code_address).is_none() {
|
||||||
|
gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *value > U256::zero() {
|
||||||
|
assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible");
|
||||||
|
gas_cost = gas_cost + self.schedule.call_value_transfer_gas as u64;
|
||||||
|
call_gas = call_gas + self.schedule.call_stipend as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if gas_cost > gas {
|
||||||
|
return Err(evm::Error::OutOfGas)
|
||||||
|
}
|
||||||
|
|
||||||
|
let gas = gas - gas_cost;
|
||||||
|
|
||||||
|
//println!("depth: {:?}", self.depth);
|
||||||
|
// if balance is insufficient or we are to deep, return
|
||||||
|
if self.state.balance(&self.params.address) < *value && self.depth >= 1024 {
|
||||||
|
return Ok(gas + call_gas)
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = ActionParams {
|
||||||
|
address: receive_address.clone(),
|
||||||
|
sender: self.params.address.clone(),
|
||||||
|
origin: self.params.origin.clone(),
|
||||||
|
gas: U256::from(call_gas),
|
||||||
|
gas_price: self.params.gas_price.clone(),
|
||||||
|
value: value.clone(),
|
||||||
|
code: self.state.code(code_address).unwrap_or(vec![]),
|
||||||
|
data: data.to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
||||||
|
ex.call(¶ms, self.substate, output).map(|gas_left| gas + gas_left.low_u64())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcode(&self, address: &Address) -> Vec<u8> {
|
||||||
|
self.state.code(address).unwrap_or(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, evm::Error> {
|
||||||
|
match &mut self.output {
|
||||||
|
&mut OutputPolicy::Return(ref mut slice) => unsafe {
|
||||||
|
let len = cmp::min(slice.len(), data.len());
|
||||||
|
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
|
||||||
|
Ok(gas)
|
||||||
|
},
|
||||||
|
&mut OutputPolicy::InitContract => {
|
||||||
|
let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64;
|
||||||
|
if return_cost > gas {
|
||||||
|
return Err(evm::Error::OutOfGas);
|
||||||
|
}
|
||||||
|
let mut code = vec![];
|
||||||
|
code.reserve(data.len());
|
||||||
|
unsafe {
|
||||||
|
ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
|
||||||
|
code.set_len(data.len());
|
||||||
|
}
|
||||||
|
let address = &self.params.address;
|
||||||
|
self.state.init_code(address, code);
|
||||||
|
Ok(gas - return_cost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
|
||||||
|
let address = self.params.address.clone();
|
||||||
|
self.substate.logs.push(LogEntry::new(address, topics, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suicide(&mut self) {
|
||||||
|
let address = self.params.address.clone();
|
||||||
|
self.substate.suicides.insert(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule(&self) -> &Schedule {
|
||||||
|
&self.schedule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use common::*;
|
||||||
|
use state::*;
|
||||||
|
use ethereum;
|
||||||
|
use null_engine::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contract_address() {
|
||||||
|
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let expected_address = Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap();
|
||||||
|
assert_eq!(expected_address, contract_address(&address, &U256::from(88)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// TODO: replace params with transactions!
|
||||||
|
fn test_executive() {
|
||||||
|
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||||
|
let address = contract_address(&sender, &U256::zero());
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = sender.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "3331600055".from_hex().unwrap();
|
||||||
|
params.value = U256::from(0x7);
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.add_balance(&sender, &U256::from(0x100u64));
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = NullEngine::new_boxed(ethereum::new_frontier());
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ex = Executive::new(&mut state, &info, engine.deref());
|
||||||
|
let _res = ex.create(¶ms, &mut substate);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0xf9u64)));
|
||||||
|
assert_eq!(state.balance(&sender), U256::from(0xf9));
|
||||||
|
assert_eq!(state.balance(&address), U256::from(0x7));
|
||||||
|
|
||||||
|
// TODO: just test state root.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_contract() {
|
||||||
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
|
let address = contract_address(&sender, &U256::zero());
|
||||||
|
let next_address = contract_address(&address, &U256::zero());
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = sender.clone();
|
||||||
|
params.origin = sender.clone();
|
||||||
|
params.gas = U256::from(0x174876e800u64);
|
||||||
|
params.code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036000f0600055".from_hex().unwrap();
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.add_balance(&sender, &U256::from(0x100u64));
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = NullEngine::new_boxed(ethereum::new_frontier());
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ex = Executive::new(&mut state, &info, engine.deref());
|
||||||
|
let _res = ex.create(¶ms, &mut substate);
|
||||||
|
println!("res: {:?}", _res);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(next_address.clone()));
|
||||||
|
assert_eq!(state.code(&next_address).unwrap(), "6000355415600957005b602035600035".from_hex().unwrap());
|
||||||
|
//assert!(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recursive_bomb1() {
|
||||||
|
// 60 01 - push 1
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 54 - sload
|
||||||
|
// 01 - add
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 55 - sstore
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 60 00 - push 0
|
||||||
|
// 30 - load address
|
||||||
|
// 60 e0 - push e0
|
||||||
|
// 5a - get gas
|
||||||
|
// 03 - sub
|
||||||
|
// f1 - message call (self in this case)
|
||||||
|
// 60 01 - push 1
|
||||||
|
// 55 - store
|
||||||
|
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
|
||||||
|
let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap();
|
||||||
|
let address = contract_address(&sender, &U256::zero());
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = sender.clone();
|
||||||
|
params.origin = sender.clone();
|
||||||
|
params.gas = U256::from(0x590b3);
|
||||||
|
params.gas_price = U256::one();
|
||||||
|
params.code = code.clone();
|
||||||
|
println!("init gas: {:?}", params.gas.low_u64());
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.init_code(&address, code.clone());
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = NullEngine::new_boxed(ethereum::new_frontier());
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ex = Executive::new(&mut state, &info, engine.deref());
|
||||||
|
let _res = ex.call(¶ms, &mut substate, &mut []);
|
||||||
|
println!("res: {:?}", _res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -83,27 +83,33 @@ extern crate time;
|
|||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
#[cfg(feature = "jit" )]
|
#[cfg(feature = "jit" )]
|
||||||
extern crate evmjit;
|
extern crate evmjit;
|
||||||
|
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod basic_types;
|
pub mod basic_types;
|
||||||
|
pub mod executive;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod log_entry;
|
pub mod log_entry;
|
||||||
pub mod env_info;
|
pub mod env_info;
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod account;
|
pub mod account;
|
||||||
|
pub mod action_params;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub mod receipt;
|
pub mod receipt;
|
||||||
pub mod null_engine;
|
pub mod null_engine;
|
||||||
pub mod evm_schedule;
|
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod spec;
|
pub mod spec;
|
||||||
pub mod views;
|
pub mod views;
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod extras;
|
pub mod extras;
|
||||||
|
pub mod evm;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod verification;
|
||||||
|
pub mod queue;
|
||||||
pub mod ethereum;
|
pub mod ethereum;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use evm_schedule::EvmSchedule;
|
use evm::Schedule;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
|
|
||||||
/// An engine which does not provide any consensus mechanism.
|
/// An engine which does not provide any consensus mechanism.
|
||||||
@ -17,5 +17,5 @@ impl NullEngine {
|
|||||||
impl Engine for NullEngine {
|
impl Engine for NullEngine {
|
||||||
fn name(&self) -> &str { "NullEngine" }
|
fn name(&self) -> &str { "NullEngine" }
|
||||||
fn spec(&self) -> &Spec { &self.spec }
|
fn spec(&self) -> &Spec { &self.spec }
|
||||||
fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() }
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
|
||||||
}
|
}
|
||||||
|
36
src/queue.rs
36
src/queue.rs
@ -1,16 +1,24 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use blockchain::BlockChain;
|
use blockchain::BlockChain;
|
||||||
use client::{QueueStatus, ImportResult};
|
|
||||||
use views::{BlockView};
|
use views::{BlockView};
|
||||||
|
use verification::*;
|
||||||
|
use error::*;
|
||||||
|
use engine::Engine;
|
||||||
|
|
||||||
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
|
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
|
||||||
/// Sorts them ready for blockchain insertion.
|
/// Sorts them ready for blockchain insertion.
|
||||||
pub struct BlockQueue;
|
pub struct BlockQueue {
|
||||||
|
bc: Arc<RwLock<BlockChain>>,
|
||||||
|
engine: Arc<Box<Engine>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl BlockQueue {
|
impl BlockQueue {
|
||||||
/// Creates a new queue instance.
|
/// Creates a new queue instance.
|
||||||
pub fn new() -> BlockQueue {
|
pub fn new(bc: Arc<RwLock<BlockChain>>, engine: Arc<Box<Engine>>) -> BlockQueue {
|
||||||
|
BlockQueue {
|
||||||
|
bc: bc,
|
||||||
|
engine: engine,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the queue and stop verification activity.
|
/// Clear the queue and stop verification activity.
|
||||||
@ -18,18 +26,16 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a block to the queue.
|
/// Add a block to the queue.
|
||||||
pub fn import_block(&mut self, bytes: &[u8], bc: &mut BlockChain) -> ImportResult {
|
pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
|
||||||
//TODO: verify block
|
let header = BlockView::new(bytes).header();
|
||||||
{
|
if self.bc.read().unwrap().is_known(&header.hash()) {
|
||||||
let block = BlockView::new(bytes);
|
return Err(ImportError::AlreadyInChain);
|
||||||
let header = block.header_view();
|
|
||||||
let hash = header.sha3();
|
|
||||||
if self.chain.is_known(&hash) {
|
|
||||||
return ImportResult::Bad;
|
|
||||||
}
|
}
|
||||||
}
|
try!(verify_block_basic(bytes, self.engine.deref().deref()));
|
||||||
bc.insert_block(bytes);
|
try!(verify_block_unordered(bytes, self.engine.deref().deref()));
|
||||||
ImportResult::Queued(QueueStatus::Known)
|
try!(verify_block_final(bytes, self.engine.deref().deref(), self.bc.read().unwrap().deref()));
|
||||||
|
self.bc.write().unwrap().insert_block(bytes);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,17 @@ pub struct Receipt {
|
|||||||
pub logs: Vec<LogEntry>,
|
pub logs: Vec<LogEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Receipt {
|
||||||
|
pub fn new(state_root: H256, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
|
||||||
|
Receipt {
|
||||||
|
state_root: state_root,
|
||||||
|
gas_used: gas_used,
|
||||||
|
log_bloom: logs.iter().fold(LogBloom::new(), |mut b, l| { b |= &l.bloom(); b }),
|
||||||
|
logs: logs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RlpStandard for Receipt {
|
impl RlpStandard for Receipt {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
s.append_list(4);
|
s.append_list(4);
|
||||||
|
20
src/state.rs
20
src/state.rs
@ -1,12 +1,8 @@
|
|||||||
use common::*;
|
use common::*;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
|
use executive::Executive;
|
||||||
|
|
||||||
/// Information concerning the result of the `State::apply` operation.
|
pub type ApplyResult = Result<Receipt, ExecutionError>;
|
||||||
pub struct ApplyInfo {
|
|
||||||
pub receipt: Receipt,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ApplyResult = Result<ApplyInfo, Error>;
|
|
||||||
|
|
||||||
/// Representation of the entire state of all accounts in the system.
|
/// Representation of the entire state of all accounts in the system.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
@ -134,8 +130,10 @@ impl State {
|
|||||||
|
|
||||||
/// Execute a given transaction.
|
/// Execute a given transaction.
|
||||||
/// This will change the state accordingly.
|
/// This will change the state accordingly.
|
||||||
pub fn apply(&mut self, _env_info: &EnvInfo, _engine: &Engine, _t: &Transaction) -> ApplyResult {
|
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
|
||||||
unimplemented!();
|
let e = try!(Executive::new(self, env_info, engine).transact(t));
|
||||||
|
self.commit();
|
||||||
|
Ok(Receipt::new(self.root().clone(), e.gas_used, e.logs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert into a JSON representation.
|
/// Convert into a JSON representation.
|
||||||
@ -221,6 +219,12 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for State {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
State::from_existing(self.db.clone(), self.root.clone(), self.account_start_nonce.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
use std::collections::{HashMap, VecDeque};
|
use util::*;
|
||||||
use util::bytes::Bytes;
|
use client::{BlockChainClient, BlockStatus, TreeRoute, BlockQueueStatus, BlockChainInfo};
|
||||||
use util::hash::{H256, FixedHash};
|
|
||||||
use util::uint::{U256};
|
|
||||||
use util::sha3::Hashable;
|
|
||||||
use util::rlp::{self, Rlp, RlpStream, View, Stream};
|
|
||||||
use util::network::{PeerId, PacketId};
|
|
||||||
use util::error::UtilError;
|
|
||||||
use client::{BlockChainClient, BlockStatus, TreeRoute, BlockQueueStatus, BlockChainInfo, ImportResult};
|
|
||||||
use header::{Header as BlockHeader, BlockNumber};
|
use header::{Header as BlockHeader, BlockNumber};
|
||||||
|
use error::*;
|
||||||
use sync::io::SyncIo;
|
use sync::io::SyncIo;
|
||||||
use sync::chain::ChainSync;
|
use sync::chain::ChainSync;
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ pub enum Action {
|
|||||||
/// A set of information describing an externally-originating message call
|
/// A set of information describing an externally-originating message call
|
||||||
/// or contract creation operation.
|
/// or contract creation operation.
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
nonce: U256,
|
pub nonce: U256,
|
||||||
gas_price: U256,
|
pub gas_price: U256,
|
||||||
gas: U256,
|
pub gas: U256,
|
||||||
action: Action,
|
pub action: Action,
|
||||||
value: U256,
|
pub value: U256,
|
||||||
data: Bytes,
|
pub data: Bytes,
|
||||||
|
|
||||||
hash: RefCell<Option<H256>>, //TODO: make this private
|
hash: RefCell<Option<H256>>, //TODO: make this private
|
||||||
}
|
}
|
||||||
@ -53,6 +53,9 @@ impl Transaction {
|
|||||||
|
|
||||||
/// Returns transaction type.
|
/// Returns transaction type.
|
||||||
pub fn action(&self) -> &Action { &self.action }
|
pub fn action(&self) -> &Action { &self.action }
|
||||||
|
|
||||||
|
/// Returns transaction sender.
|
||||||
|
pub fn sender(&self) -> Address { Address::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decodable for Action {
|
impl Decodable for Action {
|
||||||
|
154
src/verification.rs
Normal file
154
src/verification.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/// Block and transaction verification functions
|
||||||
|
///
|
||||||
|
/// Block verification is done in 3 steps
|
||||||
|
/// 1. Quick verification upon adding to the block queue
|
||||||
|
/// 2. Signatures verification done in the queue.
|
||||||
|
/// 3. Final verification against the blockchain done before enactment.
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use engine::Engine;
|
||||||
|
use blockchain::BlockChain;
|
||||||
|
|
||||||
|
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
|
||||||
|
pub fn verify_block_basic(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||||
|
let block = BlockView::new(bytes);
|
||||||
|
let header = block.header();
|
||||||
|
try!(verify_header(&header));
|
||||||
|
try!(verify_block_integrity(bytes, &header.transactions_root, &header.uncles_hash));
|
||||||
|
try!(engine.verify_block_basic(&header, Some(bytes)));
|
||||||
|
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
|
try!(verify_header(&u));
|
||||||
|
try!(engine.verify_block_basic(&u, None));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
|
||||||
|
/// Still operates on a individual block
|
||||||
|
/// TODO: return cached transactions, header hash.
|
||||||
|
pub fn verify_block_unordered(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
|
||||||
|
let block = BlockView::new(bytes);
|
||||||
|
let header = block.header();
|
||||||
|
try!(engine.verify_block_unordered(&header, Some(bytes)));
|
||||||
|
for u in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
|
try!(engine.verify_block_unordered(&u, None));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 3 verification. Check block information against parent and uncles.
|
||||||
|
pub fn verify_block_final(bytes: &[u8], engine: &Engine, bc: &BlockChain) -> Result<(), Error> {
|
||||||
|
let block = BlockView::new(bytes);
|
||||||
|
let header = block.header();
|
||||||
|
let parent = try!(bc.block_header(&header.parent_hash).ok_or::<Error>(From::from(BlockError::UnknownParent(header.parent_hash.clone()))));
|
||||||
|
try!(verify_parent(&header, &parent));
|
||||||
|
try!(engine.verify_block_final(&header, &parent, Some(bytes)));
|
||||||
|
|
||||||
|
let num_uncles = Rlp::new(bytes).at(2).item_count();
|
||||||
|
if num_uncles != 0 {
|
||||||
|
if num_uncles > engine.maximum_uncle_count() {
|
||||||
|
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: 0, max: engine.maximum_uncle_count(), found: num_uncles })));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut excluded = HashSet::new();
|
||||||
|
excluded.insert(header.hash());
|
||||||
|
let mut hash = header.parent_hash.clone();
|
||||||
|
excluded.insert(hash.clone());
|
||||||
|
for _ in 0..6 {
|
||||||
|
match bc.block_details(&hash) {
|
||||||
|
Some(details) => {
|
||||||
|
excluded.insert(details.parent.clone());
|
||||||
|
let b = bc.block(&hash).unwrap();
|
||||||
|
excluded.extend(BlockView::new(&b).uncle_hashes());
|
||||||
|
hash = details.parent;
|
||||||
|
}
|
||||||
|
None => break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for uncle in Rlp::new(bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
|
||||||
|
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or::<Error>(From::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
|
||||||
|
if excluded.contains(&uncle_parent.hash()) {
|
||||||
|
return Err(From::from(BlockError::UncleInChain(uncle_parent.hash())))
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_currentBlock.number() - uncle.number() m_cB.n - uP.n()
|
||||||
|
// 1 2
|
||||||
|
// 2
|
||||||
|
// 3
|
||||||
|
// 4
|
||||||
|
// 5
|
||||||
|
// 6 7
|
||||||
|
// (8 Invalid)
|
||||||
|
|
||||||
|
let depth = if header.number > uncle.number { header.number - uncle.number } else { 0 };
|
||||||
|
if depth > 6 {
|
||||||
|
return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: header.number - depth, max: header.number - 1, found: uncle.number })));
|
||||||
|
}
|
||||||
|
else if depth < 1 {
|
||||||
|
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: header.number - depth, max: header.number - 1, found: uncle.number })));
|
||||||
|
}
|
||||||
|
|
||||||
|
// cB
|
||||||
|
// cB.p^1 1 depth, valid uncle
|
||||||
|
// cB.p^2 ---/ 2
|
||||||
|
// cB.p^3 -----/ 3
|
||||||
|
// cB.p^4 -------/ 4
|
||||||
|
// cB.p^5 ---------/ 5
|
||||||
|
// cB.p^6 -----------/ 6
|
||||||
|
// cB.p^7 -------------/
|
||||||
|
// cB.p^8
|
||||||
|
let mut expected_uncle_parent = header.parent_hash.clone();
|
||||||
|
for _ in 0..depth {
|
||||||
|
expected_uncle_parent = bc.block_details(&expected_uncle_parent).unwrap().parent;
|
||||||
|
}
|
||||||
|
if expected_uncle_parent != uncle_parent.hash() {
|
||||||
|
return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash())));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(engine.verify_block_final(&uncle, &uncle_parent, Some(bytes)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check basic header parameters.
|
||||||
|
fn verify_header(header: &Header) -> Result<(), Error> {
|
||||||
|
if header.number > From::from(BlockNumber::max_value()) {
|
||||||
|
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: From::from(BlockNumber::max_value()), min: 0, found: header.number })))
|
||||||
|
}
|
||||||
|
if header.gas_used > header.gas_limit {
|
||||||
|
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: header.gas_limit, min: From::from(0), found: header.gas_used })));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check header parameters agains parent header.
|
||||||
|
fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
|
||||||
|
if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash {
|
||||||
|
return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() })))
|
||||||
|
}
|
||||||
|
if header.timestamp <= parent.timestamp {
|
||||||
|
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: u64::max_value(), min: parent.timestamp + 1, found: header.timestamp })))
|
||||||
|
}
|
||||||
|
if header.number <= parent.number {
|
||||||
|
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: BlockNumber::max_value(), min: parent.number + 1, found: header.number })));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify block data against header: transactions root and uncles hash.
|
||||||
|
fn verify_block_integrity(block: &[u8], transactions_root: &H256, uncles_hash: &H256) -> Result<(), Error> {
|
||||||
|
let block = Rlp::new(block);
|
||||||
|
let tx = block.at(1);
|
||||||
|
let expected_root = &ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here
|
||||||
|
if expected_root != transactions_root {
|
||||||
|
return Err(From::from(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root.clone(), found: transactions_root.clone() })))
|
||||||
|
}
|
||||||
|
let expected_uncles = &block.at(2).as_raw().sha3();
|
||||||
|
if expected_uncles != uncles_hash {
|
||||||
|
return Err(From::from(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles.clone(), found: uncles_hash.clone() })))
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user