Merge branch 'master' into gav

This commit is contained in:
Gav Wood 2016-01-11 19:47:26 +01:00
commit 4ca8fde530
26 changed files with 2065 additions and 190 deletions

View File

@ -12,7 +12,7 @@ env_logger = "0.3"
ethcore-util = { path = "../ethcore-util" }
rustc-serialize = "0.3"
flate2 = "0.2"
rocksdb = "0.2.1"
rocksdb = "0.2"
heapsize = "0.2.0"
rust-crypto = "0.2.34"
time = "0.1"
@ -20,4 +20,5 @@ time = "0.1"
evmjit = { path = "rust-evmjit", optional = true }
[features]
default = ["jit"]
jit = ["evmjit"]

View File

@ -3,6 +3,8 @@ name = "evmjit"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
[lib]
crate-type = ["dylib"]
[dependencies]
libc = "0.2.2"
tiny-keccak = "1.0"

View File

@ -7,27 +7,52 @@
//! use evmjit::*;
//!
//! 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);
//! }
//! ```
//!
//!
//! 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;
use std::ops::{Deref, DerefMut};
use self::ffi::*;
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 {
runtime_data: *mut JitRuntimeData
}
impl RuntimeDataHandle {
/// Creates new handle.
/// Creates new `RuntimeData` handle.
pub fn new() -> Self {
RuntimeDataHandle {
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 {
context: *mut JitContext,
_data_handle: RuntimeDataHandle
data_handle: RuntimeDataHandle,
}
impl ContextHandle {
/// 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) };
ContextHandle {
context: context,
_data_handle: data_handle
}
///
/// This function is unsafe cause ext lifetime is not considered
/// We also can't make ExtHandle a member of `ContextHandle` structure,
/// cause this would be a move operation or it would require a template
/// 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.
pub fn exec(&mut self) -> JitReturnCode {
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 {
@ -79,76 +139,76 @@ impl Drop for ContextHandle {
}
}
/// Component oriented wrapper around jit env c interface.
pub trait Env {
/// Component oriented wrapper around jit ext c interface.
pub trait Ext {
fn sload(&self, index: *const JitI256, out_value: *mut JitI256);
fn sstore(&mut self, index: *const JitI256, value: *const JitI256);
fn balance(&self, address: *const JitI256, out_value: *mut JitI256);
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitI256);
fn balance(&self, address: *const JitH256, out_value: *mut JitI256);
fn blockhash(&self, number: *const JitI256, out_hash: *mut JitH256);
fn create(&mut self,
io_gas: *mut u64,
endowment: *const JitI256,
init_beg: *const u8,
init_size: *const u64,
address: *mut JitI256);
init_size: u64,
address: *mut JitH256);
fn call(&mut self,
io_gas: *mut u64,
call_gas: *const u64,
receive_address: *const JitI256,
call_gas: u64,
receive_address: *const JitH256,
value: *const JitI256,
in_beg: *const u8,
in_size: *const u64,
in_size: u64,
out_beg: *mut u8,
out_size: *mut u64,
code_address: JitI256) -> bool;
out_size: u64,
code_address: *const JitH256) -> bool;
fn log(&mut self,
beg: *const u8,
size: *const u64,
topic1: *const JitI256,
topic2: *const JitI256,
topic3: *const JitI256,
topic4: *const JitI256);
size: u64,
topic1: *const JitH256,
topic2: *const JitH256,
topic3: *const JitH256,
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.
pub struct EnvHandle {
env_impl: Option<Box<Env>>
/// C abi compatible wrapper for jit ext implementers.
pub struct ExtHandle {
ext_impl: Option<Box<Ext>>
}
impl EnvHandle {
/// Creates new environment wrapper for given implementation
pub fn new<T>(env_impl: T) -> Self where T: Env + 'static {
EnvHandle { env_impl: Some(Box::new(env_impl)) }
impl ExtHandle {
/// Creates new extironment wrapper for given implementation
pub fn new<T>(ext_impl: T) -> Self where T: Ext + 'static {
ExtHandle { ext_impl: Some(Box::new(ext_impl)) }
}
/// Creates empty environment.
/// Creates empty extironment.
/// It can be used to for any operations.
pub fn empty() -> Self {
EnvHandle { env_impl: None }
ExtHandle { ext_impl: None }
}
}
impl Deref for EnvHandle {
type Target = Box<Env>;
impl Deref for ExtHandle {
type Target = Box<Ext>;
fn deref(&self) -> &Self::Target {
match self.env_impl {
Some(ref env) => env,
None => { panic!(); }
match self.ext_impl {
Some(ref ext) => ext,
None => { panic!("Handle is empty!"); }
}
}
}
impl DerefMut for EnvHandle {
impl DerefMut for ExtHandle {
fn deref_mut(&mut self) -> &mut Self::Target {
match self.env_impl {
Some(ref mut env) => env,
None => { panic!(); }
match self.ext_impl {
Some(ref mut ext) => ext,
None => { panic!("Handle is empty!"); }
}
}
}
@ -156,7 +216,7 @@ impl DerefMut for EnvHandle {
/// ffi functions
pub mod ffi {
use std::slice;
use libc;
use std::mem;
use tiny_keccak::Keccak;
use super::*;
@ -178,17 +238,50 @@ pub mod ffi {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
/// Signed 256 bit integer.
pub struct JitI256 {
pub words: [u64; 4]
}
#[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.
pub struct JitRuntimeData {
pub gas: i64,
pub gas_price: i64,
pub call_data: *const libc::c_char,
pub call_data: *const u8,
pub call_data_size: u64,
pub address: JitI256,
pub caller: JitI256,
@ -199,88 +292,88 @@ pub mod ffi {
pub gas_limit: JitI256,
pub number: u64,
pub timestamp: i64,
pub code: *const libc::c_char,
pub code: *const u8,
pub code_size: u64,
pub code_hash: JitI256
}
#[no_mangle]
pub unsafe extern fn env_sload(env: *const EnvHandle, index: *const JitI256, out_value: *mut JitI256) {
let env = &*env;
env.sload(index, out_value);
pub unsafe extern "C" fn env_sload(ext: *const ExtHandle, index: *const JitI256, out_value: *mut JitI256) {
let ext = &*ext;
ext.sload(index, out_value);
}
#[no_mangle]
pub unsafe extern fn env_sstore(env: *mut EnvHandle, index: *const JitI256, value: *const JitI256) {
let env = &mut *env;
env.sstore(index, value);
pub unsafe extern "C" fn env_sstore(ext: *mut ExtHandle, index: *mut JitI256, value: *mut JitI256) {
let ext = &mut *ext;
ext.sstore(index, value);
}
#[no_mangle]
pub unsafe extern fn env_balance(env: *const EnvHandle, address: *const JitI256, out_value: *mut JitI256) {
let env = &*env;
env.balance(address, out_value);
pub unsafe extern "C" fn env_balance(ext: *const ExtHandle, address: *const JitH256, out_value: *mut JitI256) {
let ext = &*ext;
ext.balance(address, out_value);
}
#[no_mangle]
pub unsafe extern fn env_blockhash(env: *const EnvHandle, number: *const JitI256, out_hash: *mut JitI256) {
let env = &*env;
env.blockhash(number, out_hash);
pub unsafe extern "C" fn env_blockhash(ext: *const ExtHandle, number: *const JitI256, out_hash: *mut JitH256) {
let ext = &*ext;
ext.blockhash(number, out_hash);
}
#[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,
endowment: *const JitI256,
init_beg: *const u8,
init_size: *const u64,
address: *mut JitI256) {
let env = &mut *env;
env.create(io_gas, endowment, init_beg, init_size, address);
init_size: u64,
address: *mut JitH256) {
let ext = &mut *ext;
ext.create(io_gas, endowment, init_beg, init_size, address);
}
#[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,
call_gas: *const u64,
receive_address: *const JitI256,
call_gas: u64,
receive_address: *const JitH256,
value: *const JitI256,
in_beg: *const u8,
in_size: *const u64,
in_size: u64,
out_beg: *mut u8,
out_size: *mut u64,
code_address: JitI256) -> bool {
let env = &mut *env;
env.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
out_size: u64,
code_address: *const JitH256) -> bool {
let ext = &mut *ext;
ext.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address)
}
#[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 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 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.finalize(output);
}
#[no_mangle]
pub unsafe extern fn env_extcode(env: *const EnvHandle, address: *const JitI256, size: *mut u64) -> *const u8 {
let env = &*env;
env.extcode(address, size)
pub unsafe extern "C" fn env_extcode(ext: *const ExtHandle, address: *const JitH256, size: *mut u64) -> *const u8 {
let ext = &*ext;
ext.extcode(address, size)
}
#[no_mangle]
pub unsafe extern fn env_log(env: *mut EnvHandle,
pub unsafe extern "C" fn env_log(ext: *mut ExtHandle,
beg: *const u8,
size: *const u64,
topic1: *const JitI256,
topic2: *const JitI256,
topic3: *const JitI256,
topic4: *const JitI256) {
let env = &mut *env;
env.log(beg, size, topic1, topic2, topic3, topic4);
size: u64,
topic1: *const JitH256,
topic2: *const JitH256,
topic3: *const JitH256,
topic4: *const JitH256) {
let ext = &mut *ext;
ext.log(beg, size, topic1, topic2, topic3, topic4);
}
@ -292,11 +385,11 @@ pub mod ffi {
pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode;
}
// ExtHandle is not a C type, so we need to allow "improper_ctypes"
#[link(name="evmjit")]
// EnvHandle does not have to by a C type
#[allow(improper_ctypes)]
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() {
unsafe {
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);
assert_eq!(code, JitReturnCode::Stop);
@ -316,6 +409,16 @@ fn ffi_test() {
#[test]
fn handle_test() {
let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty());
assert_eq!(context.exec(), ReturnCode::Stop);
unsafe {
let mut context = ContextHandle::new(RuntimeDataHandle::new(), &mut ExtHandle::empty());
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
View 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![],
}
}
}

View File

@ -142,7 +142,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
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 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> {
let env_info = self.env_info();
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.push(Entry { transaction: t, receipt: x.receipt });
self.block.archive.push(Entry { transaction: t, receipt: receipt });
Ok(&self.block.archive.last().unwrap().receipt)
}
Err(x) => Err(x)
Err(x) => Err(From::from(x))
}
}

View File

@ -5,6 +5,7 @@ use error::*;
use header::BlockNumber;
use spec::Spec;
use engine::Engine;
use queue::BlockQueue;
/// General block status
pub enum BlockStatus {
@ -18,9 +19,6 @@ pub enum BlockStatus {
Unknown,
}
/// Result of import block operation.
pub type ImportResult = Result<(), ImportError>;
/// Information about the blockchain gthered together.
pub struct BlockChainInfo {
/// 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.
pub struct Client {
chain: Arc<BlockChain>,
chain: Arc<RwLock<BlockChain>>,
_engine: Arc<Box<Engine>>,
queue: BlockQueue,
}
impl Client {
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()));
Ok(Client {
chain: chain.clone(),
_engine: engine,
_engine: engine.clone(),
queue: BlockQueue::new(chain.clone(), engine.clone()),
})
}
}
impl BlockChainClient for Client {
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> {
self.chain.block(hash).map(|bytes| {
self.chain.read().unwrap().block(hash).map(|bytes| {
let rlp = Rlp::new(&bytes);
let mut body = RlpStream::new();
body.append_raw(rlp.at(1).as_raw(), 1);
@ -126,34 +126,34 @@ impl BlockChainClient for Client {
}
fn block(&self, hash: &H256) -> Option<Bytes> {
self.chain.block(hash)
self.chain.read().unwrap().block(hash)
}
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> {
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> {
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> {
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 {
match self.chain.block_hash(n) {
match self.chain.read().unwrap().block_hash(n) {
Some(h) => self.block_status(&h),
None => BlockStatus::Unknown
}
}
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> {
@ -165,17 +165,7 @@ impl BlockChainClient for Client {
}
fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
//TODO: verify block
{
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(())
self.queue.import_block(bytes)
}
fn queue_status(&self) -> BlockQueueStatus {
@ -188,12 +178,13 @@ impl BlockChainClient for Client {
}
fn chain_info(&self) -> BlockChainInfo {
let chain = self.chain.read().unwrap();
BlockChainInfo {
total_difficulty: self.chain.best_block_total_difficulty(),
pending_total_difficulty: self.chain.best_block_total_difficulty(),
genesis_hash: self.chain.genesis_hash(),
best_block_hash: self.chain.best_block_hash(),
best_block_number: From::from(self.chain.best_block_number())
total_difficulty: chain.best_block_total_difficulty(),
pending_total_difficulty: chain.best_block_total_difficulty(),
genesis_hash: chain.genesis_hash(),
best_block_hash: chain.best_block_hash(),
best_block_number: From::from(chain.best_block_number())
}
}
}

View File

@ -2,11 +2,11 @@ pub use util::*;
pub use basic_types::*;
pub use error::*;
pub use env_info::*;
pub use evm_schedule::*;
pub use views::*;
pub use builtin::*;
pub use header::*;
pub use account::*;
pub use transaction::*;
pub use log_entry::*;
pub use receipt::*;
pub use receipt::*;
pub use action_params::*;

View File

@ -1,6 +1,7 @@
use common::*;
use block::Block;
use spec::Spec;
use evm::Schedule;
/// 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.
@ -22,7 +23,7 @@ pub trait Engine : Sync + Send {
fn spec(&self) -> &Spec;
/// 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`.
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_close_block(&self, _block: &mut Block) {}
/// Verify that `header` is valid.
/// `parent` (the parent header) and `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.
// TODO: consider including State in the params.
fn verify_block(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
// TODO: consider including State in the params for verification functions.
/// Phase 1 quick block verification. Only does checks that are cheap. `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_basic(&self, _header: &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.
// TODO: Add flags for which bits of the transaction to check.

View File

@ -22,3 +22,17 @@ pub struct EnvInfo {
/// The gas used.
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()
}
}
}

View File

@ -1,6 +1,7 @@
//! General error types for use in ethcore.
use util::*;
use header::BlockNumber;
#[derive(Debug)]
pub struct Mismatch<T: fmt::Debug> {
@ -15,27 +16,73 @@ pub struct OutOfBounds<T: fmt::Debug> {
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)]
pub enum BlockError {
TooManyUncles,
TooManyUncles(OutOfBounds<usize>),
UncleWrongGeneration,
ExtraDataOutOfBounds(OutOfBounds<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)]
pub enum ImportError {
Bad(BlockError),
Bad(Error),
AlreadyInChain,
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)]
/// General error type which should be capable of representing all errors in ethcore.
pub enum Error {
Util(UtilError),
Block(BlockError),
UnknownEngineName(String),
Execution(ExecutionError),
}
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.
/*#![feature(concat_idents)]
macro_rules! assimilate {

View File

@ -2,6 +2,7 @@ use common::*;
use block::*;
use spec::*;
use engine::*;
use evm::Schedule;
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// 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`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
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.
/// 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)));
}
}
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]
@ -57,3 +135,5 @@ fn on_close_block() {
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
// TODO: difficulty test

29
src/evm/evm.rs Normal file
View 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
View 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
View 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
View 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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &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, &params, &mut substate, OutputPolicy::InitContract);
let evm = JitEvm;
let _res = evm.exec(&params, &mut ext);
//assert_eq!(evm.exec(&params, &mut ext), Result::Stop {});
}
assert_eq!(state.storage_at(&address, &H256::new()), H256::from_str("23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").unwrap());
}
}

13
src/evm/mod.rs Normal file
View 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;

View File

@ -1,5 +1,5 @@
/// Definition of the cost schedule and other parameterisations for the EVM.
pub struct EvmSchedule {
pub struct Schedule {
pub exceptional_failed_code_deposit: bool,
pub have_delegate_call: bool,
pub stack_limit: usize,
@ -32,19 +32,19 @@ pub struct EvmSchedule {
pub copy_gas: usize,
}
impl EvmSchedule {
impl Schedule {
/// 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)
}
/// 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)
}
fn new(efcd: bool, hdc: bool, tcg: usize) -> EvmSchedule {
EvmSchedule{
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule{
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
stack_limit: 1024,

563
src/executive.rs Normal file
View 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(&params, &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(&params, &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(&params.sender, &params.address, &params.value);
if self.engine.is_builtin(&params.address) {
// if destination is builtin, try to execute it
let cost = self.engine.cost_of_builtin(&params.address, &params.data);
match cost <= params.gas {
true => {
self.engine.execute_builtin(&params.address, &params.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(&params, &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(&params.address);
// then transfer value to it
self.state.transfer_balance(&params.sender, &params.address, &params.value);
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
let evm = Factory::create();
evm.exec(&params, &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(&params, 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(&params, 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(&params, &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(&params, &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(&params, &mut substate, &mut []);
println!("res: {:?}", _res);
}
}
}

View File

@ -83,27 +83,33 @@ extern crate time;
extern crate env_logger;
#[cfg(feature = "jit" )]
extern crate evmjit;
extern crate ethcore_util as util;
pub mod common;
pub mod basic_types;
pub mod executive;
pub mod error;
pub mod log_entry;
pub mod env_info;
pub mod engine;
pub mod state;
pub mod account;
pub mod action_params;
pub mod header;
pub mod transaction;
pub mod receipt;
pub mod null_engine;
pub mod evm_schedule;
pub mod builtin;
pub mod spec;
pub mod views;
pub mod blockchain;
pub mod extras;
pub mod evm;
pub mod client;
pub mod sync;
pub mod block;
pub mod verification;
pub mod queue;
pub mod ethereum;

View File

@ -1,6 +1,6 @@
use engine::Engine;
use spec::Spec;
use evm_schedule::EvmSchedule;
use evm::Schedule;
use env_info::EnvInfo;
/// An engine which does not provide any consensus mechanism.
@ -17,5 +17,5 @@ impl NullEngine {
impl Engine for NullEngine {
fn name(&self) -> &str { "NullEngine" }
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() }
}

View File

@ -1,16 +1,24 @@
use std::sync::Arc;
use util::*;
use blockchain::BlockChain;
use client::{QueueStatus, ImportResult};
use views::{BlockView};
use verification::*;
use error::*;
use engine::Engine;
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
/// Sorts them ready for blockchain insertion.
pub struct BlockQueue;
pub struct BlockQueue {
bc: Arc<RwLock<BlockChain>>,
engine: Arc<Box<Engine>>,
}
impl BlockQueue {
/// 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.
@ -18,18 +26,16 @@ impl BlockQueue {
}
/// Add a block to the queue.
pub fn import_block(&mut self, bytes: &[u8], bc: &mut BlockChain) -> ImportResult {
//TODO: verify block
{
let block = BlockView::new(bytes);
let header = block.header_view();
let hash = header.sha3();
if self.chain.is_known(&hash) {
return ImportResult::Bad;
}
pub fn import_block(&mut self, bytes: &[u8]) -> ImportResult {
let header = BlockView::new(bytes).header();
if self.bc.read().unwrap().is_known(&header.hash()) {
return Err(ImportError::AlreadyInChain);
}
bc.insert_block(bytes);
ImportResult::Queued(QueueStatus::Known)
try!(verify_block_basic(bytes, self.engine.deref().deref()));
try!(verify_block_unordered(bytes, self.engine.deref().deref()));
try!(verify_block_final(bytes, self.engine.deref().deref(), self.bc.read().unwrap().deref()));
self.bc.write().unwrap().insert_block(bytes);
Ok(())
}
}

View File

@ -10,6 +10,17 @@ pub struct Receipt {
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 {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(4);

View File

@ -1,12 +1,8 @@
use common::*;
use engine::Engine;
use executive::Executive;
/// Information concerning the result of the `State::apply` operation.
pub struct ApplyInfo {
pub receipt: Receipt,
}
pub type ApplyResult = Result<ApplyInfo, Error>;
pub type ApplyResult = Result<Receipt, ExecutionError>;
/// Representation of the entire state of all accounts in the system.
pub struct State {
@ -134,8 +130,10 @@ impl State {
/// Execute a given transaction.
/// This will change the state accordingly.
pub fn apply(&mut self, _env_info: &EnvInfo, _engine: &Engine, _t: &Transaction) -> ApplyResult {
unimplemented!();
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
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.
@ -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)]
mod tests {
@ -377,4 +381,4 @@ fn create_empty() {
assert_eq!(s.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
}
}
}

View File

@ -1,13 +1,7 @@
use std::collections::{HashMap, VecDeque};
use util::bytes::Bytes;
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 util::*;
use client::{BlockChainClient, BlockStatus, TreeRoute, BlockQueueStatus, BlockChainInfo};
use header::{Header as BlockHeader, BlockNumber};
use error::*;
use sync::io::SyncIo;
use sync::chain::ChainSync;

View File

@ -8,12 +8,12 @@ pub enum Action {
/// A set of information describing an externally-originating message call
/// or contract creation operation.
pub struct Transaction {
nonce: U256,
gas_price: U256,
gas: U256,
action: Action,
value: U256,
data: Bytes,
pub nonce: U256,
pub gas_price: U256,
pub gas: U256,
pub action: Action,
pub value: U256,
pub data: Bytes,
hash: RefCell<Option<H256>>, //TODO: make this private
}
@ -53,6 +53,9 @@ impl Transaction {
/// Returns transaction type.
pub fn action(&self) -> &Action { &self.action }
/// Returns transaction sender.
pub fn sender(&self) -> Address { Address::new() }
}
impl Decodable for Action {

154
src/verification.rs Normal file
View 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(())
}