Merge branch 'master' into coverage-effort

Conflicts:
	ethcore/res/ethereum/tests
This commit is contained in:
Nikolay Volf
2016-01-31 18:40:59 +04:00
71 changed files with 180 additions and 76 deletions

340
ethcore/src/account.rs Normal file
View File

@@ -0,0 +1,340 @@
use util::*;
use pod_account::*;
/// Single account in the system.
#[derive(Clone)]
pub struct Account {
// Balance of the account.
balance: U256,
// Nonce of the account.
nonce: U256,
// Trie-backed storage.
storage_root: H256,
// Overlay on trie-backed storage - tuple is (<clean>, <value>).
storage_overlay: RefCell<HashMap<H256, (Filth, H256)>>,
// Code hash of the account. If None, means that it's a contract whose code has not yet been set.
code_hash: Option<H256>,
// Code cache of the account.
code_cache: Bytes,
}
impl Account {
/// General constructor.
pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account {
Account {
balance: balance,
nonce: nonce,
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(code.sha3()),
code_cache: code
}
}
/// General constructor.
pub fn from_pod(pod: PodAccount) -> Account {
Account {
balance: pod.balance,
nonce: pod.nonce,
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(pod.code.sha3()),
code_cache: pod.code
}
}
/// Create a new account with the given balance.
pub fn new_basic(balance: U256, nonce: U256) -> Account {
Account {
balance: balance,
nonce: nonce,
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(HashMap::new()),
code_hash: Some(SHA3_EMPTY),
code_cache: vec![],
}
}
/// Create a new account from RLP.
pub fn from_rlp(rlp: &[u8]) -> Account {
let r: Rlp = Rlp::new(rlp);
Account {
nonce: r.val_at(0),
balance: r.val_at(1),
storage_root: r.val_at(2),
storage_overlay: RefCell::new(HashMap::new()),
code_hash: Some(r.val_at(3)),
code_cache: vec![],
}
}
/// Create a new contract account.
/// NOTE: make sure you use `init_code` on this before `commit`ing.
pub fn new_contract(balance: U256) -> Account {
Account {
balance: balance,
nonce: U256::from(0u8),
storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(HashMap::new()),
code_hash: None,
code_cache: vec![],
}
}
/// Reset this account to the status of a not-yet-initialised contract.
/// NOTE: Account should have `init_code()` called on it later.
pub fn reset_code(&mut self) {
self.code_hash = None;
self.code_cache = vec![];
}
/// Set this account's code to the given code.
/// NOTE: Account should have been created with `new_contract()` or have `reset_code()` called on it.
pub fn init_code(&mut self, code: Bytes) {
assert!(self.code_hash.is_none());
self.code_cache = code;
}
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
pub fn set_storage(&mut self, key: H256, value: H256) {
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
}
/// Get (and cache) the contents of the trie's storage at `key`.
pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 {
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
(Filth::Clean, H256::from(SecTrieDB::new(db, &self.storage_root).get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)})))
}).1.clone()
}
/// return the balance associated with this account.
pub fn balance(&self) -> &U256 { &self.balance }
/// return the nonce associated with this account.
pub fn nonce(&self) -> &U256 { &self.nonce }
/// return the code hash associated with this account.
pub fn code_hash(&self) -> H256 {
self.code_hash.clone().unwrap_or(SHA3_EMPTY)
}
/// returns the account's code. If `None` then the code cache isn't available -
/// get someone who knows to call `note_code`.
pub fn code(&self) -> Option<&[u8]> {
match self.code_hash {
Some(SHA3_EMPTY) | None if self.code_cache.is_empty() => Some(&self.code_cache),
Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache),
None => Some(&self.code_cache),
_ => None,
}
}
/// Provide a byte array which hashes to the `code_hash`. returns the hash as a result.
pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> {
let h = code.sha3();
match self.code_hash {
Some(ref i) if h == *i => {
self.code_cache = code;
Ok(())
},
_ => Err(h)
}
}
/// Is `code_cache` valid; such that code is going to return Some?
pub fn is_cached(&self) -> bool {
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY))
}
/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
pub fn cache_code(&mut self, db: &HashDB) -> bool {
// TODO: fill out self.code_cache;
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
self.is_cached() ||
match self.code_hash {
Some(ref h) => match db.lookup(h) {
Some(x) => { self.code_cache = x.to_vec(); true },
_ => {
warn!("Failed reverse lookup of {}", h);
false
},
},
_ => false,
}
}
/// return the storage root associated with this account.
pub fn base_root(&self) -> &H256 { &self.storage_root }
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() }
/// return the storage root associated with this account or None if it has been altered via the overlay.
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
/// return the storage root associated with this account or None if it has been altered via the overlay.
pub fn recent_storage_root(&self) -> &H256 { &self.storage_root }
/// return the storage overlay.
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
/// Increment the nonce of the account by one.
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
/// Increment the nonce of the account by one.
pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; }
/// Increment the nonce of the account by one.
pub fn sub_balance(&mut self, x: &U256) { self.balance = self.balance - *x; }
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, db: &mut HashDB) {
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root);
for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
if f == &Filth::Dirty {
// cast key and value to trait type,
// so we can call overloaded `to_bytes` method
match v.is_zero() {
true => { t.remove(k); },
false => { t.insert(k, &encode(&U256::from(v.as_slice()))); },
}
*f = Filth::Clean;
}
}
}
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
pub fn commit_code(&mut self, db: &mut HashDB) {
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
(true, true) => self.code_hash = Some(SHA3_EMPTY),
(true, false) => {
self.code_hash = Some(db.insert(&self.code_cache));
},
(false, _) => {},
}
}
/// Export to RLP.
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&self.nonce);
stream.append(&self.balance);
stream.append(&self.storage_root);
stream.append(self.code_hash.as_ref().expect("Cannot form RLP of contract account without code."));
stream.out()
}
}
impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", PodAccount::from_account(self))
}
}
#[cfg(test)]
mod tests {
use util::*;
use super::*;
#[test]
fn storage_at() {
let mut db = MemoryDB::new();
let rlp = {
let mut a = Account::new_contract(U256::from(69u8));
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
a.commit_storage(&mut db);
a.init_code(vec![]);
a.commit_code(&mut db);
a.rlp()
};
let a = Account::from_rlp(&rlp);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
assert_eq!(a.storage_at(&db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_at(&db, &H256::from(&U256::from(0x01u64))), H256::new());
}
#[test]
fn note_code() {
let mut db = MemoryDB::new();
let rlp = {
let mut a = Account::new_contract(U256::from(69u8));
a.init_code(vec![0x55, 0x44, 0xffu8]);
a.commit_code(&mut db);
a.rlp()
};
let mut a = Account::from_rlp(&rlp);
assert!(a.cache_code(&db));
let mut a = Account::from_rlp(&rlp);
assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(()));
}
#[test]
fn commit_storage() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
a.set_storage(x!(0), x!(0x1234));
assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}
#[test]
fn commit_remove_commit_storage() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
a.set_storage(x!(0), x!(0x1234));
a.commit_storage(&mut db);
a.set_storage(x!(1), x!(0x1234));
a.commit_storage(&mut db);
a.set_storage(x!(1), x!(0));
a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}
#[test]
fn commit_code() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
a.init_code(vec![0x55, 0x44, 0xffu8]);
assert_eq!(a.code_hash(), SHA3_EMPTY);
a.commit_code(&mut db);
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
}
#[test]
fn rlpio() {
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
let b = Account::from_rlp(&a.rlp());
assert_eq!(a.balance(), b.balance());
assert_eq!(a.nonce(), b.nonce());
assert_eq!(a.code_hash(), b.code_hash());
assert_eq!(a.storage_root(), b.storage_root());
}
#[test]
fn new_account() {
use rustc_serialize::hex::ToHex;
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
assert_eq!(a.balance(), &U256::from(69u8));
assert_eq!(a.nonce(), &U256::from(0u8));
assert_eq!(a.code_hash(), SHA3_EMPTY);
assert_eq!(a.storage_root().unwrap(), &SHA3_NULL_RLP);
}
#[test]
fn create_account() {
use rustc_serialize::hex::ToHex;
let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new());
assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
}
}

130
ethcore/src/account_diff.rs Normal file
View File

@@ -0,0 +1,130 @@
use util::*;
use pod_account::*;
#[derive(Debug,Clone,PartialEq,Eq)]
/// Change in existance type.
// TODO: include other types of change.
pub enum Existance {
/// TODO [Gav Wood] Please document me
Born,
/// TODO [Gav Wood] Please document me
Alive,
/// TODO [Gav Wood] Please document me
Died,
}
impl fmt::Display for Existance {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Existance::Born => try!(write!(f, "+++")),
Existance::Alive => try!(write!(f, "***")),
Existance::Died => try!(write!(f, "XXX")),
}
Ok(())
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
/// TODO [Gav Wood] Please document me
pub struct AccountDiff {
/// TODO [Gav Wood] Please document me
pub balance: Diff<U256>, // Allowed to be Same
/// TODO [Gav Wood] Please document me
pub nonce: Diff<U256>, // Allowed to be Same
/// TODO [Gav Wood] Please document me
pub code: Diff<Bytes>, // Allowed to be Same
/// TODO [Gav Wood] Please document me
pub storage: BTreeMap<H256, Diff<H256>>,// Not allowed to be Same
}
impl AccountDiff {
/// TODO [Gav Wood] Please document me
pub fn existance(&self) -> Existance {
match self.balance {
Diff::Born(_) => Existance::Born,
Diff::Died(_) => Existance::Died,
_ => Existance::Alive,
}
}
/// TODO [Gav Wood] Please document me
pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<AccountDiff> {
match (pre, post) {
(None, Some(x)) => Some(AccountDiff {
balance: Diff::Born(x.balance.clone()),
nonce: Diff::Born(x.nonce.clone()),
code: Diff::Born(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Born(v.clone()))).collect(),
}),
(Some(x), None) => Some(AccountDiff {
balance: Diff::Died(x.balance.clone()),
nonce: Diff::Died(x.nonce.clone()),
code: Diff::Died(x.code.clone()),
storage: x.storage.iter().map(|(k, v)| (k.clone(), Diff::Died(v.clone()))).collect(),
}),
(Some(pre), Some(post)) => {
let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys())
.filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new()))
.collect();
let r = AccountDiff {
balance: Diff::new(pre.balance.clone(), post.balance.clone()),
nonce: Diff::new(pre.nonce.clone(), post.nonce.clone()),
code: Diff::new(pre.code.clone(), post.code.clone()),
storage: storage.into_iter().map(|k|
(k.clone(), Diff::new(
pre.storage.get(&k).cloned().unwrap_or_else(H256::new),
post.storage.get(&k).cloned().unwrap_or_else(H256::new)
))).collect(),
};
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.is_empty() {
None
} else {
Some(r)
}
},
_ => None,
}
}
}
// TODO: refactor into something nicer.
fn interpreted_hash(u: &H256) -> String {
if u <= &H256::from(0xffffffff) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
} else if u <= &H256::from(u64::max_value()) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u64(), U256::from(u.as_slice()).low_u64())
// } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") {
// format!("@{}", Address::from(u))
} else {
format!("#{}", u)
}
}
impl fmt::Display for AccountDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.nonce {
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),
_ => {},
}
match self.balance {
Diff::Born(ref x) => try!(write!(f, " bal {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))),
_ => {},
}
if let Diff::Born(ref x) = self.code {
try!(write!(f, " code {}", x.pretty()));
}
try!(write!(f, "\n"));
for (k, dv) in &self.storage {
match *dv {
Diff::Born(ref v) => try!(write!(f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v))),
Diff::Changed(ref pre, ref post) => try!(write!(f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))),
Diff::Died(_) => try!(write!(f, " X {}\n", interpreted_hash(k))),
_ => {},
}
}
Ok(())
}
}

View File

@@ -0,0 +1,55 @@
//! Evm input params.
use util::hash::*;
use util::uint::*;
use util::bytes::*;
/// Transaction value
#[derive(Clone, Debug)]
pub enum ActionValue {
/// Value that should be transfered
Transfer(U256),
/// Apparent value for transaction (not transfered)
Apparent(U256)
}
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
/// Action (call/create) input params. Everything else should be specified in Externalities.
#[derive(Clone, Debug)]
pub struct ActionParams {
/// Address of currently executed code.
pub code_address: Address,
/// Receive address. Usually equal to code_address,
/// except when called using CALLCODE.
pub address: Address,
/// Sender of current part of the transaction.
pub sender: Address,
/// Transaction initiator.
pub origin: Address,
/// Gas paid up front for transaction execution
pub gas: U256,
/// Gas price.
pub gas_price: U256,
/// Transaction value.
pub value: ActionValue,
/// Code being executed.
pub code: Option<Bytes>,
/// Input data.
pub data: Option<Bytes>
}
impl Default for ActionParams {
/// Returns default ActionParams initialized with zeros
fn default() -> ActionParams {
ActionParams {
code_address: Address::new(),
address: Address::new(),
sender: Address::new(),
origin: Address::new(),
gas: U256::zero(),
gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()),
code: None,
data: None
}
}
}

View File

@@ -0,0 +1,15 @@
use util::*;
/// Type for a 2048-bit log-bloom, as used by our blocks.
pub type LogBloom = H2048;
/// Constant 2048-bit datum for 0. Often used as a default.
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
/// TODO [Gav Wood] Please document me
pub enum Seal {
/// TODO [Gav Wood] Please document me
With,
/// TODO [Gav Wood] Please document me
Without,
}

397
ethcore/src/block.rs Normal file
View File

@@ -0,0 +1,397 @@
#![allow(ptr_arg)] // Because of &LastHashes -> &Vec<_>
use common::*;
use engine::*;
use state::*;
use verification::PreVerifiedBlock;
/// A block, encoded as it is on the block chain.
// TODO: rename to Block
#[derive(Default, Debug, Clone)]
pub struct Block {
/// The header of this block.
pub header: Header,
/// The transactions in this block.
pub transactions: Vec<Transaction>,
/// The uncles of this block.
pub uncles: Vec<Header>,
}
impl Block {
/// Returns true iff the given bytes form a valid encoding of a block in RLP.
// TODO: implement Decoder for this and have this use that.
pub fn is_good(b: &[u8]) -> bool {
/*
let urlp = UntrustedRlp::new(&b);
if !urlp.is_list() || urlp.item_count() != 3 || urlp.size() != b.len() { return false; }
if urlp.val_at::<Header>(0).is_err() { return false; }
if !urlp.at(1).unwrap().is_list() { return false; }
if urlp.at(1).unwrap().iter().find(|i| i.as_val::<Transaction>().is_err()).is_some() {
return false;
}
if !urlp.at(2).unwrap().is_list() { return false; }
if urlp.at(2).unwrap().iter().find(|i| i.as_val::<Header>().is_err()).is_some() {
return false;
}
true*/
UntrustedRlp::new(b).as_val::<Block>().is_ok()
}
}
impl Decodable for Block {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
if decoder.as_raw().len() != try!(decoder.as_rlp().payload_info()).total() {
return Err(DecoderError::RlpIsTooBig);
}
let d = decoder.as_rlp();
if d.item_count() != 3 {
return Err(DecoderError::RlpIncorrectListLen);
}
Ok(Block {
header: try!(d.val_at(0)),
transactions: try!(d.val_at(1)),
uncles: try!(d.val_at(2)),
})
}
}
/// Internal type for a block's common elements.
// TODO: rename to ExecutedBlock
// TODO: use BareBlock
#[derive(Debug, Clone)]
pub struct ExecutedBlock {
base: Block,
receipts: Vec<Receipt>,
transactions_set: HashSet<H256>,
state: State,
}
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
pub struct BlockRefMut<'a> {
/// TODO [Gav Wood] Please document me
pub header: &'a Header,
/// TODO [Gav Wood] Please document me
pub transactions: &'a Vec<Transaction>,
/// TODO [Gav Wood] Please document me
pub uncles: &'a Vec<Header>,
/// TODO [Gav Wood] Please document me
pub receipts: &'a Vec<Receipt>,
/// TODO [Gav Wood] Please document me
pub state: &'a mut State,
}
impl ExecutedBlock {
/// Create a new block from the given `state`.
fn new(state: State) -> ExecutedBlock { ExecutedBlock { base: Default::default(), receipts: Default::default(), transactions_set: Default::default(), state: state } }
/// Get a structure containing individual references to all public fields.
pub fn fields(&mut self) -> BlockRefMut {
BlockRefMut {
header: &self.base.header,
transactions: &self.base.transactions,
uncles: &self.base.uncles,
state: &mut self.state,
receipts: &self.receipts,
}
}
}
/// Trait for a object that is_a `ExecutedBlock`.
pub trait IsBlock {
/// Get the block associated with this object.
fn block(&self) -> &ExecutedBlock;
/// Get the header associated with this object's block.
fn header(&self) -> &Header { &self.block().base.header }
/// Get the final state associated with this object's block.
fn state(&self) -> &State { &self.block().state }
/// Get all information on transactions in this block.
fn transactions(&self) -> &Vec<Transaction> { &self.block().base.transactions }
/// Get all information on receipts in this block.
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
/// Get all uncles in this block.
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
}
impl IsBlock for ExecutedBlock {
fn block(&self) -> &ExecutedBlock { self }
}
/// Block that is ready for transactions to be added.
///
/// It's a bit like a Vec<Transaction>, eccept that whenever a transaction is pushed, we execute it and
/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation.
pub struct OpenBlock<'x, 'y> {
block: ExecutedBlock,
engine: &'x Engine,
last_hashes: &'y LastHashes,
}
/// Just like OpenBlock, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
/// and collected the uncles.
///
/// There is no function available to push a transaction. If you want that you'll need to `reopen()` it.
pub struct ClosedBlock<'x, 'y> {
open_block: OpenBlock<'x, 'y>,
uncle_bytes: Bytes,
}
/// A block that has a valid seal.
///
/// The block's header has valid seal arguments. The block cannot be reversed into a ClosedBlock or OpenBlock.
pub struct SealedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
}
impl<'x, 'y> OpenBlock<'x, 'y> {
/// Create a new OpenBlock ready for transaction pushing.
pub fn new<'a, 'b>(engine: &'a Engine, db: JournalDB, parent: &Header, last_hashes: &'b LastHashes, author: Address, extra_data: Bytes) -> OpenBlock<'a, 'b> {
let mut r = OpenBlock {
block: ExecutedBlock::new(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce())),
engine: engine,
last_hashes: last_hashes,
};
r.block.base.header.set_number(parent.number() + 1);
r.block.base.header.set_author(author);
r.block.base.header.set_extra_data(extra_data);
r.block.base.header.set_timestamp_now();
engine.populate_from_parent(&mut r.block.base.header, parent);
engine.on_new_block(&mut r.block);
r
}
/// Alter the author for the block.
pub fn set_author(&mut self, author: Address) { self.block.base.header.set_author(author); }
/// Alter the timestamp of the block.
pub fn set_timestamp(&mut self, timestamp: u64) { self.block.base.header.set_timestamp(timestamp); }
/// Alter the difficulty for the block.
pub fn set_difficulty(&mut self, a: U256) { self.block.base.header.set_difficulty(a); }
/// Alter the gas limit for the block.
pub fn set_gas_limit(&mut self, a: U256) { self.block.base.header.set_gas_limit(a); }
/// Alter the gas limit for the block.
pub fn set_gas_used(&mut self, a: U256) { self.block.base.header.set_gas_used(a); }
/// Alter the extra_data for the block.
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
if extra_data.len() > self.engine.maximum_extra_data_size() {
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()}))
} else {
self.block.base.header.set_extra_data(extra_data);
Ok(())
}
}
/// Add an uncle to the block, if possible.
///
/// NOTE Will check chain constraints and the uncle number but will NOT check
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
if self.block.base.uncles.len() >= self.engine.maximum_uncle_count() {
return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.base.uncles.len()}));
}
// TODO: check number
// TODO: check not a direct ancestor (use last_hashes for that)
self.block.base.uncles.push(valid_uncle_header);
Ok(())
}
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
EnvInfo {
number: self.block.base.header.number,
author: self.block.base.header.author.clone(),
timestamp: self.block.base.header.timestamp,
difficulty: self.block.base.header.difficulty.clone(),
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
gas_limit: self.block.base.header.gas_limit.clone(),
}
}
/// Push a transaction into the block.
///
/// If valid, it will be executed, and archived together with the receipt.
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
let env_info = self.env_info();
// info!("env_info says gas_used={}", env_info.gas_used);
match self.block.state.apply(&env_info, self.engine, &t) {
Ok(receipt) => {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t);
self.block.receipts.push(receipt);
Ok(&self.block.receipts.last().unwrap())
}
Err(x) => Err(From::from(x))
}
}
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles.
pub fn close(self) -> ClosedBlock<'x, 'y> {
let mut s = self;
s.engine.on_close_block(&mut s.block);
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out();
s.block.base.header.uncles_hash = uncle_bytes.sha3();
s.block.base.header.state_root = s.block.state.root().clone();
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b |= &r.log_bloom; b});
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
s.block.base.header.note_dirty();
ClosedBlock::new(s, uncle_bytes)
}
}
impl<'x, 'y> IsBlock for OpenBlock<'x, 'y> {
fn block(&self) -> &ExecutedBlock { &self.block }
}
impl<'x, 'y> IsBlock for ClosedBlock<'x, 'y> {
fn block(&self) -> &ExecutedBlock { &self.open_block.block }
}
impl<'x, 'y> ClosedBlock<'x, 'y> {
fn new<'a, 'b>(open_block: OpenBlock<'a, 'b>, uncle_bytes: Bytes) -> ClosedBlock<'a, 'b> {
ClosedBlock {
open_block: open_block,
uncle_bytes: uncle_bytes,
}
}
/// Get the hash of the header without seal arguments.
pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) }
/// Provide a valid seal in order to turn this into a `SealedBlock`.
///
/// NOTE: This does not check the validity of `seal` with the engine.
pub fn seal(self, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
let mut s = self;
if seal.len() != s.open_block.engine.seal_fields() {
return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()}));
}
s.open_block.block.base.header.set_seal(seal);
Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes })
}
/// Turn this back into an `OpenBlock`.
pub fn reopen(self) -> OpenBlock<'x, 'y> { self.open_block }
/// Drop this object and return the underlieing database.
pub fn drain(self) -> JournalDB { self.open_block.block.state.drop().1 }
}
impl SealedBlock {
/// Get the RLP-encoding of the block.
pub fn rlp_bytes(&self) -> Bytes {
let mut block_rlp = RlpStream::new_list(3);
self.block.base.header.stream_rlp(&mut block_rlp, Seal::With);
block_rlp.append(&self.block.base.transactions);
block_rlp.append_raw(&self.uncle_bytes, 1);
block_rlp.out()
}
/// Drop this object and return the underlieing database.
pub fn drain(self) -> JournalDB { self.block.state.drop().1 }
}
impl IsBlock for SealedBlock {
fn block(&self) -> &ExecutedBlock { &self.block }
}
/// Enact the block given by block header, transactions and uncles
pub fn enact<'x, 'y>(header: &Header, transactions: &[Transaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
{
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
}
let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author().clone(), header.extra_data().clone());
b.set_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp());
for t in transactions { try!(b.push_transaction(t.clone(), None)); }
for u in uncles { try!(b.push_uncle(u.clone())); }
Ok(b.close())
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_bytes<'x, 'y>(block_bytes: &[u8], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
let block = BlockView::new(block_bytes);
let header = block.header();
enact(&header, &block.transactions(), &block.uncles(), engine, db, parent, last_hashes)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified<'x, 'y>(block: &PreVerifiedBlock, engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
}
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
pub fn enact_and_seal(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &Header, last_hashes: &LastHashes) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view();
Ok(try!(try!(enact_bytes(block_bytes, engine, db, parent, last_hashes)).seal(header.seal())))
}
#[cfg(test)]
mod tests {
use tests::helpers::*;
use super::*;
use common::*;
use engine::*;
#[test]
fn open_block() {
use spec::*;
let engine = Spec::new_test().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = b.close();
let _ = b.seal(vec![]);
}
#[test]
fn enact_block() {
use spec::*;
let engine = Spec::new_test().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();
let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let e = enact_and_seal(&orig_bytes, engine.deref(), db.clone(), &genesis_header, &vec![genesis_header.hash()]).unwrap();
assert_eq!(e.rlp_bytes(), orig_bytes);
let db = e.drain();
assert_eq!(orig_db.keys(), db.keys());
assert!(orig_db.keys().iter().filter(|k| orig_db.get(k.0) != db.get(k.0)).next() == None);
}
}

359
ethcore/src/block_queue.rs Normal file
View File

@@ -0,0 +1,359 @@
//! A queue of blocks. Sits between network or other I/O and the BlockChain.
//! Sorts them ready for blockchain insertion.
use std::thread::{JoinHandle, self};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use util::*;
use verification::*;
use error::*;
use engine::Engine;
use views::*;
use header::*;
use service::*;
/// Block queue status
#[derive(Debug)]
pub struct BlockQueueInfo {
/// Indicates that queue is full
pub full: bool,
/// Number of queued blocks pending verification
pub unverified_queue_size: usize,
/// Number of verified queued blocks pending import
pub verified_queue_size: usize,
/// Number of blocks being verified
pub verifying_queue_size: usize,
}
impl BlockQueueInfo {
/// The total size of the queues.
pub fn total_queue_size(&self) -> usize { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size }
/// The size of the unverified and verifying queues.
pub fn incomplete_queue_size(&self) -> usize { self.unverified_queue_size + self.verifying_queue_size }
}
/// A queue of blocks. Sits between network or other I/O and the BlockChain.
/// Sorts them ready for blockchain insertion.
pub struct BlockQueue {
engine: Arc<Box<Engine>>,
more_to_verify: Arc<Condvar>,
verification: Arc<Mutex<Verification>>,
verifiers: Vec<JoinHandle<()>>,
deleting: Arc<AtomicBool>,
ready_signal: Arc<QueueSignal>,
empty: Arc<Condvar>,
processing: HashSet<H256>
}
struct UnVerifiedBlock {
header: Header,
bytes: Bytes,
}
struct VerifyingBlock {
hash: H256,
block: Option<PreVerifiedBlock>,
}
struct QueueSignal {
signalled: AtomicBool,
message_channel: IoChannel<NetSyncMessage>,
}
impl QueueSignal {
fn set(&self) {
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
self.message_channel.send(UserMessage(SyncMessage::BlockVerified)).expect("Error sending BlockVerified message");
}
}
fn reset(&self) {
self.signalled.store(false, AtomicOrdering::Relaxed);
}
}
#[derive(Default)]
struct Verification {
unverified: VecDeque<UnVerifiedBlock>,
verified: VecDeque<PreVerifiedBlock>,
verifying: VecDeque<VerifyingBlock>,
bad: HashSet<H256>,
}
impl BlockQueue {
/// Creates a new queue instance.
pub fn new(engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
let verification = Arc::new(Mutex::new(Verification::default()));
let more_to_verify = Arc::new(Condvar::new());
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
let deleting = Arc::new(AtomicBool::new(false));
let empty = Arc::new(Condvar::new());
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
let thread_count = max(::num_cpus::get(), 3) - 2;
for i in 0..thread_count {
let verification = verification.clone();
let engine = engine.clone();
let more_to_verify = more_to_verify.clone();
let ready_signal = ready_signal.clone();
let empty = empty.clone();
let deleting = deleting.clone();
verifiers.push(thread::Builder::new().name(format!("Verifier #{}", i)).spawn(move || BlockQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty))
.expect("Error starting block verification thread"));
}
BlockQueue {
engine: engine,
ready_signal: ready_signal.clone(),
more_to_verify: more_to_verify.clone(),
verification: verification.clone(),
verifiers: verifiers,
deleting: deleting.clone(),
processing: HashSet::new(),
empty: empty.clone(),
}
}
fn verify(verification: Arc<Mutex<Verification>>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
while !deleting.load(AtomicOrdering::Relaxed) {
{
let mut lock = verification.lock().unwrap();
if lock.unverified.is_empty() && lock.verifying.is_empty() {
empty.notify_all();
}
while lock.unverified.is_empty() && !deleting.load(AtomicOrdering::Relaxed) {
lock = wait.wait(lock).unwrap();
}
if deleting.load(AtomicOrdering::Relaxed) {
return;
}
}
let block = {
let mut v = verification.lock().unwrap();
if v.unverified.is_empty() {
continue;
}
let block = v.unverified.pop_front().unwrap();
v.verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
block
};
let block_hash = block.header.hash();
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
Ok(verified) => {
let mut v = verification.lock().unwrap();
for e in &mut v.verifying {
if e.hash == block_hash {
e.block = Some(verified);
break;
}
}
if !v.verifying.is_empty() && v.verifying.front().unwrap().hash == block_hash {
// we're next!
let mut vref = v.deref_mut();
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
ready.set();
}
},
Err(err) => {
let mut v = verification.lock().unwrap();
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
v.bad.insert(block_hash.clone());
v.verifying.retain(|e| e.hash != block_hash);
let mut vref = v.deref_mut();
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
ready.set();
}
}
}
}
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreVerifiedBlock>, bad: &mut HashSet<H256>) {
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
let block = verifying.pop_front().unwrap().block.unwrap();
if bad.contains(&block.header.parent_hash) {
bad.insert(block.header.hash());
}
else {
verified.push_back(block);
}
}
}
/// Clear the queue and stop verification activity.
pub fn clear(&mut self) {
let mut verification = self.verification.lock().unwrap();
verification.unverified.clear();
verification.verifying.clear();
}
/// Wait for queue to be empty
pub fn flush(&mut self) {
let mut verification = self.verification.lock().unwrap();
while !verification.unverified.is_empty() || !verification.verifying.is_empty() {
verification = self.empty.wait(verification).unwrap();
}
}
/// Add a block to the queue.
pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(&bytes).header();
let h = header.hash();
if self.processing.contains(&h) {
return Err(ImportError::AlreadyQueued);
}
{
let mut verification = self.verification.lock().unwrap();
if verification.bad.contains(&h) {
return Err(ImportError::Bad(None));
}
if verification.bad.contains(&header.parent_hash) {
verification.bad.insert(h.clone());
return Err(ImportError::Bad(None));
}
}
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
Ok(()) => {
self.processing.insert(h.clone());
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
self.more_to_verify.notify_all();
Ok(h)
},
Err(err) => {
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
self.verification.lock().unwrap().bad.insert(h.clone());
Err(From::from(err))
}
}
}
/// Mark given block and all its children as bad. Stops verification.
pub fn mark_as_bad(&mut self, hash: &H256) {
let mut verification_lock = self.verification.lock().unwrap();
let mut verification = verification_lock.deref_mut();
verification.bad.insert(hash.clone());
let mut new_verified = VecDeque::new();
for block in verification.verified.drain(..) {
if verification.bad.contains(&block.header.parent_hash) {
verification.bad.insert(block.header.hash());
}
else {
new_verified.push_back(block);
}
}
verification.verified = new_verified;
}
/// Removes up to `max` verified blocks from the queue
pub fn drain(&mut self, max: usize) -> Vec<PreVerifiedBlock> {
let mut verification = self.verification.lock().unwrap();
let count = min(max, verification.verified.len());
let mut result = Vec::with_capacity(count);
for _ in 0..count {
let block = verification.verified.pop_front().unwrap();
self.processing.remove(&block.header.hash());
result.push(block);
}
self.ready_signal.reset();
if !verification.verified.is_empty() {
self.ready_signal.set();
}
result
}
/// Get queue status.
pub fn queue_info(&self) -> BlockQueueInfo {
let verification = self.verification.lock().unwrap();
BlockQueueInfo {
full: false,
verified_queue_size: verification.verified.len(),
unverified_queue_size: verification.unverified.len(),
verifying_queue_size: verification.verifying.len(),
}
}
}
impl Drop for BlockQueue {
fn drop(&mut self) {
self.clear();
self.deleting.store(true, AtomicOrdering::Relaxed);
self.more_to_verify.notify_all();
for t in self.verifiers.drain(..) {
t.join().unwrap();
}
}
}
#[cfg(test)]
mod tests {
use util::*;
use spec::*;
use block_queue::*;
use tests::helpers::*;
use error::*;
fn get_test_queue() -> BlockQueue {
let spec = get_test_spec();
let engine = spec.to_engine().unwrap();
BlockQueue::new(Arc::new(engine), IoChannel::disconnected())
}
#[test]
fn can_be_created() {
// TODO better test
let spec = Spec::new_test();
let engine = spec.to_engine().unwrap();
let _ = BlockQueue::new(Arc::new(engine), IoChannel::disconnected());
}
#[test]
fn can_import_blocks() {
let mut queue = get_test_queue();
if let Err(e) = queue.import_block(get_good_dummy_block()) {
panic!("error importing block that is valid by definition({:?})", e);
}
}
#[test]
fn returns_error_for_duplicates() {
let mut queue = get_test_queue();
if let Err(e) = queue.import_block(get_good_dummy_block()) {
panic!("error importing block that is valid by definition({:?})", e);
}
let duplicate_import = queue.import_block(get_good_dummy_block());
match duplicate_import {
Err(e) => {
match e {
ImportError::AlreadyQueued => {},
_ => { panic!("must return AlreadyQueued error"); }
}
}
Ok(_) => { panic!("must produce error"); }
}
}
#[test]
fn returns_error_for_verified_duplicates() {
let mut queue = get_test_queue();
if let Err(e) = queue.import_block(get_good_dummy_block()) {
panic!("error importing block that is valid by definition({:?})", e);
}
queue.drain(10);
queue.flush();
let duplicate_import = queue.import_block(get_good_dummy_block());
match duplicate_import {
Err(e) => {
match e {
ImportError::AlreadyQueued => {},
_ => { panic!("must return AlreadyQueued error"); }
}
}
Ok(_) => { panic!("must produce error"); }
}
}
}

807
ethcore/src/blockchain.rs Normal file
View File

@@ -0,0 +1,807 @@
//! Fast access to blockchain data.
use util::*;
use rocksdb::{DB, WriteBatch, Writable};
use header::*;
use extras::*;
use transaction::*;
use views::*;
/// Represents a tree route between `from` block and `to` block:
///
/// - `blocks` - a vector of hashes of all blocks, ordered from `from` to `to`.
///
/// - `ancestor` - best common ancestor of these blocks.
///
/// - `index` - an index where best common ancestor would be.
pub struct TreeRoute {
/// TODO [debris] Please document me
pub blocks: Vec<H256>,
/// TODO [debris] Please document me
pub ancestor: H256,
/// TODO [debris] Please document me
pub index: usize
}
/// Represents blockchain's in-memory cache size in bytes.
#[derive(Debug)]
pub struct CacheSize {
/// TODO [debris] Please document me
pub blocks: usize,
/// TODO [debris] Please document me
pub block_details: usize,
/// TODO [debris] Please document me
pub transaction_addresses: usize,
/// TODO [debris] Please document me
pub block_logs: usize,
/// TODO [debris] Please document me
pub blocks_blooms: usize
}
impl CacheSize {
/// Total amount used by the cache.
fn total(&self) -> usize { self.blocks + self.block_details + self.transaction_addresses + self.block_logs + self.blocks_blooms }
}
/// Information about best block gathered together
struct BestBlock {
pub hash: H256,
pub number: BlockNumber,
pub total_difficulty: U256
}
impl BestBlock {
fn new() -> BestBlock {
BestBlock {
hash: H256::new(),
number: 0,
total_difficulty: U256::from(0)
}
}
}
/// Interface for querying blocks by hash and by number.
pub trait BlockProvider {
/// Returns true if the given block is known
/// (though not necessarily a part of the canon chain).
fn is_known(&self, hash: &H256) -> bool;
/// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes>;
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails>;
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256>;
/// Get the partial-header of a block.
fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
}
/// Get a list of uncles for a given block.
/// Returns None if block deos not exist.
fn uncles(&self, hash: &H256) -> Option<Vec<Header>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncles())
}
/// Get a list of uncle hashes for a given block.
/// Returns None if block does not exist.
fn uncle_hashes(&self, hash: &H256) -> Option<Vec<H256>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).uncle_hashes())
}
/// Get the number of given block's hash.
fn block_number(&self, hash: &H256) -> Option<BlockNumber> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
}
/// Get a list of transactions for a given block.
/// Returns None if block deos not exist.
fn transactions(&self, hash: &H256) -> Option<Vec<Transaction>> {
self.block(hash).map(|bytes| BlockView::new(&bytes).transactions())
}
/// Returns reference to genesis hash.
fn genesis_hash(&self) -> H256 {
self.block_hash(0).expect("Genesis hash should always exist")
}
/// Returns the header of the genesis block.
fn genesis_header(&self) -> Header {
self.block_header(&self.genesis_hash()).unwrap()
}
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
enum CacheID {
Block(H256),
Extras(ExtrasIndex, H256),
}
struct CacheManager {
cache_usage: VecDeque<HashSet<CacheID>>,
in_use: HashSet<CacheID>,
}
/// Structure providing fast access to blockchain data.
///
/// **Does not do input data verification.**
pub struct BlockChain {
best_block: RwLock<BestBlock>,
// block cache
blocks: RwLock<HashMap<H256, Bytes>>,
// extra caches
block_details: RwLock<HashMap<H256, BlockDetails>>,
block_hashes: RwLock<HashMap<BlockNumber, H256>>,
transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
block_logs: RwLock<HashMap<H256, BlockLogBlooms>>,
blocks_blooms: RwLock<HashMap<H256, BlocksBlooms>>,
extras_db: DB,
blocks_db: DB,
cache_man: RwLock<CacheManager>,
}
impl BlockProvider for BlockChain {
/// Returns true if the given block is known
/// (though not necessarily a part of the canon chain).
fn is_known(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.block_details)
}
/// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes> {
{
let read = self.blocks.read().unwrap();
if let Some(v) = read.get(hash) {
return Some(v.clone());
}
}
let opt = self.blocks_db.get(hash)
.expect("Low level database error. Some issue with disk?");
self.note_used(CacheID::Block(hash.clone()));
match opt {
Some(b) => {
let bytes: Bytes = b.to_vec();
let mut write = self.blocks.write().unwrap();
write.insert(hash.clone(), bytes.clone());
Some(bytes)
},
None => None
}
}
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.query_extras(hash, &self.block_details)
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.query_extras(&index, &self.block_hashes)
}
}
const COLLECTION_QUEUE_SIZE: usize = 2;
const MIN_CACHE_SIZE: usize = 1;
const MAX_CACHE_SIZE: usize = 1024 * 1024;
impl BlockChain {
/// Create new instance of blockchain from given Genesis
///
/// ```rust
/// extern crate ethcore_util as util;
/// extern crate ethcore;
/// use std::env;
/// use std::str::FromStr;
/// use ethcore::spec::*;
/// use ethcore::blockchain::*;
/// use ethcore::ethereum;
/// use util::hash::*;
/// use util::uint::*;
///
/// fn main() {
/// let spec = ethereum::new_frontier();
///
/// let mut dir = env::temp_dir();
/// dir.push(H32::random().hex());
///
/// let bc = BlockChain::new(&spec.genesis_block(), &dir);
///
/// let genesis_hash = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3";
/// assert_eq!(bc.genesis_hash(), H256::from_str(genesis_hash).unwrap());
/// assert!(bc.is_known(&bc.genesis_hash()));
/// assert_eq!(bc.genesis_hash(), bc.block_hash(0).unwrap());
/// }
/// ```
pub fn new(genesis: &[u8], path: &Path) -> BlockChain {
// open extras db
let mut extras_path = path.to_path_buf();
extras_path.push("extras");
let extras_db = DB::open_default(extras_path.to_str().unwrap()).unwrap();
// open blocks db
let mut blocks_path = path.to_path_buf();
blocks_path.push("blocks");
let blocks_db = DB::open_default(blocks_path.to_str().unwrap()).unwrap();
let mut cache_man = CacheManager{cache_usage: VecDeque::new(), in_use: HashSet::new()};
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));
let bc = BlockChain {
best_block: RwLock::new(BestBlock::new()),
blocks: RwLock::new(HashMap::new()),
block_details: RwLock::new(HashMap::new()),
block_hashes: RwLock::new(HashMap::new()),
transaction_addresses: RwLock::new(HashMap::new()),
block_logs: RwLock::new(HashMap::new()),
blocks_blooms: RwLock::new(HashMap::new()),
extras_db: extras_db,
blocks_db: blocks_db,
cache_man: RwLock::new(cache_man),
};
// load best block
let best_block_hash = match bc.extras_db.get(b"best").unwrap() {
Some(best) => H256::from_slice(&best),
None => {
// best block does not exist
// we need to insert genesis into the cache
let block = BlockView::new(genesis);
let header = block.header_view();
let hash = block.sha3();
let details = BlockDetails {
number: header.number(),
total_difficulty: header.difficulty(),
parent: header.parent_hash(),
children: vec![]
};
bc.blocks_db.put(&hash, genesis).unwrap();
let batch = WriteBatch::new();
batch.put_extras(&hash, &details);
batch.put_extras(&header.number(), &hash);
batch.put(b"best", &hash).unwrap();
bc.extras_db.write(batch).unwrap();
hash
}
};
{
let mut best_block = bc.best_block.write().unwrap();
best_block.number = bc.block_number(&best_block_hash).unwrap();
best_block.total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty;
best_block.hash = best_block_hash;
}
bc
}
/// Returns a tree route between `from` and `to`, which is a tuple of:
///
/// - a vector of hashes of all blocks, ordered from `from` to `to`.
///
/// - common ancestor of these blocks.
///
/// - an index where best common ancestor would be
///
/// 1.) from newer to older
///
/// - bc: `A1 -> A2 -> A3 -> A4 -> A5`
/// - from: A5, to: A4
/// - route:
///
/// ```json
/// { blocks: [A5], ancestor: A4, index: 1 }
/// ```
///
/// 2.) from older to newer
///
/// - bc: `A1 -> A2 -> A3 -> A4 -> A5`
/// - from: A3, to: A4
/// - route:
///
/// ```json
/// { blocks: [A4], ancestor: A3, index: 0 }
/// ```
///
/// 3.) fork:
///
/// - bc:
///
/// ```text
/// A1 -> A2 -> A3 -> A4
/// -> B3 -> B4
/// ```
/// - from: B4, to: A4
/// - route:
///
/// ```json
/// { blocks: [B4, B3, A3, A4], ancestor: A2, index: 2 }
/// ```
pub fn tree_route(&self, from: H256, to: H256) -> Option<TreeRoute> {
let from_details = match self.block_details(&from) {
Some(h) => h,
None => return None,
};
let to_details = match self.block_details(&to) {
Some(h) => h,
None => return None,
};
Some(self._tree_route((&from_details, &from), (&to_details, &to)))
}
/// Similar to `tree_route` function, but can be used to return a route
/// between blocks which may not be in database yet.
fn _tree_route(&self, from: (&BlockDetails, &H256), to: (&BlockDetails, &H256)) -> TreeRoute {
let mut from_branch = vec![];
let mut to_branch = vec![];
let mut from_details = from.0.clone();
let mut to_details = to.0.clone();
let mut current_from = from.1.clone();
let mut current_to = to.1.clone();
// reset from && to to the same level
while from_details.number > to_details.number {
from_branch.push(current_from);
current_from = from_details.parent.clone();
from_details = self.block_details(&from_details.parent).unwrap();
}
while to_details.number > from_details.number {
to_branch.push(current_to);
current_to = to_details.parent.clone();
to_details = self.block_details(&to_details.parent).unwrap();
}
assert_eq!(from_details.number, to_details.number);
// move to shared parent
while current_from != current_to {
from_branch.push(current_from);
current_from = from_details.parent.clone();
from_details = self.block_details(&from_details.parent).unwrap();
to_branch.push(current_to);
current_to = to_details.parent.clone();
to_details = self.block_details(&to_details.parent).unwrap();
}
let index = from_branch.len();
from_branch.extend(to_branch.into_iter().rev());
TreeRoute {
blocks: from_branch,
ancestor: current_from,
index: index
}
}
/// Inserts the block into backing cache database.
/// Expects the block to be valid and already verified.
/// If the block is already known, does nothing.
pub fn insert_block(&self, bytes: &[u8]) {
// create views onto rlp
let block = BlockView::new(bytes);
let header = block.header_view();
let hash = header.sha3();
if self.is_known(&hash) {
return;
}
// store block in db
self.blocks_db.put(&hash, &bytes).unwrap();
let (batch, new_best, details) = self.block_to_extras_insert_batch(bytes);
// update best block
let mut best_block = self.best_block.write().unwrap();
if let Some(b) = new_best {
*best_block = b;
}
// update caches
let mut write = self.block_details.write().unwrap();
write.remove(&header.parent_hash());
write.insert(hash.clone(), details);
self.note_used(CacheID::Block(hash));
// update extras database
self.extras_db.write(batch).unwrap();
}
/// Transforms block into WriteBatch that may be written into database
/// Additionally, if it's new best block it returns new best block object.
fn block_to_extras_insert_batch(&self, bytes: &[u8]) -> (WriteBatch, Option<BestBlock>, BlockDetails) {
// create views onto rlp
let block = BlockView::new(bytes);
let header = block.header_view();
// prepare variables
let hash = block.sha3();
let mut parent_details = self.block_details(&header.parent_hash()).expect("Invalid parent hash.");
let total_difficulty = parent_details.total_difficulty + header.difficulty();
let is_new_best = total_difficulty > self.best_block_total_difficulty();
let parent_hash = header.parent_hash();
// create current block details
let details = BlockDetails {
number: header.number(),
total_difficulty: total_difficulty,
parent: parent_hash.clone(),
children: vec![]
};
// prepare the batch
let batch = WriteBatch::new();
// insert new block details
batch.put_extras(&hash, &details);
// update parent details
parent_details.children.push(hash.clone());
batch.put_extras(&parent_hash, &parent_details);
// if it's not new best block, just return
if !is_new_best {
return (batch, None, details);
}
// if its new best block we need to make sure that all ancestors
// are moved to "canon chain"
// find the route between old best block and the new one
let best_hash = self.best_block_hash();
let best_details = self.block_details(&best_hash).expect("best block hash is invalid!");
let route = self._tree_route((&best_details, &best_hash), (&details, &hash));
match route.blocks.len() {
// its our parent
1 => batch.put_extras(&header.number(), &hash),
// it is a fork
i if i > 1 => {
let ancestor_number = self.block_number(&route.ancestor).unwrap();
let start_number = ancestor_number + 1;
for (index, hash) in route.blocks.iter().skip(route.index).enumerate() {
batch.put_extras(&(start_number + index as BlockNumber), hash);
}
},
// route.blocks.len() could be 0 only if inserted block is best block,
// and this is not possible at this stage
_ => { unreachable!(); }
};
// this is new best block
batch.put(b"best", &hash).unwrap();
let best_block = BestBlock {
hash: hash,
number: header.number(),
total_difficulty: total_difficulty
};
(batch, Some(best_block), details)
}
/// Returns true if transaction is known.
pub fn is_known_transaction(&self, hash: &H256) -> bool {
self.query_extras_exist(hash, &self.transaction_addresses)
}
/// Get best block hash.
pub fn best_block_hash(&self) -> H256 {
self.best_block.read().unwrap().hash.clone()
}
/// Get best block number.
pub fn best_block_number(&self) -> BlockNumber {
self.best_block.read().unwrap().number
}
/// Get best block total difficulty.
pub fn best_block_total_difficulty(&self) -> U256 {
self.best_block.read().unwrap().total_difficulty
}
/// Get the transactions' log blooms of a block.
pub fn log_blooms(&self, hash: &H256) -> Option<BlockLogBlooms> {
self.query_extras(hash, &self.block_logs)
}
fn query_extras<K, T>(&self, hash: &K, cache: &RwLock<HashMap<K, T>>) -> Option<T> where
T: Clone + Decodable + ExtrasIndexable,
K: ExtrasSliceConvertable + Eq + Hash + Clone {
{
let read = cache.read().unwrap();
if let Some(v) = read.get(hash) {
return Some(v.clone());
}
}
if let Some(h) = hash.as_h256() {
self.note_used(CacheID::Extras(T::extras_index(), h.clone()));
}
self.extras_db.get_extras(hash).map(| t: T | {
let mut write = cache.write().unwrap();
write.insert(hash.clone(), t.clone());
t
})
}
fn query_extras_exist<K, T>(&self, hash: &K, cache: &RwLock<HashMap<K, T>>) -> bool where
K: ExtrasSliceConvertable + Eq + Hash + Clone,
T: ExtrasIndexable {
{
let read = cache.read().unwrap();
if let Some(_) = read.get(hash) {
return true;
}
}
self.extras_db.extras_exists::<_, T>(hash)
}
/// Get current cache size.
pub fn cache_size(&self) -> CacheSize {
CacheSize {
blocks: self.blocks.read().unwrap().heap_size_of_children(),
block_details: self.block_details.read().unwrap().heap_size_of_children(),
transaction_addresses: self.transaction_addresses.read().unwrap().heap_size_of_children(),
block_logs: self.block_logs.read().unwrap().heap_size_of_children(),
blocks_blooms: self.blocks_blooms.read().unwrap().heap_size_of_children()
}
}
/// Let the cache system know that a cacheable item has been used.
fn note_used(&self, id: CacheID) {
let mut cache_man = self.cache_man.write().unwrap();
if !cache_man.cache_usage[0].contains(&id) {
cache_man.cache_usage[0].insert(id.clone());
if cache_man.in_use.contains(&id) {
if let Some(c) = cache_man.cache_usage.iter_mut().skip(1).find(|e|e.contains(&id)) {
c.remove(&id);
}
} else {
cache_man.in_use.insert(id);
}
}
}
/// Ticks our cache system and throws out any old data.
pub fn collect_garbage(&self, force: bool) {
// TODO: check time.
let timeout = true;
let t = self.cache_size().total();
if t < MIN_CACHE_SIZE || (!timeout && (!force || t < MAX_CACHE_SIZE)) { return; }
let mut cache_man = self.cache_man.write().unwrap();
let mut blocks = self.blocks.write().unwrap();
let mut block_details = self.block_details.write().unwrap();
let mut block_hashes = self.block_hashes.write().unwrap();
let mut transaction_addresses = self.transaction_addresses.write().unwrap();
let mut block_logs = self.block_logs.write().unwrap();
let mut blocks_blooms = self.blocks_blooms.write().unwrap();
for id in cache_man.cache_usage.pop_back().unwrap().into_iter() {
cache_man.in_use.remove(&id);
match id {
CacheID::Block(h) => { blocks.remove(&h); },
CacheID::Extras(ExtrasIndex::BlockDetails, h) => { block_details.remove(&h); },
CacheID::Extras(ExtrasIndex::TransactionAddress, h) => { transaction_addresses.remove(&h); },
CacheID::Extras(ExtrasIndex::BlockLogBlooms, h) => { block_logs.remove(&h); },
CacheID::Extras(ExtrasIndex::BlocksBlooms, h) => { blocks_blooms.remove(&h); },
_ => panic!(),
}
}
cache_man.cache_usage.push_front(HashSet::new());
// TODO: handle block_hashes properly.
block_hashes.clear();
// TODO: m_lastCollection = chrono::system_clock::now();
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use rustc_serialize::hex::FromHex;
use util::hash::*;
use blockchain::*;
use tests::helpers::*;
#[test]
fn valid_tests_extra32() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a0363659b251bf8b819179874c8cce7b9b983d7f3704cbb58a3b334431f7032871889032d09c281e1236c0c0".from_hex().unwrap();
let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
let genesis_hash = H256::from_str("3caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942").unwrap();
assert_eq!(bc.genesis_hash(), genesis_hash.clone());
assert_eq!(bc.best_block_number(), 0);
assert_eq!(bc.best_block_hash(), genesis_hash.clone());
assert_eq!(bc.block_hash(0), Some(genesis_hash.clone()));
assert_eq!(bc.block_hash(1), None);
let first = "f90285f90219a03caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0d45893a296c1490a978e0bd321b5f2635d8280365c1fe9f693d65f233e791344a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b845627cb99a00102030405060708091011121314151617181920212223242526272829303132a08ccb2837fb2923bd97e8f2d08ea32012d6e34be018c73e49a0f98843e8f47d5d88e53be49fec01012ef866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba0cb088b8d2ff76a7b2c6616c9d02fb6b7a501afbf8b69d7180b09928a1b80b5e4a06448fe7476c606582039bb72a9f6f4b4fad18507b8dfbd00eebbe151cc573cd2c0".from_hex().unwrap();
bc.insert_block(&first);
let first_hash = H256::from_str("a940e5af7d146b3b917c953a82e1966b906dace3a4e355b5b0a4560190357ea1").unwrap();
assert_eq!(bc.block_hash(0), Some(genesis_hash.clone()));
assert_eq!(bc.best_block_number(), 1);
assert_eq!(bc.best_block_hash(), first_hash.clone());
assert_eq!(bc.block_hash(1), Some(first_hash.clone()));
assert_eq!(bc.block_details(&first_hash).unwrap().parent, genesis_hash.clone());
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![first_hash.clone()]);
assert_eq!(bc.block_hash(2), None);
}
#[test]
#[allow(cyclomatic_complexity)]
fn test_small_fork() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a059262c330941f3fe2a34d16d6e3c7b30d2ceb37c6a0e9a994c494ee1a61d2410885aa4c8bf8e56e264c0c0".from_hex().unwrap();
let b1 = "f90261f901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0cb52de543653d86ccd13ba3ddf8b052525b04231c6884a4db3188a184681d878a0e78628dd45a1f8dc495594d83b76c588a3ee67463260f8b7d4a42f574aeab29aa0e9244cf7503b79c03d3a099e07a80d2dbc77bb0b502d8a89d51ac0d68dd31313b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884562791e580a051b3ecba4e3f2b49c11d42dd0851ec514b1be3138080f72a2b6e83868275d98f8877671f479c414b47f862f86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca09e2709d7ec9bbe6b1bbbf0b2088828d14cd5e8642a1fee22dc74bfa89761a7f9a04bd8813dee4be989accdb708b1c2e325a7e9c695a8024e30e89d6c644e424747c0".from_hex().unwrap();
let b2 = "f902ccf901f9a0437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70a5dc56146e5ef025e4e5726a6373c6f12fd2f6784093a19ead0a7d17fb292a040645cbce4fd399e7bb9160b4c30c40d7ee616a030d4e18ef0ed3b02bdb65911a086e608555f63628417032a011d107b36427af37d153f0da02ce3f90fdd5e8c08b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384562791e880a0e3cc39ff775cc0a32f175995b92e84b729e5c9a3563ff899e3555b908bc21d75887c3cde283f4846a6f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ba05258615c63503c0a600d6994b12ea5750d45b3c69668e2a371b4fbfb9eeff6b8a0a11be762bc90491231274a2945be35a43f23c27775b1ff24dd521702fe15f73ec0".from_hex().unwrap();
let b3a = "f90261f901f9a036fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a09dc4b1357c0b7b8108f8a098f4f9a1a274957bc9ebc22a9ae67ae81739e5b19ca007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882524d84562791eb80a074861666bd346c025889745c793b91ab9cd1e2ca19b5cf3c50d04d135b0a4d2b8809fe9587ea4cdc04f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba06fd84874d36d5de9e8e48978c03619b53a96b7ae0a4cd1ac118f103098b44801a00572596974dd7df4f9f69bd7456585618c568d8434ef6453391b89281ce12ae1c0".from_hex().unwrap();
let b3b = "f90265f901f9a036fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ab87dc338bfd6f662b1cd90bc0c9e40a1b2146a095312393c9e13ce3a5008b09a0e609b7a7d4b8a2403ec1268627ecd98783627246e8f1b26addb3ff504f76a054a0592fabf92476512952db3a69a2481a42912e668a1ee28c4c322e703bb665f8beb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882a1f084562791ee80a0fe7098fa7e4ac5d637eea81fb23f8f78346826dbab430068dd9a249d0afa99818853e1a6b201ae3545f866f86402018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d0284c04062261ca06edc9ce8e7da4cc34067beb325dcad59e5655a164a5100a50bc3eb681b12c716a0abf9053d5de65b1be81fe50d327b84de685efbeecea34e7b747180a6c6023e44c0".from_hex().unwrap();
let genesis_hash = H256::from_str("5716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2").unwrap();
let b1_hash = H256::from_str("437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4f").unwrap();
let b2_hash = H256::from_str("36fde1253128666fcb95a5956da14a73489e988bb72738717ec1d31e1cee781a").unwrap();
let b3a_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap();
let b3b_hash = H256::from_str("bf72270ae0d95c9ea39a6adab994793fddb8c10fba7391e26279474124605d54").unwrap();
// b3a is a part of canon chain, whereas b3b is part of sidechain
let best_block_hash = H256::from_str("c208f88c9f5bf7e00840439742c12e5226d9752981f3ec0521bdcb6dd08af277").unwrap();
let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
bc.insert_block(&b1);
bc.insert_block(&b2);
bc.insert_block(&b3a);
bc.insert_block(&b3b);
assert_eq!(bc.best_block_hash(), best_block_hash);
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
assert_eq!(bc.block_number(&b1_hash).unwrap(), 1);
assert_eq!(bc.block_number(&b2_hash).unwrap(), 2);
assert_eq!(bc.block_number(&b3a_hash).unwrap(), 3);
assert_eq!(bc.block_number(&b3b_hash).unwrap(), 3);
assert_eq!(bc.block_hash(0).unwrap(), genesis_hash);
assert_eq!(bc.block_hash(1).unwrap(), b1_hash);
assert_eq!(bc.block_hash(2).unwrap(), b2_hash);
assert_eq!(bc.block_hash(3).unwrap(), b3a_hash);
// test trie route
let r0_1 = bc.tree_route(genesis_hash.clone(), b1_hash.clone()).unwrap();
assert_eq!(r0_1.ancestor, genesis_hash);
assert_eq!(r0_1.blocks, [b1_hash.clone()]);
assert_eq!(r0_1.index, 0);
let r0_2 = bc.tree_route(genesis_hash.clone(), b2_hash.clone()).unwrap();
assert_eq!(r0_2.ancestor, genesis_hash);
assert_eq!(r0_2.blocks, [b1_hash.clone(), b2_hash.clone()]);
assert_eq!(r0_2.index, 0);
let r1_3a = bc.tree_route(b1_hash.clone(), b3a_hash.clone()).unwrap();
assert_eq!(r1_3a.ancestor, b1_hash);
assert_eq!(r1_3a.blocks, [b2_hash.clone(), b3a_hash.clone()]);
assert_eq!(r1_3a.index, 0);
let r1_3b = bc.tree_route(b1_hash.clone(), b3b_hash.clone()).unwrap();
assert_eq!(r1_3b.ancestor, b1_hash);
assert_eq!(r1_3b.blocks, [b2_hash.clone(), b3b_hash.clone()]);
assert_eq!(r1_3b.index, 0);
let r3a_3b = bc.tree_route(b3a_hash.clone(), b3b_hash.clone()).unwrap();
assert_eq!(r3a_3b.ancestor, b2_hash);
assert_eq!(r3a_3b.blocks, [b3a_hash.clone(), b3b_hash.clone()]);
assert_eq!(r3a_3b.index, 1);
let r1_0 = bc.tree_route(b1_hash.clone(), genesis_hash.clone()).unwrap();
assert_eq!(r1_0.ancestor, genesis_hash);
assert_eq!(r1_0.blocks, [b1_hash.clone()]);
assert_eq!(r1_0.index, 1);
let r2_0 = bc.tree_route(b2_hash.clone(), genesis_hash.clone()).unwrap();
assert_eq!(r2_0.ancestor, genesis_hash);
assert_eq!(r2_0.blocks, [b2_hash.clone(), b1_hash.clone()]);
assert_eq!(r2_0.index, 2);
let r3a_1 = bc.tree_route(b3a_hash.clone(), b1_hash.clone()).unwrap();
assert_eq!(r3a_1.ancestor, b1_hash);
assert_eq!(r3a_1.blocks, [b3a_hash.clone(), b2_hash.clone()]);
assert_eq!(r3a_1.index, 2);
let r3b_1 = bc.tree_route(b3b_hash.clone(), b1_hash.clone()).unwrap();
assert_eq!(r3b_1.ancestor, b1_hash);
assert_eq!(r3b_1.blocks, [b3b_hash.clone(), b2_hash.clone()]);
assert_eq!(r3b_1.index, 2);
let r3b_3a = bc.tree_route(b3b_hash.clone(), b3a_hash.clone()).unwrap();
assert_eq!(r3b_3a.ancestor, b2_hash);
assert_eq!(r3b_3a.blocks, [b3b_hash.clone(), b3a_hash.clone()]);
assert_eq!(r3b_3a.index, 1);
}
#[test]
fn test_reopen_blockchain_db() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07dba07d6b448a186e9612e5f737d1c909dce473e53199901a302c00646d523c1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a059262c330941f3fe2a34d16d6e3c7b30d2ceb37c6a0e9a994c494ee1a61d2410885aa4c8bf8e56e264c0c0".from_hex().unwrap();
let b1 = "f90261f901f9a05716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0cb52de543653d86ccd13ba3ddf8b052525b04231c6884a4db3188a184681d878a0e78628dd45a1f8dc495594d83b76c588a3ee67463260f8b7d4a42f574aeab29aa0e9244cf7503b79c03d3a099e07a80d2dbc77bb0b502d8a89d51ac0d68dd31313b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884562791e580a051b3ecba4e3f2b49c11d42dd0851ec514b1be3138080f72a2b6e83868275d98f8877671f479c414b47f862f86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca09e2709d7ec9bbe6b1bbbf0b2088828d14cd5e8642a1fee22dc74bfa89761a7f9a04bd8813dee4be989accdb708b1c2e325a7e9c695a8024e30e89d6c644e424747c0".from_hex().unwrap();
let genesis_hash = H256::from_str("5716670833ec874362d65fea27a7cd35af5897d275b31a44944113111e4e96d2").unwrap();
let b1_hash = H256::from_str("437e51676ff10756fcfee5edd9159fa41dbcb1b2c592850450371cbecd54ee4f").unwrap();
let temp = RandomTempPath::new();
{
let bc = BlockChain::new(&genesis, temp.as_path());
assert_eq!(bc.best_block_hash(), genesis_hash);
bc.insert_block(&b1);
assert_eq!(bc.best_block_hash(), b1_hash);
}
{
let bc = BlockChain::new(&genesis, temp.as_path());
assert_eq!(bc.best_block_hash(), b1_hash);
}
}
#[test]
fn can_contain_arbitrary_block_sequence() {
let bc_result = generate_dummy_blockchain(50);
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 49);
}
#[test]
fn can_collect_garbage() {
let bc_result = generate_dummy_blockchain(3000);
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 2999);
let best_hash = bc.best_block_hash();
let mut block_header = bc.block_header(&best_hash);
while !block_header.is_none() {
block_header = bc.block_header(&block_header.unwrap().parent_hash);
}
assert!(bc.cache_size().blocks > 1024 * 1024);
for _ in 0..2 {
bc.collect_garbage(true);
}
assert!(bc.cache_size().blocks < 1024 * 1024);
}
#[test]
fn can_contain_arbitrary_block_sequence_with_extra() {
let bc_result = generate_dummy_blockchain_with_extra(25);
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 24);
}
#[test]
fn can_contain_only_genesis_block() {
let bc_result = generate_dummy_empty_blockchain();
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 0);
}
}

272
ethcore/src/builtin.rs Normal file
View File

@@ -0,0 +1,272 @@
use util::*;
use crypto::sha2::Sha256;
use crypto::ripemd160::Ripemd160;
use crypto::digest::Digest;
/// Definition of a contract whose implementation is built-in.
pub struct Builtin {
/// The gas cost of running this built-in for the given size of input data.
pub cost: Box<Fn(usize) -> U256>, // TODO: U256 should be bignum.
/// Run this built-in function with the input being the first argument and the output
/// being placed into the second.
pub execute: Box<Fn(&[u8], &mut [u8])>,
}
// Rust does not mark closurer that do not capture as Sync
// We promise that all builtins are thread safe since they only operate on given input.
unsafe impl Sync for Builtin {}
unsafe impl Send for Builtin {}
impl fmt::Debug for Builtin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<Builtin>")
}
}
impl Builtin {
/// Create a new object from components.
pub fn new(cost: Box<Fn(usize) -> U256>, execute: Box<Fn(&[u8], &mut [u8])>) -> Builtin {
Builtin {cost: cost, execute: execute}
}
/// Create a new object from a builtin-function name with a linear cost associated with input size.
pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Option<Builtin> {
new_builtin_exec(name).map(|b| {
let cost = Box::new(move|s: usize| -> U256 {
U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)
});
Self::new(cost, b)
})
}
/// Simple forwarder for cost.
pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) }
/// Simple forwarder for execute.
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
/// Create a builtin from JSON.
///
/// JSON must be of the form `{ "name": "identity", "linear": {"base": 10, "word": 20} }`.
pub fn from_json(json: &Json) -> Option<Builtin> {
// NICE: figure out a more convenient means of handing errors here.
if let Json::String(ref name) = json["name"] {
if let Json::Object(ref o) = json["linear"] {
if let Json::U64(ref word) = o["word"] {
if let Json::U64(ref base) = o["base"] {
return Self::from_named_linear(&name[..], *base as usize, *word as usize);
}
}
}
}
None
}
}
/// TODO [Gav Wood] Please document me
pub fn copy_to(src: &[u8], dest: &mut[u8]) {
// NICE: optimise
for i in 0..min(src.len(), dest.len()) {
dest[i] = src[i];
}
}
/// Create a new builtin executor according to `name`.
/// TODO: turn in to a factory with dynamic registration.
pub fn new_builtin_exec(name: &str) -> Option<Box<Fn(&[u8], &mut [u8])>> {
match name {
"identity" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
for i in 0..min(input.len(), output.len()) {
output[i] = input[i];
}
})),
"ecrecover" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
#[repr(packed)]
#[derive(Debug)]
struct InType {
hash: H256,
v: H256,
r: H256,
s: H256,
}
let mut it: InType = InType { hash: H256::new(), v: H256::new(), r: H256::new(), s: H256::new() };
it.copy_raw(input);
if it.v == H256::from(&U256::from(27)) || it.v == H256::from(&U256::from(28)) {
let s = Signature::from_rsv(&it.r, &it.s, it.v[31] - 27);
if ec::is_valid(&s) {
if let Ok(p) = ec::recover(&s, &it.hash) {
let r = p.as_slice().sha3();
// NICE: optimise and separate out into populate-like function
for i in 0..min(32, output.len()) {
output[i] = if i < 12 {0} else {r[i]};
}
}
}
}
})),
"sha256" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
let mut sha = Sha256::new();
sha.input(input);
if output.len() >= 32 {
sha.result(output);
} else {
let mut ret = H256::new();
sha.result(ret.as_slice_mut());
copy_to(&ret, output);
}
})),
"ripemd160" => Some(Box::new(move|input: &[u8], output: &mut[u8]| {
let mut sha = Ripemd160::new();
sha.input(input);
let mut ret = H256::new();
sha.result(&mut ret.as_slice_mut()[12..32]);
copy_to(&ret, output);
})),
_ => None
}
}
#[test]
fn identity() {
let f = new_builtin_exec("identity").unwrap();
let i = [0u8, 1, 2, 3];
let mut o2 = [255u8; 2];
f(&i[..], &mut o2[..]);
assert_eq!(i[0..2], o2);
let mut o4 = [255u8; 4];
f(&i[..], &mut o4[..]);
assert_eq!(i, o4);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(i, o8[..4]);
assert_eq!([255u8; 4], o8[4..]);
}
#[test]
fn sha256() {
use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("sha256").unwrap();
let i = [0u8; 0];
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
}
#[test]
fn ripemd160() {
use rustc_serialize::hex::FromHex;
let f = new_builtin_exec("ripemd160").unwrap();
let i = [0u8; 0];
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
}
#[test]
fn ecrecover() {
use rustc_serialize::hex::FromHex;
/*let k = KeyPair::from_secret(b"test".sha3()).unwrap();
let a: Address = From::from(k.public().sha3());
println!("Address: {}", a);
let m = b"hello world".sha3();
println!("Message: {}", m);
let s = k.sign(&m).unwrap();
println!("Signed: {}", s);*/
let f = new_builtin_exec("ecrecover").unwrap();
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f(&i[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
let mut o8 = [255u8; 8];
f(&i[..], &mut o8[..]);
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
let mut o34 = [255u8; 34];
f(&i[..], &mut o34[..]);
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let mut o = [255u8; 32];
f(&i_bad[..], &mut o[..]);
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/
}
#[test]
fn from_named_linear() {
let b = Builtin::from_named_linear("identity", 10, 20).unwrap();
assert_eq!((*b.cost)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30));
assert_eq!((*b.cost)(33), U256::from(50));
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
(*b.execute)(&i[..], &mut o[..]);
assert_eq!(i, o);
}
#[test]
fn from_json() {
let text = "{ \"name\": \"identity\", \"linear\": {\"base\": 10, \"word\": 20} }";
let json = Json::from_str(text).unwrap();
let b = Builtin::from_json(&json).unwrap();
assert_eq!((*b.cost)(0), U256::from(10));
assert_eq!((*b.cost)(1), U256::from(30));
assert_eq!((*b.cost)(32), U256::from(30));
assert_eq!((*b.cost)(33), U256::from(50));
let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4];
(*b.execute)(&i[..], &mut o[..]);
assert_eq!(i, o);
}

389
ethcore/src/client.rs Normal file
View File

@@ -0,0 +1,389 @@
use util::*;
use rocksdb::{Options, DB};
use blockchain::{BlockChain, BlockProvider, CacheSize};
use views::BlockView;
use error::*;
use header::BlockNumber;
use state::State;
use spec::Spec;
use engine::Engine;
use views::HeaderView;
use block_queue::{BlockQueue, BlockQueueInfo};
use service::NetSyncMessage;
use env_info::LastHashes;
use verification::*;
use block::*;
/// General block status
#[derive(Debug)]
pub enum BlockStatus {
/// Part of the blockchain.
InChain,
/// Queued for import.
Queued,
/// Known as bad.
Bad,
/// Unknown.
Unknown,
}
/// Information about the blockchain gthered together.
#[derive(Debug)]
pub struct BlockChainInfo {
/// Blockchain difficulty.
pub total_difficulty: U256,
/// Block queue difficulty.
pub pending_total_difficulty: U256,
/// Genesis block hash.
pub genesis_hash: H256,
/// Best blockchain block hash.
pub best_block_hash: H256,
/// Best blockchain block number.
pub best_block_number: BlockNumber
}
impl fmt::Display for BlockChainInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}.{}", self.best_block_number, self.best_block_hash)
}
}
/// TODO [arkpar] Please document me
pub type TreeRoute = ::blockchain::TreeRoute;
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
/// Get raw block header data by block header hash.
fn block_header(&self, hash: &H256) -> Option<Bytes>;
/// Get raw block body data by block header hash.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body(&self, hash: &H256) -> Option<Bytes>;
/// Get raw block data by block header hash.
fn block(&self, hash: &H256) -> Option<Bytes>;
/// Get block status by block header hash.
fn block_status(&self, hash: &H256) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty(&self, hash: &H256) -> Option<U256>;
/// Get raw block header data by block number.
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;
/// Get raw block body data by block number.
/// Block body is an RLP list of two items: uncles and transactions.
fn block_body_at(&self, n: BlockNumber) -> Option<Bytes>;
/// Get raw block data by block number.
fn block_at(&self, n: BlockNumber) -> Option<Bytes>;
/// Get block status by block number.
fn block_status_at(&self, n: BlockNumber) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256>;
/// Get a tree route between `from` and `to`.
/// See `BlockChain::tree_route`.
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
/// Get latest state node
fn state_data(&self, hash: &H256) -> Option<Bytes>;
/// Get raw block receipts data by block header hash.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Import a block into the blockchain.
fn import_block(&self, bytes: Bytes) -> ImportResult;
/// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo;
/// Clear block queue and abort all import activity.
fn clear_queue(&self);
/// Get blockchain information.
fn chain_info(&self) -> BlockChainInfo;
/// Get the best block header.
fn best_block_header(&self) -> Bytes {
self.block_header(&self.chain_info().best_block_hash).unwrap()
}
}
#[derive(Default, Clone, Debug, Eq, PartialEq)]
/// TODO [Gav Wood] Please document me
pub struct ClientReport {
/// TODO [Gav Wood] Please document me
pub blocks_imported: usize,
/// TODO [Gav Wood] Please document me
pub transactions_applied: usize,
/// TODO [Gav Wood] Please document me
pub gas_processed: U256,
}
impl ClientReport {
/// TODO [Gav Wood] Please document me
pub fn accrue_block(&mut self, block: &PreVerifiedBlock) {
self.blocks_imported += 1;
self.transactions_applied += block.transactions.len();
self.gas_processed += block.header.gas_used;
}
}
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
pub struct Client {
chain: Arc<RwLock<BlockChain>>,
engine: Arc<Box<Engine>>,
state_db: JournalDB,
block_queue: RwLock<BlockQueue>,
report: RwLock<ClientReport>,
uncommited_states: RwLock<HashMap<H256, JournalDB>>,
import_lock: Mutex<()>
}
const HISTORY: u64 = 1000;
impl Client {
/// Create a new client with given spec and DB path.
pub fn new(spec: Spec, path: &Path, message_channel: IoChannel<NetSyncMessage> ) -> Result<Arc<Client>, Error> {
let gb = spec.genesis_block();
let chain = Arc::new(RwLock::new(BlockChain::new(&gb, path)));
let mut opts = Options::new();
opts.set_max_open_files(256);
opts.create_if_missing(true);
opts.set_use_fsync(false);
/*
opts.set_bytes_per_sync(8388608);
opts.set_disable_data_sync(false);
opts.set_block_cache_size_mb(1024);
opts.set_table_cache_num_shard_bits(6);
opts.set_max_write_buffer_number(32);
opts.set_write_buffer_size(536870912);
opts.set_target_file_size_base(1073741824);
opts.set_min_write_buffer_number_to_merge(4);
opts.set_level_zero_stop_writes_trigger(2000);
opts.set_level_zero_slowdown_writes_trigger(0);
opts.set_compaction_style(DBUniversalCompaction);
opts.set_max_background_compactions(4);
opts.set_max_background_flushes(4);
opts.set_filter_deletes(false);
opts.set_disable_auto_compactions(false);*/
let mut state_path = path.to_path_buf();
state_path.push("state");
let db = Arc::new(DB::open(&opts, state_path.to_str().unwrap()).unwrap());
let engine = Arc::new(try!(spec.to_engine()));
let mut state_db = JournalDB::new_with_arc(db.clone());
if engine.spec().ensure_db_good(&mut state_db) {
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
}
Ok(Arc::new(Client {
chain: chain,
engine: engine.clone(),
state_db: state_db,
block_queue: RwLock::new(BlockQueue::new(engine, message_channel)),
report: RwLock::new(Default::default()),
uncommited_states: RwLock::new(HashMap::new()),
import_lock: Mutex::new(()),
}))
}
/// Flush the block import queue.
pub fn flush_queue(&self) {
self.block_queue.write().unwrap().flush();
}
/// This is triggered by a message coming from a block queue when the block is ready for insertion
pub fn import_verified_blocks(&self, _io: &IoChannel<NetSyncMessage>) -> usize {
let mut ret = 0;
let mut bad = HashSet::new();
let _import_lock = self.import_lock.lock();
let blocks = self.block_queue.write().unwrap().drain(128);
for block in blocks {
if bad.contains(&block.header.parent_hash) {
self.block_queue.write().unwrap().mark_as_bad(&block.header.hash());
bad.insert(block.header.hash());
continue;
}
let header = &block.header;
if let Err(e) = verify_block_family(&header, &block.bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
bad.insert(block.header.hash());
break;
};
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
Some(p) => p,
None => {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
bad.insert(block.header.hash());
break;
},
};
// build last hashes
let mut last_hashes = LastHashes::new();
last_hashes.resize(256, H256::new());
last_hashes[0] = header.parent_hash.clone();
for i in 0..255 {
match self.chain.read().unwrap().block_details(&last_hashes[i]) {
Some(details) => {
last_hashes[i + 1] = details.parent.clone();
},
None => break,
}
}
let db = self.state_db.clone();
let result = match enact_verified(&block, self.engine.deref().deref(), db, &parent, &last_hashes) {
Ok(b) => b,
Err(e) => {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
bad.insert(block.header.hash());
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
break;
}
};
if let Err(e) = verify_block_final(&header, result.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
break;
}
self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
let ancient = if header.number() >= HISTORY { Some(header.number() - HISTORY) } else { None };
match result.drain().commit(header.number(), &header.hash(), ancient.map(|n|(n, self.chain.read().unwrap().block_hash(n).unwrap()))) {
Ok(_) => (),
Err(e) => {
warn!(target: "client", "State DB commit failed: {:?}", e);
break;
}
}
self.report.write().unwrap().accrue_block(&block);
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
ret += 1;
}
ret
}
/// Clear cached state overlay
pub fn clear_state(&self, hash: &H256) {
self.uncommited_states.write().unwrap().remove(hash);
}
/// Get a copy of the best block's state.
pub fn state(&self) -> State {
State::from_existing(self.state_db.clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
}
/// Get info on the cache.
pub fn cache_info(&self) -> CacheSize {
self.chain.read().unwrap().cache_size()
}
/// Get the report.
pub fn report(&self) -> ClientReport {
self.report.read().unwrap().clone()
}
/// Tick the client.
pub fn tick(&self) {
self.chain.read().unwrap().collect_garbage(false);
}
}
impl BlockChainClient for Client {
fn block_header(&self, hash: &H256) -> Option<Bytes> {
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.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);
body.append_raw(rlp.at(2).as_raw(), 1);
body.out()
})
}
fn block(&self, hash: &H256) -> Option<Bytes> {
self.chain.read().unwrap().block(hash)
}
fn block_status(&self, hash: &H256) -> BlockStatus {
if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
}
fn block_total_difficulty(&self, hash: &H256) -> Option<U256> {
self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty)
}
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
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.read().unwrap().block_hash(n).and_then(|h| self.block_body(&h))
}
fn block_at(&self, n: BlockNumber) -> Option<Bytes> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block(&h))
}
fn block_status_at(&self, n: BlockNumber) -> BlockStatus {
match self.chain.read().unwrap().block_hash(n) {
Some(h) => self.block_status(&h),
None => BlockStatus::Unknown
}
}
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_total_difficulty(&h))
}
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
}
fn state_data(&self, _hash: &H256) -> Option<Bytes> {
unimplemented!();
}
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
unimplemented!();
}
fn import_block(&self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(&bytes).header();
if self.chain.read().unwrap().is_known(&header.hash()) {
return Err(ImportError::AlreadyInChain);
}
self.block_queue.write().unwrap().import_block(bytes)
}
fn queue_info(&self) -> BlockQueueInfo {
self.block_queue.read().unwrap().queue_info()
}
fn clear_queue(&self) {
self.block_queue.write().unwrap().clear();
}
fn chain_info(&self) -> BlockChainInfo {
let chain = self.chain.read().unwrap();
BlockChainInfo {
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())
}
}
}

12
ethcore/src/common.rs Normal file
View File

@@ -0,0 +1,12 @@
pub use util::*;
pub use basic_types::*;
pub use error::*;
pub use env_info::*;
pub use views::*;
pub use builtin::*;
pub use header::*;
pub use account::*;
pub use transaction::*;
pub use log_entry::*;
pub use receipt::*;
pub use action_params::*;

78
ethcore/src/engine.rs Normal file
View File

@@ -0,0 +1,78 @@
use common::*;
use block::ExecutedBlock;
use spec::Spec;
use evm::Schedule;
use evm::Factory;
/// 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.
pub trait Engine : Sync + Send {
/// The name of this engine.
fn name(&self) -> &str;
/// The version of this engine. Should be of the form
fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) }
/// The number of additional header fields required for this engine.
fn seal_fields(&self) -> usize { 0 }
/// Default values of the additional fields RLP-encoded in a raw (non-list) harness.
fn seal_rlp(&self) -> Bytes { vec![] }
/// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
/// Get the general parameters of the chain.
fn spec(&self) -> &Spec;
/// Get current EVM factory
fn vm_factory(&self) -> &Factory;
/// Get the EVM schedule for the given `env_info`.
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()) }
/// TODO [Gav Wood] Please document me
fn maximum_uncle_count(&self) -> usize { 2 }
/// TODO [Gav Wood] Please document me
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
/// Block transformation functions, before and after the transactions.
fn on_new_block(&self, _block: &mut ExecutedBlock) {}
/// TODO [Gav Wood] Please document me
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
// 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_family(&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.
// TODO: consider including State in the params.
fn verify_transaction_basic(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
/// TODO [Gav Wood] Please document me
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
/// Don't forget to call Super::populateFromParent when subclassing & overriding.
// TODO: consider including State in the params.
fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) {}
// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
// from Spec into here and removing the Spec::builtins field.
/// TODO [Gav Wood] Please document me
fn is_builtin(&self, a: &Address) -> bool { self.spec().builtins.contains_key(a) }
/// TODO [Gav Wood] Please document me
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.spec().builtins.get(a).unwrap().cost(input.len()) }
/// TODO [Gav Wood] Please document me
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); }
// TODO: sealing stuff - though might want to leave this for later.
}

94
ethcore/src/env_info.rs Normal file
View File

@@ -0,0 +1,94 @@
use util::*;
use header::BlockNumber;
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
/// for a block whose number is less than 257.
pub type LastHashes = Vec<H256>;
/// Information concerning the execution environment for a message-call/contract-creation.
#[derive(Debug)]
pub struct EnvInfo {
/// The block number.
pub number: BlockNumber,
/// The block author.
pub author: Address,
/// The block timestamp.
pub timestamp: u64,
/// The block difficulty.
pub difficulty: U256,
/// The block gas limit.
pub gas_limit: U256,
/// The last 256 block hashes.
pub last_hashes: LastHashes,
/// The gas used.
pub gas_used: U256,
}
impl EnvInfo {
/// Create empty env_info initialized with zeros
pub fn new() -> EnvInfo {
EnvInfo::default()
}
}
impl Default for EnvInfo {
fn default() -> Self {
EnvInfo {
number: 0,
author: Address::new(),
timestamp: 0,
difficulty: x!(0),
gas_limit: x!(0),
last_hashes: vec![],
gas_used: x!(0),
}
}
}
impl FromJson for EnvInfo {
fn from_json(json: &Json) -> EnvInfo {
let current_number: u64 = xjson!(&json["currentNumber"]);
EnvInfo {
number: current_number,
author: xjson!(&json["currentCoinbase"]),
difficulty: xjson!(&json["currentDifficulty"]),
gas_limit: xjson!(&json["currentGasLimit"]),
timestamp: xjson!(&json["currentTimestamp"]),
last_hashes: (1..cmp::min(current_number + 1, 257)).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
gas_used: x!(0),
}
}
}
#[cfg(test)]
mod tests {
extern crate rustc_serialize;
use super::*;
use rustc_serialize::*;
use util::from_json::FromJson;
#[test]
fn it_serializes_form_json() {
let env_info = EnvInfo::from_json(&json::Json::from_str(
r#"
{
"currentCoinbase": "0x0000000000000000000000000000000000000000",
"currentNumber": 0,
"currentDifficulty": 0,
"currentGasLimit" : 0,
"currentTimestamp" : 0
}
"#
).unwrap());
assert_eq!(env_info.number, 0);
}
#[test]
fn it_can_be_created_as_default() {
let default_env_info = EnvInfo::default();
assert_eq!(default_env_info.difficulty, x!(0));
}
}

221
ethcore/src/error.rs Normal file
View File

@@ -0,0 +1,221 @@
//! General error types for use in ethcore.
use util::*;
use header::BlockNumber;
use basic_types::LogBloom;
#[derive(Debug, PartialEq, Eq)]
/// TODO [Gav Wood] Please document me
pub struct Mismatch<T: fmt::Debug> {
/// TODO [Gav Wood] Please document me
pub expected: T,
/// TODO [Gav Wood] Please document me
pub found: T,
}
#[derive(Debug, PartialEq, Eq)]
/// TODO [Gav Wood] Please document me
pub struct OutOfBounds<T: fmt::Debug> {
/// TODO [Gav Wood] Please document me
pub min: Option<T>,
/// TODO [Gav Wood] Please document me
pub max: Option<T>,
/// TODO [Gav Wood] Please document me
pub found: T,
}
/// Result of executing the transaction.
#[derive(PartialEq, Debug)]
pub enum ExecutionError {
/// Returned when there gas paid for transaction execution is
/// lower than base gas required.
/// TODO [Gav Wood] Please document me
NotEnoughBaseGas {
/// TODO [Gav Wood] Please document me
required: U256,
/// TODO [Gav Wood] Please document me
got: U256
},
/// Returned when block (gas_used + gas) > gas_limit.
///
/// If gas =< gas_limit, upstream may try to execute the transaction
/// in next block.
BlockGasLimitReached {
/// TODO [Gav Wood] Please document me
gas_limit: U256,
/// TODO [Gav Wood] Please document me
gas_used: U256,
/// TODO [Gav Wood] Please document me
gas: U256
},
/// Returned when transaction nonce does not match state nonce.
InvalidNonce {
/// TODO [Gav Wood] Please document me
expected: U256,
/// TODO [Gav Wood] Please document me
got: U256
},
/// Returned when cost of transaction (value + gas_price * gas) exceeds
/// current sender balance.
NotEnoughCash {
/// TODO [Gav Wood] Please document me
required: U512,
/// TODO [Gav Wood] Please document me
got: U512
},
/// Returned when internal evm error occurs.
Internal
}
#[derive(Debug)]
/// TODO [Gav Wood] Please document me
pub enum TransactionError {
/// TODO [Gav Wood] Please document me
InvalidGasLimit(OutOfBounds<U256>),
}
#[derive(Debug, PartialEq, Eq)]
/// TODO [arkpar] Please document me
pub enum BlockError {
/// TODO [Gav Wood] Please document me
TooManyUncles(OutOfBounds<usize>),
/// TODO [Gav Wood] Please document me
UncleWrongGeneration,
/// TODO [Gav Wood] Please document me
ExtraDataOutOfBounds(OutOfBounds<usize>),
/// TODO [arkpar] Please document me
InvalidSealArity(Mismatch<usize>),
/// TODO [arkpar] Please document me
TooMuchGasUsed(OutOfBounds<U256>),
/// TODO [arkpar] Please document me
InvalidUnclesHash(Mismatch<H256>),
/// TODO [arkpar] Please document me
UncleTooOld(OutOfBounds<BlockNumber>),
/// TODO [arkpar] Please document me
UncleIsBrother(OutOfBounds<BlockNumber>),
/// TODO [arkpar] Please document me
UncleInChain(H256),
/// TODO [arkpar] Please document me
UncleParentNotInChain(H256),
/// TODO [arkpar] Please document me
InvalidStateRoot(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidGasUsed(Mismatch<U256>),
/// TODO [arkpar] Please document me
InvalidTransactionsRoot(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidDifficulty(Mismatch<U256>),
/// TODO [arkpar] Please document me
InvalidGasLimit(OutOfBounds<U256>),
/// TODO [arkpar] Please document me
InvalidReceiptsStateRoot(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidTimestamp(OutOfBounds<u64>),
/// TODO [arkpar] Please document me
InvalidLogBloom(Mismatch<LogBloom>),
/// TODO [arkpar] Please document me
InvalidEthashDifficulty(Mismatch<U256>),
/// TODO [arkpar] Please document me
InvalidBlockNonce(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidParentHash(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidNumber(Mismatch<BlockNumber>),
/// Block number isn't sensible.
RidiculousNumber(OutOfBounds<BlockNumber>),
/// TODO [arkpar] Please document me
UnknownParent(H256),
/// TODO [Gav Wood] Please document me
UnknownUncleParent(H256),
}
#[derive(Debug)]
/// TODO [arkpar] Please document me
pub enum ImportError {
/// TODO [arkpar] Please document me
Bad(Option<Error>),
/// TODO [arkpar] Please document me
AlreadyInChain,
/// TODO [arkpar] Please document me
AlreadyQueued,
}
impl From<Error> for ImportError {
fn from(err: Error) -> ImportError {
ImportError::Bad(Some(err))
}
}
/// Result of import block operation.
pub type ImportResult = Result<H256, ImportError>;
#[derive(Debug)]
/// General error type which should be capable of representing all errors in ethcore.
pub enum Error {
/// TODO [Gav Wood] Please document me
Util(UtilError),
/// TODO [Gav Wood] Please document me
Block(BlockError),
/// TODO [Gav Wood] Please document me
UnknownEngineName(String),
/// TODO [Gav Wood] Please document me
Execution(ExecutionError),
/// TODO [Gav Wood] Please document me
Transaction(TransactionError),
}
impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Error {
Error::Transaction(err)
}
}
impl From<BlockError> for Error {
fn from(err: BlockError) -> Error {
Error::Block(err)
}
}
impl From<ExecutionError> for Error {
fn from(err: ExecutionError) -> Error {
Error::Execution(err)
}
}
impl From<CryptoError> for Error {
fn from(err: CryptoError) -> Error {
Error::Util(UtilError::Crypto(err))
}
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Error {
Error::Util(UtilError::Decoder(err))
}
}
impl From<UtilError> for Error {
fn from(err: UtilError) -> Error {
Error::Util(err)
}
}
impl From<IoError> for Error {
fn from(err: IoError) -> Error {
Error::Util(From::from(err))
}
}
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)]
macro_rules! assimilate {
($name:ident) => (
impl From<concat_idents!($name, Error)> for Error {
fn from(err: concat_idents!($name, Error)) -> Error {
Error:: $name (err)
}
}
)
}
assimilate!(FromHex);
assimilate!(BaseData);*/

View File

@@ -0,0 +1,22 @@
use util::*;
#[inline]
/// TODO [debris] Please document me
pub fn ether() -> U256 { U256::exp10(18) }
#[inline]
/// TODO [debris] Please document me
pub fn finney() -> U256 { U256::exp10(15) }
#[inline]
/// TODO [debris] Please document me
pub fn szabo() -> U256 { U256::exp10(12) }
#[inline]
/// TODO [debris] Please document me
pub fn shannon() -> U256 { U256::exp10(9) }
#[inline]
/// TODO [debris] Please document me
pub fn wei() -> U256 { U256::exp10(0) }

View File

@@ -0,0 +1,261 @@
extern crate ethash;
use self::ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
use common::*;
use block::*;
use spec::*;
use engine::*;
use evm::Schedule;
use evm::Factory;
#[cfg(test)]
use tests::helpers::*;
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// mainnet chains in the Olympic, Frontier and Homestead eras.
pub struct Ethash {
spec: Spec,
pow: EthashManager,
factory: Factory,
u64_params: RwLock<HashMap<String, u64>>,
u256_params: RwLock<HashMap<String, U256>>,
}
impl Ethash {
/// TODO [arkpar] Please document me
pub fn new_boxed(spec: Spec) -> Box<Engine> {
Box::new(Ethash {
spec: spec,
pow: EthashManager::new(),
// TODO [todr] should this return any specific factory?
factory: Factory::default(),
u64_params: RwLock::new(HashMap::new()),
u256_params: RwLock::new(HashMap::new())
})
}
fn u64_param(&self, name: &str) -> u64 {
*self.u64_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(0u64, |a| decode(&a)))
}
fn u256_param(&self, name: &str) -> U256 {
*self.u256_params.write().unwrap().entry(name.to_owned()).or_insert_with(||
self.spec().engine_params.get(name).map_or(x!(0), |a| decode(&a)))
}
}
impl Engine for Ethash {
fn name(&self) -> &str { "Ethash" }
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
// Two fields - mix
fn seal_fields(&self) -> usize { 2 }
// Two empty data items in RLP.
fn seal_rlp(&self) -> Bytes { encode(&H64::new()).to_vec() }
/// 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 vm_factory(&self) -> &Factory {
&self.factory
}
fn schedule(&self, env_info: &EnvInfo) -> Schedule {
trace!(target: "client", "Creating schedule. param={:?}, fCML={}", self.spec().engine_params.get("frontierCompatibilityModeLimit"), self.u64_param("frontierCompatibilityModeLimit"));
match env_info.number < self.u64_param("frontierCompatibilityModeLimit") {
true => {
Schedule::new_frontier()
},
_ => {
Schedule::new_homestead()
},
}
}
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
header.difficulty = self.calculate_difficuty(header, parent);
header.gas_limit = {
let gas_floor_target: U256 = x!(3141562);
let gas_limit = parent.gas_limit;
let bound_divisor = self.u256_param("gasLimitBoundDivisor");
if gas_limit < gas_floor_target {
min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1))
} else {
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor)
}
};
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
}
/// 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).
fn on_close_block(&self, block: &mut ExecutedBlock) {
let reward = self.spec().engine_params.get("blockReward").map_or(U256::from(0u64), |a| decode(&a));
let fields = block.fields();
// Bestow block reward
fields.state.add_balance(&fields.header.author, &(reward + reward / U256::from(32) * U256::from(fields.uncles.len())));
// Bestow uncle rewards
let current_number = fields.header.number();
for u in fields.uncles.iter() {
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
}
fields.state.commit();
}
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the seal fields.
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
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 difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
&Ethash::to_ethash(header.bare_hash()),
header.nonce().low_u64(),
&Ethash::to_ethash(header.mix_hash()))));
if difficulty < header.difficulty {
return Err(From::from(BlockError::InvalidEthashDifficulty(Mismatch { expected: header.difficulty, found: difficulty })));
}
Ok(())
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
let mix = Ethash::from_ethash(result.mix_hash);
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
if mix != header.mix_hash() {
return Err(From::from(BlockError::InvalidBlockNonce(Mismatch { expected: mix, found: header.mix_hash() })));
}
if difficulty < header.difficulty {
return Err(From::from(BlockError::InvalidEthashDifficulty(Mismatch { expected: header.difficulty, found: difficulty })));
}
Ok(())
}
fn verify_block_family(&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: Some(min_gas), max: Some(max_gas), found: header.gas_limit })));
}
Ok(())
}
fn verify_transaction_basic(&self, t: &Transaction, header: &Header) -> result::Result<(), Error> {
if header.number() >= self.u64_param("frontierCompatibilityModeLimit") {
try!(t.check_low_s());
}
Ok(())
}
fn verify_transaction(&self, t: &Transaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
}
#[allow(wrong_self_convention)] // to_ethash should take self
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 = self.u256_param("minimumDifficulty");
let difficulty_bound_divisor = self.u256_param("difficultyBoundDivisor");
let duration_limit = self.u64_param("durationLimit");
let frontier_limit = self.u64_param("frontierCompatibilityModeLimit");
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 {
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty, header.timestamp, parent.timestamp);
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
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(min(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
}
fn boundary_to_difficulty(boundary: &H256) -> U256 {
U256::from((U512::one() << 256) / x!(U256::from(boundary.as_slice())))
}
fn to_ethash(hash: H256) -> EH256 {
unsafe { mem::transmute(hash) }
}
fn from_ethash(hash: EH256) -> H256 {
unsafe { mem::transmute(hash) }
}
}
impl Header {
fn nonce(&self) -> H64 {
decode(&self.seal()[1])
}
fn mix_hash(&self) -> H256 {
decode(&self.seal()[0])
}
}
#[test]
fn on_close_block() {
use super::*;
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
#[test]
fn on_close_block_with_uncle() {
use super::*;
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let last_hashes = vec![genesis_header.hash()];
let mut b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
b.push_uncle(uncle).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("478eae0e571ba000").unwrap());
assert_eq!(b.state().balance(&uncle_author), U256::from_str("3cb71f51fc558000").unwrap());
}
// TODO: difficulty test

View File

@@ -0,0 +1,79 @@
//! Ethereum protocol module.
//!
//! Contains all Ethereum network specific stuff, such as denominations and
//! consensus specifications.
/// TODO [Gav Wood] Please document me
pub mod ethash;
/// TODO [Gav Wood] Please document me
pub mod denominations;
pub use self::ethash::*;
pub use self::denominations::*;
use super::spec::*;
/// Create a new Olympic chain spec.
pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/olympic.json")) }
/// Create a new Frontier mainnet chain spec.
pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier.json")) }
/// Create a new Frontier chain spec as though it never changes to Homestead.
pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_test.json")) }
/// Create a new Homestead chain spec as though it never changed from Frontier.
pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/homestead_test.json")) }
/// Create a new Frontier main net chain spec without genesis accounts.
pub fn new_mainnet_like() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
/// Create a new Morden chain spec.
pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("../../res/ethereum/morden.json")) }
#[cfg(test)]
mod tests {
use common::*;
use state::*;
use engine::*;
use super::*;
use tests::helpers::*;
#[test]
fn ensure_db_good() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce());
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000004")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c")), U256::from(1u64) << 200);
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000000")), U256::from(0u64));
}
#[test]
fn morden() {
let morden = new_morden();
assert_eq!(morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
let _ = morden.to_engine();
}
#[test]
fn frontier() {
let frontier = new_frontier();
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
let _ = frontier.to_engine();
}
}

59
ethcore/src/evm/evm.rs Normal file
View File

@@ -0,0 +1,59 @@
//! 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,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination {
/// TODO [Tomusdrw] Please document me
destination: usize
},
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction {
/// TODO [Tomusdrw] Please document me
instruction: u8,
},
/// `StackUnderflow` when there is not enough stack elements to execute instruction
/// First parameter says how many elements were needed and the second how many were actually on Stack
StackUnderflow {
/// TODO [Tomusdrw] Please document me
instruction: &'static str,
/// TODO [Tomusdrw] Please document me
wanted: usize,
/// TODO [Tomusdrw] Please document me
on_stack: usize
},
/// When execution would exceed defined Stack Limit
OutOfStack {
/// TODO [Tomusdrw] Please document me
instruction: &'static str,
/// TODO [Tomusdrw] Please document me
wanted: usize,
/// TODO [Tomusdrw] Please document me
limit: usize
},
/// 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;
}

93
ethcore/src/evm/ext.rs Normal file
View File

@@ -0,0 +1,93 @@
//! Interface for Evm externalities.
use common::Bytes;
use util::hash::*;
use util::uint::*;
use evm::{Schedule, Error};
use env_info::*;
/// Result of externalities create function.
pub enum ContractCreateResult {
/// Returned when creation was successfull.
/// Contains an address of newly created contract and gas left.
Created(Address, U256),
/// Returned when contract creation failed.
/// VM doesn't have to know the reason.
Failed
}
/// Result of externalities call function.
pub enum MessageCallResult {
/// Returned when message call was successfull.
/// Contains gas left.
Success(U256),
/// Returned when message call failed.
/// VM doesn't have to know the reason.
Failed
}
/// Externalities interface for EVMs
pub trait Ext {
/// Returns a value for given key.
fn storage_at(&self, key: &H256) -> H256;
/// Stores a value for given key.
fn set_storage(&mut self, key: H256, value: H256);
/// Determine whether an account exists.
fn exists(&self, address: &Address) -> bool;
/// 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.
///
/// Returns gas_left and contract address if contract creation was succesfull.
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult;
/// Message call.
///
/// Returns Err, if we run out of gas.
/// Otherwise returns call_result which contains gas left
/// and true if subcall was successfull.
fn call(&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> MessageCallResult;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Bytes;
/// Creates log entry with given topics and data
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
/// 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: &U256, data: &[u8]) -> Result<U256, Error>;
/// Should be called when contract commits suicide.
/// Address to which funds should be refunded.
fn suicide(&mut self, refund_address: &Address);
/// Returns schedule.
fn schedule(&self) -> &Schedule;
/// Returns environment info.
fn env_info(&self) -> &EnvInfo;
/// Returns current depth of execution.
///
/// If contract A calls contract B, and contract B calls C,
/// then A depth is 0, B is 1, C is 2 and so on.
fn depth(&self) -> usize;
/// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self);
}

127
ethcore/src/evm/factory.rs Normal file
View File

@@ -0,0 +1,127 @@
//! Evm factory.
use std::fmt;
use evm::Evm;
#[derive(Clone)]
/// TODO [Tomusdrw] Please document me
pub enum VMType {
/// TODO [Tomusdrw] Please document me
Jit,
/// TODO [Tomusdrw] Please document me
Interpreter
}
impl fmt::Display for VMType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match *self {
VMType::Jit => "JIT",
VMType::Interpreter => "INT"
})
}
}
impl VMType {
/// Return all possible VMs (JIT, Interpreter)
#[cfg(feature="jit")]
pub fn all() -> Vec<VMType> {
vec![VMType::Jit, VMType::Interpreter]
}
/// Return all possible VMs (Interpreter)
#[cfg(not(feature="jit"))]
pub fn all() -> Vec<VMType> {
vec![VMType::Interpreter]
}
}
/// Evm factory. Creates appropriate Evm.
pub struct Factory {
evm : VMType
}
impl Factory {
/// Create fresh instance of VM
pub fn create(&self) -> Box<Evm> {
match self.evm {
VMType::Jit => {
Factory::jit()
},
VMType::Interpreter => {
Box::new(super::interpreter::Interpreter)
}
}
}
/// Create new instance of specific `VMType` factory
pub fn new(evm: VMType) -> Factory {
Factory {
evm: evm
}
}
#[cfg(feature = "jit")]
fn jit() -> Box<Evm> {
Box::new(super::jit::JitEvm)
}
#[cfg(not(feature = "jit"))]
fn jit() -> Box<Evm> {
unimplemented!()
}
}
impl Default for Factory {
/// Returns jitvm factory
#[cfg(feature = "jit")]
fn default() -> Factory {
Factory {
evm: VMType::Jit
}
}
/// Returns native rust evm factory
#[cfg(not(feature = "jit"))]
fn default() -> Factory {
Factory {
evm: VMType::Interpreter
}
}
}
#[test]
fn test_create_vm() {
let _vm = Factory::default().create();
}
/// Create tests by injecting different VM factories
#[macro_export]
macro_rules! evm_test(
($name_test: ident: $name_jit: ident, $name_int: ident) => {
#[test]
#[cfg(feature = "jit")]
fn $name_jit() {
$name_test(Factory::new(VMType::Jit));
}
#[test]
fn $name_int() {
$name_test(Factory::new(VMType::Interpreter));
}
}
);
/// Create ignored tests by injecting different VM factories
#[macro_export]
macro_rules! evm_test_ignore(
($name_test: ident: $name_jit: ident, $name_int: ident) => {
#[test]
#[ignore]
#[cfg(feature = "jit")]
fn $name_jit() {
$name_test(Factory::new(VMType::Jit));
}
#[test]
#[ignore]
fn $name_int() {
$name_test(Factory::new(VMType::Interpreter));
}
}
);

View File

@@ -0,0 +1,534 @@
//! VM Instructions list and utility functions
pub type Instruction = u8;
/// Returns true if given instruction is `PUSHN` instruction.
pub fn is_push(i: Instruction) -> bool {
i >= PUSH1 && i <= PUSH32
}
/// Returns number of bytes to read for `PUSHN` instruction
/// PUSH1 -> 1
pub fn get_push_bytes(i: Instruction) -> usize {
assert!(is_push(i), "Only for PUSH instructions.");
(i - PUSH1 + 1) as usize
}
#[test]
fn test_get_push_bytes() {
assert_eq!(get_push_bytes(PUSH1), 1);
assert_eq!(get_push_bytes(PUSH3), 3);
assert_eq!(get_push_bytes(PUSH32), 32);
}
/// Returns stack position of item to duplicate
/// DUP1 -> 0
pub fn get_dup_position(i: Instruction) -> usize {
assert!(i >= DUP1 && i <= DUP16);
(i - DUP1) as usize
}
#[test]
fn test_get_dup_position() {
assert_eq!(get_dup_position(DUP1), 0);
assert_eq!(get_dup_position(DUP5), 4);
assert_eq!(get_dup_position(DUP10), 9);
}
/// Returns stack position of item to SWAP top with
/// SWAP1 -> 1
pub fn get_swap_position(i: Instruction) -> usize {
assert!(i >= SWAP1 && i <= SWAP16);
(i - SWAP1 + 1) as usize
}
#[test]
fn test_get_swap_position() {
assert_eq!(get_swap_position(SWAP1), 1);
assert_eq!(get_swap_position(SWAP5), 5);
assert_eq!(get_swap_position(SWAP10), 10);
}
/// Returns number of topcis to take from stack
/// LOG0 -> 0
pub fn get_log_topics (i: Instruction) -> usize {
assert!(i >= LOG0 && i <= LOG4);
(i - LOG0) as usize
}
#[test]
fn test_get_log_topics() {
assert_eq!(get_log_topics(LOG0), 0);
assert_eq!(get_log_topics(LOG2), 2);
assert_eq!(get_log_topics(LOG4), 4);
}
#[derive(PartialEq)]
pub enum GasPriceTier {
/// 0 Zero
Zero,
/// 2 Quick
Base,
/// 3 Fastest
VeryLow,
/// 5 Fast
Low,
/// 8 Mid
Mid,
/// 10 Slow
High,
/// 20 Ext
Ext,
/// Multiparam or otherwise special
Special,
/// Invalid
Invalid
}
/// Returns the index in schedule for specific `GasPriceTier`
pub fn get_tier_idx (tier: GasPriceTier) -> usize {
match tier {
GasPriceTier::Zero => 0,
GasPriceTier::Base => 1,
GasPriceTier::VeryLow => 2,
GasPriceTier::Low => 3,
GasPriceTier::Mid => 4,
GasPriceTier::High => 5,
GasPriceTier::Ext => 6,
GasPriceTier::Special => 7,
GasPriceTier::Invalid => 8
}
}
pub struct InstructionInfo {
pub name: &'static str,
pub additional: usize,
pub args: usize,
pub ret: usize,
pub side_effects: bool,
pub tier: GasPriceTier
}
impl InstructionInfo {
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
InstructionInfo {
name: name,
additional: additional,
args: args,
ret: ret,
side_effects: side_effects,
tier: tier
}
}
}
/// Return details about specific instruction
pub fn get_info (instruction: Instruction) -> InstructionInfo {
match instruction {
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),
SUB => InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow),
MUL => InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low),
DIV => InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low),
SDIV => InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low),
MOD => InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low),
SMOD => InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low),
EXP => InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special),
NOT => InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow),
LT => InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow),
GT => InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow),
SLT => InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow),
SGT => InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow),
EQ => InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow),
ISZERO => InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow),
AND => InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow),
OR => InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow),
XOR => InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow),
BYTE => InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow),
ADDMOD => InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid),
MULMOD => InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid),
SIGNEXTEND => InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low),
SHA3 => InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special),
ADDRESS => InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base),
BALANCE => InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext),
ORIGIN => InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base),
CALLER => InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base),
CALLVALUE => InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base),
CALLDATALOAD => InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
CALLDATASIZE => InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base),
CALLDATACOPY => InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
CODESIZE => InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base),
CODECOPY => InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
GASPRICE => InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base),
EXTCODESIZE => InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext),
EXTCODECOPY => InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext),
BLOCKHASH => InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext),
COINBASE => InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base),
TIMESTAMP => InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base),
NUMBER => InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base),
DIFFICULTY => InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base),
GASLIMIT => InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base),
POP => InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base),
MLOAD => InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
MSTORE => InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow),
MSTORE8 => InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow),
SLOAD => InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special),
SSTORE => InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special),
JUMP => InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid),
JUMPI => InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High),
PC => InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base),
MSIZE => InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base),
GAS => InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base),
JUMPDEST => InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special),
PUSH1 => InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow),
PUSH2 => InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow),
PUSH3 => InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow),
PUSH4 => InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow),
PUSH5 => InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow),
PUSH6 => InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow),
PUSH7 => InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow),
PUSH8 => InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow),
PUSH9 => InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow),
PUSH10 => InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow),
PUSH11 => InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow),
PUSH12 => InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow),
PUSH13 => InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow),
PUSH14 => InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow),
PUSH15 => InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow),
PUSH16 => InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow),
PUSH17 => InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow),
PUSH18 => InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow),
PUSH19 => InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow),
PUSH20 => InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow),
PUSH21 => InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow),
PUSH22 => InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow),
PUSH23 => InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow),
PUSH24 => InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow),
PUSH25 => InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow),
PUSH26 => InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow),
PUSH27 => InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow),
PUSH28 => InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow),
PUSH29 => InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow),
PUSH30 => InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow),
PUSH31 => InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow),
PUSH32 => InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow),
DUP1 => InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow),
DUP2 => InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow),
DUP3 => InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow),
DUP4 => InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow),
DUP5 => InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow),
DUP6 => InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow),
DUP7 => InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow),
DUP8 => InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow),
DUP9 => InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow),
DUP10 => InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow),
DUP11 => InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow),
DUP12 => InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow),
DUP13 => InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow),
DUP14 => InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow),
DUP15 => InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow),
DUP16 => InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow),
SWAP1 => InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow),
SWAP2 => InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow),
SWAP3 => InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow),
SWAP4 => InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow),
SWAP5 => InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow),
SWAP6 => InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow),
SWAP7 => InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow),
SWAP8 => InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow),
SWAP9 => InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow),
SWAP10 => InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow),
SWAP11 => InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow),
SWAP12 => InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow),
SWAP13 => InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow),
SWAP14 => InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow),
SWAP15 => InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow),
SWAP16 => InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow),
LOG0 => InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special),
LOG1 => InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special),
LOG2 => InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special),
LOG3 => InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special),
LOG4 => InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special),
CREATE => InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special),
CALL => InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special),
CALLCODE => InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special),
RETURN => InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero),
DELEGATECALL => InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special),
SUICIDE => InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero),
_ => InstructionInfo::new("INVALID_INSTRUCTION", 0, 0, 0, false, GasPriceTier::Invalid)
}
}
/// Virtual machine bytecode instruction.
/// halts execution
pub const STOP: Instruction = 0x00;
/// addition operation
pub const ADD: Instruction = 0x01;
/// mulitplication operation
pub const MUL: Instruction = 0x02;
/// subtraction operation
pub const SUB: Instruction = 0x03;
/// integer division operation
pub const DIV: Instruction = 0x04;
/// signed integer division operation
pub const SDIV: Instruction = 0x05;
/// modulo remainder operation
pub const MOD: Instruction = 0x06;
/// signed modulo remainder operation
pub const SMOD: Instruction = 0x07;
/// unsigned modular addition
pub const ADDMOD: Instruction = 0x08;
/// unsigned modular multiplication
pub const MULMOD: Instruction = 0x09;
/// exponential operation
pub const EXP: Instruction = 0x0a;
/// extend length of signed integer
pub const SIGNEXTEND: Instruction = 0x0b;
/// less-than comparision
pub const LT: Instruction = 0x10;
/// greater-than comparision
pub const GT: Instruction = 0x11;
/// signed less-than comparision
pub const SLT: Instruction = 0x12;
/// signed greater-than comparision
pub const SGT: Instruction = 0x13;
/// equality comparision
pub const EQ: Instruction = 0x14;
/// simple not operator
pub const ISZERO: Instruction = 0x15;
/// bitwise AND operation
pub const AND: Instruction = 0x16;
/// bitwise OR operation
pub const OR: Instruction = 0x17;
/// bitwise XOR operation
pub const XOR: Instruction = 0x18;
/// bitwise NOT opertation
pub const NOT: Instruction = 0x19;
/// retrieve single byte from word
pub const BYTE: Instruction = 0x1a;
/// compute SHA3-256 hash
pub const SHA3: Instruction = 0x20;
/// get address of currently executing account
pub const ADDRESS: Instruction = 0x30;
/// get balance of the given account
pub const BALANCE: Instruction = 0x31;
/// get execution origination address
pub const ORIGIN: Instruction = 0x32;
/// get caller address
pub const CALLER: Instruction = 0x33;
/// get deposited value by the instruction/transaction responsible for this execution
pub const CALLVALUE: Instruction = 0x34;
/// get input data of current environment
pub const CALLDATALOAD: Instruction = 0x35;
/// get size of input data in current environment
pub const CALLDATASIZE: Instruction = 0x36;
/// copy input data in current environment to memory
pub const CALLDATACOPY: Instruction = 0x37;
/// get size of code running in current environment
pub const CODESIZE: Instruction = 0x38;
/// copy code running in current environment to memory
pub const CODECOPY: Instruction = 0x39;
/// get price of gas in current environment
pub const GASPRICE: Instruction = 0x3a;
/// get external code size (from another contract)
pub const EXTCODESIZE: Instruction = 0x3b;
/// copy external code (from another contract)
pub const EXTCODECOPY: Instruction = 0x3c;
/// get hash of most recent complete block
pub const BLOCKHASH: Instruction = 0x40;
/// get the block's coinbase address
pub const COINBASE: Instruction = 0x41;
/// get the block's timestamp
pub const TIMESTAMP: Instruction = 0x42;
/// get the block's number
pub const NUMBER: Instruction = 0x43;
/// get the block's difficulty
pub const DIFFICULTY: Instruction = 0x44;
/// get the block's gas limit
pub const GASLIMIT: Instruction = 0x45;
/// remove item from stack
pub const POP: Instruction = 0x50;
/// load word from memory
pub const MLOAD: Instruction = 0x51;
/// save word to memory
pub const MSTORE: Instruction = 0x52;
/// save byte to memory
pub const MSTORE8: Instruction = 0x53;
/// load word from storage
pub const SLOAD: Instruction = 0x54;
/// save word to storage
pub const SSTORE: Instruction = 0x55;
/// alter the program counter
pub const JUMP: Instruction = 0x56;
/// conditionally alter the program counter
pub const JUMPI: Instruction = 0x57;
/// get the program counter
pub const PC: Instruction = 0x58;
/// get the size of active memory
pub const MSIZE: Instruction = 0x59;
/// get the amount of available gas
pub const GAS: Instruction = 0x5a;
/// set a potential jump destination
pub const JUMPDEST: Instruction = 0x5b;
/// place 1 byte item on stack
pub const PUSH1: Instruction = 0x60;
/// place 2 byte item on stack
pub const PUSH2: Instruction = 0x61;
/// place 3 byte item on stack
pub const PUSH3: Instruction = 0x62;
/// place 4 byte item on stack
pub const PUSH4: Instruction = 0x63;
/// place 5 byte item on stack
pub const PUSH5: Instruction = 0x64;
/// place 6 byte item on stack
pub const PUSH6: Instruction = 0x65;
/// place 7 byte item on stack
pub const PUSH7: Instruction = 0x66;
/// place 8 byte item on stack
pub const PUSH8: Instruction = 0x67;
/// place 9 byte item on stack
pub const PUSH9: Instruction = 0x68;
/// place 10 byte item on stack
pub const PUSH10: Instruction = 0x69;
/// place 11 byte item on stack
pub const PUSH11: Instruction = 0x6a;
/// place 12 byte item on stack
pub const PUSH12: Instruction = 0x6b;
/// place 13 byte item on stack
pub const PUSH13: Instruction = 0x6c;
/// place 14 byte item on stack
pub const PUSH14: Instruction = 0x6d;
/// place 15 byte item on stack
pub const PUSH15: Instruction = 0x6e;
/// place 16 byte item on stack
pub const PUSH16: Instruction = 0x6f;
/// place 17 byte item on stack
pub const PUSH17: Instruction = 0x70;
/// place 18 byte item on stack
pub const PUSH18: Instruction = 0x71;
/// place 19 byte item on stack
pub const PUSH19: Instruction = 0x72;
/// place 20 byte item on stack
pub const PUSH20: Instruction = 0x73;
/// place 21 byte item on stack
pub const PUSH21: Instruction = 0x74;
/// place 22 byte item on stack
pub const PUSH22: Instruction = 0x75;
/// place 23 byte item on stack
pub const PUSH23: Instruction = 0x76;
/// place 24 byte item on stack
pub const PUSH24: Instruction = 0x77;
/// place 25 byte item on stack
pub const PUSH25: Instruction = 0x78;
/// place 26 byte item on stack
pub const PUSH26: Instruction = 0x79;
/// place 27 byte item on stack
pub const PUSH27: Instruction = 0x7a;
/// place 28 byte item on stack
pub const PUSH28: Instruction = 0x7b;
/// place 29 byte item on stack
pub const PUSH29: Instruction = 0x7c;
/// place 30 byte item on stack
pub const PUSH30: Instruction = 0x7d;
/// place 31 byte item on stack
pub const PUSH31: Instruction = 0x7e;
/// place 32 byte item on stack
pub const PUSH32: Instruction = 0x7f;
/// copies the highest item in the stack to the top of the stack
pub const DUP1: Instruction = 0x80;
/// copies the second highest item in the stack to the top of the stack
pub const DUP2: Instruction = 0x81;
/// copies the third highest item in the stack to the top of the stack
pub const DUP3: Instruction = 0x82;
/// copies the 4th highest item in the stack to the top of the stack
pub const DUP4: Instruction = 0x83;
/// copies the 5th highest item in the stack to the top of the stack
pub const DUP5: Instruction = 0x84;
/// copies the 6th highest item in the stack to the top of the stack
pub const DUP6: Instruction = 0x85;
/// copies the 7th highest item in the stack to the top of the stack
pub const DUP7: Instruction = 0x86;
/// copies the 8th highest item in the stack to the top of the stack
pub const DUP8: Instruction = 0x87;
/// copies the 9th highest item in the stack to the top of the stack
pub const DUP9: Instruction = 0x88;
/// copies the 10th highest item in the stack to the top of the stack
pub const DUP10: Instruction = 0x89;
/// copies the 11th highest item in the stack to the top of the stack
pub const DUP11: Instruction = 0x8a;
/// copies the 12th highest item in the stack to the top of the stack
pub const DUP12: Instruction = 0x8b;
/// copies the 13th highest item in the stack to the top of the stack
pub const DUP13: Instruction = 0x8c;
/// copies the 14th highest item in the stack to the top of the stack
pub const DUP14: Instruction = 0x8d;
/// copies the 15th highest item in the stack to the top of the stack
pub const DUP15: Instruction = 0x8e;
/// copies the 16th highest item in the stack to the top of the stack
pub const DUP16: Instruction = 0x8f;
/// swaps the highest and second highest value on the stack
pub const SWAP1: Instruction = 0x90;
/// swaps the highest and third highest value on the stack
pub const SWAP2: Instruction = 0x91;
/// swaps the highest and 4th highest value on the stack
pub const SWAP3: Instruction = 0x92;
/// swaps the highest and 5th highest value on the stack
pub const SWAP4: Instruction = 0x93;
/// swaps the highest and 6th highest value on the stack
pub const SWAP5: Instruction = 0x94;
/// swaps the highest and 7th highest value on the stack
pub const SWAP6: Instruction = 0x95;
/// swaps the highest and 8th highest value on the stack
pub const SWAP7: Instruction = 0x96;
/// swaps the highest and 9th highest value on the stack
pub const SWAP8: Instruction = 0x97;
/// swaps the highest and 10th highest value on the stack
pub const SWAP9: Instruction = 0x98;
/// swaps the highest and 11th highest value on the stack
pub const SWAP10: Instruction = 0x99;
/// swaps the highest and 12th highest value on the stack
pub const SWAP11: Instruction = 0x9a;
/// swaps the highest and 13th highest value on the stack
pub const SWAP12: Instruction = 0x9b;
/// swaps the highest and 14th highest value on the stack
pub const SWAP13: Instruction = 0x9c;
/// swaps the highest and 15th highest value on the stack
pub const SWAP14: Instruction = 0x9d;
/// swaps the highest and 16th highest value on the stack
pub const SWAP15: Instruction = 0x9e;
/// swaps the highest and 17th highest value on the stack
pub const SWAP16: Instruction = 0x9f;
/// Makes a log entry; no topics.
pub const LOG0: Instruction = 0xa0;
/// Makes a log entry; 1 topic.
pub const LOG1: Instruction = 0xa1;
/// Makes a log entry; 2 topics.
pub const LOG2: Instruction = 0xa2;
/// Makes a log entry; 3 topics.
pub const LOG3: Instruction = 0xa3;
/// Makes a log entry; 4 topics.
pub const LOG4: Instruction = 0xa4;
/// Maximal number of topics for log instructions
pub const MAX_NO_OF_TOPICS : usize = 4;
/// create a new account with associated code
pub const CREATE: Instruction = 0xf0;
/// message-call into an account
pub const CALL: Instruction = 0xf1;
/// message-call with another account's code only
pub const CALLCODE: Instruction = 0xf2;
/// halt execution returning output data
pub const RETURN: Instruction = 0xf3;
/// like CALLCODE but keeps caller's value and sender
pub const DELEGATECALL: Instruction = 0xf4;
/// halt execution and register account for later deletion
pub const SUICIDE: Instruction = 0xff;

File diff suppressed because it is too large Load Diff

368
ethcore/src/evm/jit.rs Normal file
View File

@@ -0,0 +1,368 @@
//! Just in time compiler execution environment.
use common::*;
use evmjit;
use evm;
/// 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()
}
}
/// 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,
address: Address
}
impl<'a> ExtAdapter<'a> {
fn new(ext: &'a mut evm::Ext, address: Address) -> Self {
ExtAdapter {
ext: ext,
address: address
}
}
}
impl<'a> evmjit::Ext for ExtAdapter<'a> {
fn sload(&self, key: *const evmjit::I256, out_value: *mut evmjit::I256) {
unsafe {
let i = H256::from_jit(&*key);
let o = self.ext.storage_at(&i);
*out_value = o.into_jit();
}
}
fn sstore(&mut self, key: *const evmjit::I256, value: *const evmjit::I256) {
let key = unsafe { H256::from_jit(&*key) };
let value = unsafe { H256::from_jit(&*value) };
let old_value = self.ext.storage_at(&key);
// if SSTORE nonzero -> zero, increment refund count
if !old_value.is_zero() && value.is_zero() {
self.ext.inc_sstore_clears();
}
self.ext.set_storage(key, 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,
value: *const evmjit::I256,
init_beg: *const u8,
init_size: u64,
address: *mut evmjit::H256) {
let gas = unsafe { U256::from(*io_gas) };
let value = unsafe { U256::from_jit(&*value) };
let code = unsafe { slice::from_raw_parts(init_beg, init_size as usize) };
// check if balance is sufficient and we are not too deep
if self.ext.balance(&self.address) >= value && self.ext.depth() < self.ext.schedule().max_depth {
match self.ext.create(&gas, &value, code) {
evm::ContractCreateResult::Created(new_address, gas_left) => unsafe {
*address = new_address.into_jit();
*io_gas = gas_left.low_u64();
},
evm::ContractCreateResult::Failed => unsafe {
*address = Address::new().into_jit();
*io_gas = 0;
}
}
} else {
unsafe { *address = Address::new().into_jit(); }
}
}
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 {
let mut gas = unsafe { U256::from(*io_gas) };
let mut call_gas = U256::from(call_gas);
let mut gas_cost = call_gas;
let receive_address = unsafe { Address::from_jit(&*receive_address) };
let code_address = unsafe { Address::from_jit(&*code_address) };
let value = unsafe { U256::from_jit(&*value) };
// receive address and code address are the same in normal calls
let is_callcode = receive_address != code_address;
if !is_callcode && !self.ext.exists(&code_address) {
gas_cost = gas_cost + U256::from(self.ext.schedule().call_new_account_gas);
}
if value > U256::zero() {
assert!(self.ext.schedule().call_value_transfer_gas > self.ext.schedule().call_stipend, "overflow possible");
gas_cost = gas_cost + U256::from(self.ext.schedule().call_value_transfer_gas);
call_gas = call_gas + U256::from(self.ext.schedule().call_stipend);
}
if gas_cost > gas {
unsafe {
*io_gas = -1i64 as u64;
return false;
}
}
gas = gas - gas_cost;
// check if balance is sufficient and we are not too deep
if self.ext.balance(&self.address) < value || self.ext.depth() >= self.ext.schedule().max_depth {
unsafe {
*io_gas = (gas + call_gas).low_u64();
return false;
}
}
match self.ext.call(
&call_gas,
&self.address,
&receive_address,
Some(value),
unsafe { slice::from_raw_parts(in_beg, in_size as usize) },
&code_address,
unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) {
evm::MessageCallResult::Success(gas_left) => unsafe {
*io_gas = (gas + gas_left).low_u64();
true
},
evm::MessageCallResult::Failed => unsafe {
*io_gas = gas.low_u64();
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);
}
}
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 {
// Dirty hack. This is unsafe, but we interact with ffi, so it's justified.
let ext_adapter: ExtAdapter<'static> = unsafe { ::std::mem::transmute(ExtAdapter::new(ext, params.address.clone())) };
let mut ext_handle = evmjit::ExtHandle::new(ext_adapter);
assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63");
let call_data = params.data.unwrap_or_else(Vec::new);
let code = params.code.unwrap_or_else(Vec::new);
let mut data = evmjit::RuntimeDataHandle::new();
data.gas = params.gas.low_u64() as i64;
data.gas_price = params.gas_price.low_u64() as i64;
data.call_data = call_data.as_ptr();
data.call_data_size = call_data.len() as u64;
mem::forget(call_data);
data.code = code.as_ptr();
data.code_size = code.len() as u64;
data.code_hash = code.sha3().into_jit();
mem::forget(code);
data.address = params.address.into_jit();
data.caller = params.sender.into_jit();
data.origin = params.origin.into_jit();
data.call_value = match params.value {
ActionValue::Transfer(val) => val.into_jit(),
ActionValue::Apparent(val) => val.into_jit()
};
data.author = ext.env_info().author.clone().into_jit();
data.difficulty = ext.env_info().difficulty.into_jit();
data.gas_limit = ext.env_info().gas_limit.into_jit();
data.number = ext.env_info().number;
// don't really know why jit timestamp is int..
data.timestamp = ext.env_info().timestamp as i64;
let mut context = unsafe { evmjit::ContextHandle::new(data, &mut ext_handle) };
let res = context.exec();
match res {
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
evmjit::ReturnCode::Return => ext.ret(&U256::from(context.gas_left()), context.output_data()),
evmjit::ReturnCode::Suicide => {
ext.suicide(&Address::from_jit(&context.suicide_refund_address()));
Ok(U256::from(context.gas_left()))
},
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
_err => Err(evm::Error::Internal)
}
}
}
#[test]
fn test_to_and_from_u256() {
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() {
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);
let j: ::evmjit::H256 = h.clone().into_jit();
let h2 = H256::from_jit(&j);
assert_eq!(h, h2);
}
#[test]
fn test_to_and_from_address() {
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);
let j: ::evmjit::H256 = a.clone().into_jit();
let a2 = Address::from_jit(&j);
assert_eq!(a, a2);
}

21
ethcore/src/evm/mod.rs Normal file
View File

@@ -0,0 +1,21 @@
//! Ethereum virtual machine.
pub mod ext;
pub mod evm;
/// TODO [Tomusdrw] Please document me
pub mod interpreter;
#[macro_use]
pub mod factory;
pub mod schedule;
mod instructions;
#[cfg(feature = "jit" )]
mod jit;
#[cfg(test)]
mod tests;
pub use self::evm::{Evm, Error, Result};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::Factory;
pub use self::schedule::Schedule;
pub use self::factory::VMType;

115
ethcore/src/evm/schedule.rs Normal file
View File

@@ -0,0 +1,115 @@
//! Cost schedule and other parameterisations for the EVM.
/// Definition of the cost schedule and other parameterisations for the EVM.
pub struct Schedule {
/// Does it support exceptional failed code deposit
pub exceptional_failed_code_deposit: bool,
/// Does it have a delegate cal
pub have_delegate_call: bool,
/// VM stack limit
pub stack_limit: usize,
/// Max number of nested calls/creates
pub max_depth: usize,
/// Gas prices for instructions in all tiers
pub tier_step_gas: [usize; 8],
/// Gas price for `EXP` opcode
pub exp_gas: usize,
/// Additional gas for `EXP` opcode for each byte of exponent
pub exp_byte_gas: usize,
/// Gas price for `SHA3` opcode
pub sha3_gas: usize,
/// Additional gas for `SHA3` opcode for each word of hashed memory
pub sha3_word_gas: usize,
/// Gas price for loading from storage
pub sload_gas: usize,
/// Gas price for setting new value to storage (`storage==0`, `new!=0`)
pub sstore_set_gas: usize,
/// Gas price for altering value in storage
pub sstore_reset_gas: usize,
/// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`)
pub sstore_refund_gas: usize,
/// Gas price for `JUMPDEST` opcode
pub jumpdest_gas: usize,
/// Gas price for `LOG*`
pub log_gas: usize,
/// Additional gas for data in `LOG*`
pub log_data_gas: usize,
/// Additional gas for each topic in `LOG*`
pub log_topic_gas: usize,
/// Gas price for `CREATE` opcode
pub create_gas: usize,
/// Gas price for `*CALL*` opcodes
pub call_gas: usize,
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
pub call_stipend: usize,
/// Additional gas required for value transfer (`CALL|CALLCODE`)
pub call_value_transfer_gas: usize,
/// Additional gas for creating new account (`CALL|CALLCODE`)
pub call_new_account_gas: usize,
/// Refund for SUICIDE
pub suicide_refund_gas: usize,
/// Gas for used memory
pub memory_gas: usize,
/// Coefficient used to convert memory size to gas price for memory
pub quad_coeff_div: usize,
/// Cost for contract length when executing `CREATE`
pub create_data_gas: usize,
/// Transaction cost
pub tx_gas: usize,
/// `CREATE` transaction cost
pub tx_create_gas: usize,
/// Additional cost for empty data transaction
pub tx_data_zero_gas: usize,
/// Aditional cost for non-empty data transaction
pub tx_data_non_zero_gas: usize,
/// Gas price for copying memory
pub copy_gas: usize,
}
impl Schedule {
/// Schedule for the Frontier-era of the Ethereum main net.
pub fn new_frontier() -> Schedule {
Self::new(false, false, 21000)
}
/// Schedule for the Homestead-era of the Ethereum main net.
pub fn new_homestead() -> Schedule {
Self::new(true, true, 53000)
}
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
Schedule{
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
stack_limit: 1024,
max_depth: 1024,
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
exp_gas: 10,
exp_byte_gas: 10,
sha3_gas: 30,
sha3_word_gas: 6,
sload_gas: 50,
sstore_set_gas: 20000,
sstore_reset_gas: 5000,
sstore_refund_gas: 15000,
jumpdest_gas: 1,
log_gas: 375,
log_data_gas: 8,
log_topic_gas: 375,
create_gas: 32000,
call_gas: 40,
call_stipend: 2300,
call_value_transfer_gas: 9000,
call_new_account_gas: 25000,
suicide_refund_gas: 24000,
memory_gas: 3,
quad_coeff_div: 512,
create_data_gas: 200,
tx_gas: 21000,
tx_create_gas: tcg,
tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68,
copy_gas: 3,
}
}
}

475
ethcore/src/evm/tests.rs Normal file
View File

@@ -0,0 +1,475 @@
use common::*;
use evm;
use evm::{Ext, Schedule, Factory, VMType, ContractCreateResult, MessageCallResult};
struct FakeLogEntry {
topics: Vec<H256>,
data: Bytes
}
/// Fake externalities test structure.
///
/// Can't do recursive calls.
#[derive(Default)]
struct FakeExt {
store: HashMap<H256, H256>,
_balances: HashMap<Address, U256>,
blockhashes: HashMap<U256, H256>,
codes: HashMap<Address, Bytes>,
logs: Vec<FakeLogEntry>,
_suicides: HashSet<Address>,
info: EnvInfo,
schedule: Schedule
}
impl FakeExt {
fn new() -> Self {
FakeExt::default()
}
}
impl Default for Schedule {
fn default() -> Self {
Schedule::new_frontier()
}
}
impl Ext for FakeExt {
fn storage_at(&self, key: &H256) -> H256 {
self.store.get(key).unwrap_or(&H256::new()).clone()
}
fn set_storage(&mut self, key: H256, value: H256) {
self.store.insert(key, value);
}
fn exists(&self, _address: &Address) -> bool {
unimplemented!();
}
fn balance(&self, _address: &Address) -> U256 {
unimplemented!();
}
fn blockhash(&self, number: &U256) -> H256 {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}
fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> ContractCreateResult {
unimplemented!();
}
fn call(&mut self,
_gas: &U256,
_sender_address: &Address,
_receive_address: &Address,
_value: Option<U256>,
_data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
unimplemented!();
}
fn extcode(&self, address: &Address) -> Bytes {
self.codes.get(address).unwrap_or(&Bytes::new()).clone()
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
self.logs.push(FakeLogEntry {
topics: topics,
data: data.to_vec()
});
}
fn ret(&mut self, _gas: &U256, _data: &[u8]) -> result::Result<U256, evm::Error> {
unimplemented!();
}
fn suicide(&mut self, _refund_address: &Address) {
unimplemented!();
}
fn schedule(&self) -> &Schedule {
&self.schedule
}
fn env_info(&self) -> &EnvInfo {
&self.info
}
fn depth(&self) -> usize {
unimplemented!();
}
fn inc_sstore_clears(&mut self) {
unimplemented!();
}
}
#[test]
fn test_stack_underflow() {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "01600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let err = {
let vm : Box<evm::Evm> = Box::new(super::interpreter::Interpreter);
vm.exec(params, &mut ext).unwrap_err()
};
match err {
evm::Error::StackUnderflow {wanted, on_stack, ..} => {
assert_eq!(wanted, 2);
assert_eq!(on_stack, 0);
}
_ => {
assert!(false, "Expected StackUndeflow")
}
};
}
evm_test!{test_add: test_add_jit, test_add_int}
fn test_add(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_988));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap());
}
evm_test!{test_sha3: test_sha3_jit, test_sha3_int}
fn test_sha3(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "6000600020600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_961));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap());
}
evm_test!{test_address: test_address_jit, test_address_int}
fn test_address(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "30600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap());
}
evm_test!{test_origin: test_origin_jit, test_origin_int}
fn test_origin(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let origin = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "32600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.origin = origin.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
}
// TODO [todr] Fails with Signal 11 on JIT
evm_test!{test_sender: test_sender_jit, test_sender_int}
fn test_sender(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "33600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap());
}
evm_test!{test_extcodecopy: test_extcodecopy_jit, test_extcodecopy_int}
fn test_extcodecopy(factory: super::Factory) {
// 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("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let code = "333b60006000333c600051600055".from_hex().unwrap();
let sender_code = "6005600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.codes.insert(sender, sender_code);
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_935));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("6005600055000000000000000000000000000000000000000000000000000000").unwrap());
}
evm_test!{test_log_empty: test_log_empty_jit, test_log_empty_int}
fn test_log_empty(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "60006000a0".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(99_619));
assert_eq!(ext.logs.len(), 1);
assert_eq!(ext.logs[0].topics.len(), 0);
assert_eq!(ext.logs[0].data, vec![]);
}
evm_test!{test_log_sender: test_log_sender_jit, test_log_sender_int}
fn test_log_sender(factory: super::Factory) {
// 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 sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let code = "60ff6000533360206000a1".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(98_974));
assert_eq!(ext.logs.len(), 1);
assert_eq!(ext.logs[0].topics.len(), 1);
assert_eq!(ext.logs[0].topics[0], H256::from_str("000000000000000000000000cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap());
}
evm_test!{test_blockhash: test_blockhash_jit, test_blockhash_int}
fn test_blockhash(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "600040600055".from_hex().unwrap();
let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.blockhashes.insert(U256::zero(), blockhash.clone());
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_974));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash);
}
evm_test!{test_calldataload: test_calldataload_jit, test_calldataload_int}
fn test_calldataload(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "600135600055".from_hex().unwrap();
let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap();
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code);
params.data = Some(data);
let mut ext = FakeExt::new();
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_991));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("23ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").unwrap());
}
evm_test!{test_author: test_author_jit, test_author_int}
fn test_author(factory: super::Factory) {
let author = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = "41600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.info.author = author;
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap());
}
evm_test!{test_timestamp: test_timestamp_jit, test_timestamp_int}
fn test_timestamp(factory: super::Factory) {
let timestamp = 0x1234;
let code = "42600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.info.timestamp = timestamp;
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
evm_test!{test_number: test_number_jit, test_number_int}
fn test_number(factory: super::Factory) {
let number = 0x1234;
let code = "43600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.info.number = number;
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
evm_test!{test_difficulty: test_difficulty_jit, test_difficulty_int}
fn test_difficulty(factory: super::Factory) {
let difficulty = U256::from(0x1234);
let code = "44600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.info.difficulty = difficulty;
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}
evm_test!{test_gas_limit: test_gas_limit_jit, test_gas_limit_int}
fn test_gas_limit(factory: super::Factory) {
let gas_limit = U256::from(0x1234);
let code = "45600055".from_hex().unwrap();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(code);
let mut ext = FakeExt::new();
ext.info.gas_limit = gas_limit;
let gas_left = {
let vm = factory.create();
vm.exec(params, &mut ext).unwrap()
};
assert_eq!(gas_left, U256::from(79_995));
assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("0000000000000000000000000000000000000000000000000000000000001234").unwrap());
}

854
ethcore/src/executive.rs Normal file
View File

@@ -0,0 +1,854 @@
//! Transaction Execution environment.
use common::*;
use state::*;
use engine::*;
use evm::{self, Ext};
use externalities::*;
use substate::*;
use crossbeam;
/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM)
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
/// Maybe something like here: https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp
const MAX_VM_DEPTH_FOR_THREAD: usize = 64;
/// 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())
}
/// Transaction execution receipt.
#[derive(Debug)]
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>,
/// Addresses of contracts created during execution of transaction.
/// Ordered from earliest creation.
///
/// eg. sender creates contract A and A in constructor creates contract B
///
/// B creation ends first, and it will be the first element of the vector.
pub contracts_created: Vec<Address>
}
/// Transaction execution result.
pub type ExecutionResult = Result<Executed, ExecutionError>;
/// Transaction executor.
pub struct Executive<'a> {
state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize
}
impl<'a> Executive<'a> {
/// Basic constructor.
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.
pub 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
}
}
/// Creates `Externalities` from `Executive`.
pub fn as_externalities<'_>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_>) -> Externalities {
Externalities::new(self.state, self.info, self.engine, self.depth, origin_info, substate, output)
}
/// This funtion should be used to execute transaction.
pub fn transact(&'a mut self, t: &Transaction) -> Result<Executed, Error> {
let sender = try!(t.sender());
let nonce = self.state.nonce(&sender);
let schedule = self.engine.schedule(self.info);
let base_gas_required = U256::from(t.gas_required(&schedule));
if t.gas < base_gas_required {
return Err(From::from(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas }));
}
let init_gas = t.gas - base_gas_required;
// validate transaction nonce
if t.nonce != nonce {
return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, got: t.nonce }));
}
// validate if transaction fits into given block
if self.info.gas_used + t.gas > self.info.gas_limit {
return Err(From::from(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 = U512::from(t.gas) * U512::from(t.gas_price);
let total_cost = U512::from(t.value) + gas_cost;
// avoid unaffordable transactions
if U512::from(balance) < total_cost {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) }));
}
// NOTE: there can be no invalid transactions from this point.
self.state.inc_nonce(&sender);
self.state.sub_balance(&sender, &U256::from(gas_cost));
let mut substate = Substate::new();
let res = match *t.action() {
Action::Create => {
let new_address = contract_address(&sender, &nonce);
let params = ActionParams {
code_address: new_address.clone(),
address: new_address,
sender: sender.clone(),
origin: sender.clone(),
gas: init_gas,
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
code: Some(t.data.clone()),
data: None,
};
self.create(params, &mut substate)
},
Action::Call(ref address) => {
let params = ActionParams {
code_address: address.clone(),
address: address.clone(),
sender: sender.clone(),
origin: sender.clone(),
gas: init_gas,
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
code: self.state.code(address),
data: Some(t.data.clone()),
};
// TODO: move output upstream
let mut out = vec![];
self.call(params, &mut substate, BytesRef::Flexible(&mut out))
}
};
// finalize here!
Ok(try!(self.finalize(t, substate, res)))
}
fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result {
// Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory();
return vm_factory.create().exec(params, &mut ext);
}
// Start in new thread to reset stack
// TODO [todr] No thread builder yet, so we need to reset once for a while
// https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| {
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy);
let vm_factory = self.engine.vm_factory();
scope.spawn(move || {
vm_factory.create().exec(params, &mut ext)
})
}).join()
}
/// 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`.
pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();
// at first, transfer value to destination
if let ActionValue::Transfer(val) = params.value {
self.state.transfer_balance(&params.sender, &params.address, &val);
}
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
if self.engine.is_builtin(&params.code_address) {
// if destination is builtin, try to execute it
let default = [];
let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] };
let cost = self.engine.cost_of_builtin(&params.code_address, data);
match cost <= params.gas {
true => {
self.engine.execute_builtin(&params.code_address, data, &mut output);
Ok(params.gas - cost)
},
// just drain the whole gas
false => {
self.state.revert(backup);
Err(evm::Error::OutOfGas)
}
}
} else if params.code.is_some() {
// if destination is a contract, do normal message call
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new();
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output))
};
trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count);
trace!("exec: substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate);
self.enact_result(&res, substate, unconfirmed_substate, backup);
trace!("exec: new substate={:?}\n", substate);
res
} 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.
pub fn create(&mut self, params: ActionParams, substate: &mut Substate) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new();
// create contract and transfer value to it if necessary
let prev_bal = self.state.balance(&params.address);
if let ActionValue::Transfer(val) = params.value {
self.state.sub_balance(&params.sender, &val);
self.state.new_contract(&params.address, val + prev_bal);
} else {
self.state.new_contract(&params.address, prev_bal);
}
let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract)
};
self.enact_result(&res, substate, unconfirmed_substate, backup);
res
}
/// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &Transaction, substate: Substate, result: evm::Result) -> ExecutionResult {
let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero
let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_clears_count;
// refunds from contract suicides
let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len());
let refunds_bound = sstore_refunds + suicide_refunds;
// real ammount to refund
let gas_left_prerefund = match result { Ok(x) => x, _ => x!(0) };
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2));
let gas_left = gas_left_prerefund + refunded;
let gas_used = t.gas - gas_left;
let refund_value = gas_left * t.gas_price;
let fees_value = gas_used * t.gas_price;
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, t.sender().unwrap());
self.state.add_balance(&t.sender().unwrap(), &refund_value);
trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author);
self.state.add_balance(&self.info.author, &fees_value);
// perform suicides
for address in &substate.suicides {
self.state.kill_account(address);
}
match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(_) => {
Ok(Executed {
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
contracts_created: vec![]
})
},
_ => {
Ok(Executed {
gas: t.gas,
gas_used: gas_used,
refunded: refunded,
cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs,
contracts_created: substate.contracts_created,
})
},
}
}
fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) {
match *result {
Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..})
| Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::StackUnderflow {..})
| Err(evm::Error::OutOfStack {..}) => {
self.state.revert(backup);
},
Ok(_) | Err(evm::Error::Internal) => substate.accrue(un_substate)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::*;
use ethereum;
use engine::*;
use spec::*;
use evm::{Schedule, Factory, VMType};
use substate::*;
use tests::helpers::*;
struct TestEngine {
factory: Factory,
spec: Spec,
max_depth: usize
}
impl TestEngine {
fn new(max_depth: usize, factory: Factory) -> TestEngine {
TestEngine {
factory: factory,
spec: ethereum::new_frontier_test(),
max_depth: max_depth
}
}
}
impl Engine for TestEngine {
fn name(&self) -> &str { "TestEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn vm_factory(&self) -> &Factory {
&self.factory
}
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
let mut schedule = Schedule::new_frontier();
schedule.max_depth = self.max_depth;
schedule
}
}
#[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)));
}
// TODO: replace params with transactions!
evm_test!{test_sender_balance: test_sender_balance_jit, test_sender_balance_int}
fn test_sender_balance(factory: Factory) {
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let address = contract_address(&sender, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = Some("3331600055".from_hex().unwrap());
params.value = ActionValue::Transfer(U256::from(0x7));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(0x100u64));
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params, &mut substate).unwrap()
};
assert_eq!(gas_left, U256::from(79_975));
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));
// 0 cause contract hasn't returned
assert_eq!(substate.contracts_created.len(), 0);
// TODO: just test state root.
}
evm_test!{test_create_contract: test_create_contract_jit, test_create_contract_int}
fn test_create_contract(factory: Factory) {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 17 - push 17
// f0 - create
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params, &mut substate).unwrap()
};
assert_eq!(gas_left, U256::from(62_976));
// ended with max depth
assert_eq!(substate.contracts_created.len(), 0);
}
evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int}
fn test_create_contract_value_too_high(factory: Factory) {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 e6 - push 230
// f0 - create a contract trying to send 230.
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params, &mut substate).unwrap()
};
assert_eq!(gas_left, U256::from(62_976));
assert_eq!(substate.contracts_created.len(), 0);
}
evm_test!{test_create_contract_without_max_depth: test_create_contract_without_max_depth_jit, test_create_contract_without_max_depth_int}
fn test_create_contract_without_max_depth(factory: Factory) {
// code:
//
// 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
// 60 00 - push 0
// 52
// 60 1d - push 29
// 60 03 - push 3
// 60 17 - push 17
// f0 - create
// 60 00 - push 0
// 55 sstore
//
// other code:
//
// 60 10 - push 16
// 80 - duplicate first stack item
// 60 0c - push 12
// 60 00 - push 0
// 39 - copy current code to memory
// 60 00 - push 0
// f3 - return
let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap();
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::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from(100));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100));
let info = EnvInfo::new();
let engine = TestEngine::new(1024, factory);
let mut substate = Substate::new();
{
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params, &mut substate).unwrap();
}
assert_eq!(substate.contracts_created.len(), 1);
assert_eq!(substate.contracts_created[0], next_address);
}
// test is incorrect, mk
evm_test_ignore!{test_aba_calls: test_aba_calls_jit, test_aba_calls_int}
fn test_aba_calls(factory: Factory) {
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 18 - push 18
// 73 945304eb96065b2a98b57a48a06ae28d285a71b5 - push this address
// 61 03e8 - push 1000
// f1 - message call
// 58 - get PC
// 55 - sstore
let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855".from_hex().unwrap();
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 00 - push 0
// 60 17 - push 17
// 73 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 - push this address
// 61 0x01f4 - push 500
// f1 - message call
// 60 01 - push 1
// 01 - add
// 58 - get PC
// 55 - sstore
let code_b = "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855".from_hex().unwrap();
let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap();
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let mut params = ActionParams::default();
params.address = address_a.clone();
params.sender = sender.clone();
params.gas = U256::from(100_000);
params.code = Some(code_a.clone());
params.value = ActionValue::Transfer(U256::from(100_000));
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.init_code(&address_a, code_a.clone());
state.init_code(&address_b, code_b.clone());
state.add_balance(&sender, &U256::from(100_000));
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.call(params, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
};
assert_eq!(gas_left, U256::from(73_237));
assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))), H256::from(&U256::from(1)));
}
// test is incorrect, mk
evm_test_ignore!{test_recursive_bomb1: test_recursive_bomb1_jit, test_recursive_bomb1_int}
fn test_recursive_bomb1(factory: Factory) {
// 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 - sstore
let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap();
let address = contract_address(&sender, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = U256::from(100_000);
params.code = Some(code.clone());
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.init_code(&address, code.clone());
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.call(params, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
};
assert_eq!(gas_left, U256::from(59_870));
assert_eq!(state.storage_at(&address, &H256::from(&U256::zero())), H256::from(&U256::from(1)));
assert_eq!(state.storage_at(&address, &H256::from(&U256::one())), H256::from(&U256::from(1)));
}
// test is incorrect, mk
evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int}
fn test_transact_simple(factory: Factory) {
let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
let keypair = KeyPair::create().unwrap();
t.sign(&keypair.secret());
let sender = t.sender().unwrap();
let contract = contract_address(&sender, &U256::zero());
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(18));
let mut info = EnvInfo::new();
info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0, factory);
let executed = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t).unwrap()
};
assert_eq!(executed.gas, U256::from(100_000));
assert_eq!(executed.gas_used, U256::from(41_301));
assert_eq!(executed.refunded, U256::from(58_699));
assert_eq!(executed.cumulative_gas_used, U256::from(41_301));
assert_eq!(executed.logs.len(), 0);
assert_eq!(executed.contracts_created.len(), 0);
assert_eq!(state.balance(&sender), U256::from(1));
assert_eq!(state.balance(&contract), U256::from(17));
assert_eq!(state.nonce(&sender), U256::from(1));
assert_eq!(state.storage_at(&contract, &H256::new()), H256::from(&U256::from(1)));
}
evm_test!{test_transact_invalid_sender: test_transact_invalid_sender_jit, test_transact_invalid_sender_int}
fn test_transact_invalid_sender(factory: Factory) {
let t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let mut info = EnvInfo::new();
info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0, factory);
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t)
};
match res {
Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature))) => (),
_ => assert!(false, "Expected invalid signature error.")
}
}
evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int}
fn test_transact_invalid_nonce(factory: Factory) {
let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::one());
let keypair = KeyPair::create().unwrap();
t.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(17));
let mut info = EnvInfo::new();
info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0, factory);
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t)
};
match res {
Err(Error::Execution(ExecutionError::InvalidNonce { expected, got }))
if expected == U256::zero() && got == U256::one() => (),
_ => assert!(false, "Expected invalid nonce error.")
}
}
evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int}
fn test_transact_gas_limit_reached(factory: Factory) {
let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(80_001), U256::zero(), U256::zero());
let keypair = KeyPair::create().unwrap();
t.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(17));
let mut info = EnvInfo::new();
info.gas_used = U256::from(20_000);
info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0, factory);
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t)
};
match res {
Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas }))
if gas_limit == U256::from(100_000) && gas_used == U256::from(20_000) && gas == U256::from(80_001) => (),
_ => assert!(false, "Expected block gas limit error.")
}
}
evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int}
fn test_not_enough_cash(factory: Factory) {
let mut t = Transaction::new_create(U256::from(18), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::one(), U256::zero());
let keypair = KeyPair::create().unwrap();
t.sign(&keypair.secret());
let sender = t.sender().unwrap();
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from(100_017));
let mut info = EnvInfo::new();
info.gas_limit = U256::from(100_000);
let engine = TestEngine::new(0, factory);
let res = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.transact(&t)
};
match res {
Err(Error::Execution(ExecutionError::NotEnoughCash { required , got }))
if required == U512::from(100_018) && got == U512::from(100_017) => (),
_ => assert!(false, "Expected not enough cash error. {:?}", res)
}
}
evm_test!{test_sha3: test_sha3_jit, test_sha3_int}
fn test_sha3(factory: Factory) {
let code = "6064640fffffffff20600055".from_hex().unwrap();
let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let address = contract_address(&sender, &U256::zero());
// TODO: add tests for 'callcreate'
//let next_address = contract_address(&address, &U256::zero());
let mut params = ActionParams::default();
params.address = address.clone();
params.sender = sender.clone();
params.origin = sender.clone();
params.gas = U256::from(0x0186a0);
params.code = Some(code.clone());
params.value = ActionValue::Transfer(U256::from_str("0de0b6b3a7640000").unwrap());
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.add_balance(&sender, &U256::from_str("152d02c7e14af6800000").unwrap());
let info = EnvInfo::new();
let engine = TestEngine::new(0, factory);
let mut substate = Substate::new();
let result = {
let mut ex = Executive::new(&mut state, &info, &engine);
ex.create(params, &mut substate)
};
match result {
Err(_) => {
},
_ => {
panic!("Expected OutOfGas");
}
}
}
}

View File

@@ -0,0 +1,243 @@
//! Transaction Execution environment.
use common::*;
use state::*;
use engine::*;
use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult};
use substate::*;
/// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a> {
/// Return reference to fixed sized output.
/// Used for message calls.
Return(BytesRef<'a>),
/// Init new contract as soon as `RETURN` is called.
InitContract
}
/// Transaction properties that externalities need to know about.
pub struct OriginInfo {
address: Address,
origin: Address,
gas_price: U256,
value: U256
}
impl OriginInfo {
/// Populates origin info from action params.
pub fn from(params: &ActionParams) -> Self {
OriginInfo {
address: params.address.clone(),
origin: params.origin.clone(),
gas_price: params.gas_price.clone(),
value: match params.value {
ActionValue::Transfer(val) => val,
ActionValue::Apparent(val) => val,
}
}
}
}
/// Implementation of evm Externalities.
pub struct Externalities<'a> {
state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
schedule: Schedule,
output: OutputPolicy<'a>
}
impl<'a> Externalities<'a> {
/// Basic `Externalities` constructor.
pub fn new(state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
output: OutputPolicy<'a>) -> Self {
Externalities {
state: state,
env_info: env_info,
engine: engine,
depth: depth,
origin_info: origin_info,
substate: substate,
schedule: engine.schedule(env_info),
output: output
}
}
}
impl<'a> Ext for Externalities<'a> {
fn storage_at(&self, key: &H256) -> H256 {
self.state.storage_at(&self.origin_info.address, key)
}
fn set_storage(&mut self, key: H256, value: H256) {
self.state.set_storage(&self.origin_info.address, key, value)
}
fn exists(&self, address: &Address) -> bool {
self.state.exists(address)
}
fn balance(&self, address: &Address) -> U256 {
self.state.balance(address)
}
fn blockhash(&self, number: &U256) -> H256 {
match *number < U256::from(self.env_info.number) && number.low_u64() >= cmp::max(256, self.env_info.number) - 256 {
true => {
let index = self.env_info.number - number.low_u64() - 1;
let r = self.env_info.last_hashes[index as usize].clone();
trace!("ext: blockhash({}) -> {} self.env_info.number={}\n", number, r, self.env_info.number);
r
},
false => {
trace!("ext: blockhash({}) -> null self.env_info.number={}\n", number, self.env_info.number);
H256::from(&U256::zero())
},
}
}
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult {
// create new contract address
let address = contract_address(&self.origin_info.address, &self.state.nonce(&self.origin_info.address));
// prepare the params
let params = ActionParams {
code_address: address.clone(),
address: address.clone(),
sender: self.origin_info.address.clone(),
origin: self.origin_info.origin.clone(),
gas: *gas,
gas_price: self.origin_info.gas_price.clone(),
value: ActionValue::Transfer(value.clone()),
code: Some(code.to_vec()),
data: None,
};
self.state.inc_nonce(&self.origin_info.address);
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
// TODO: handle internal error separately
match ex.create(params, self.substate) {
Ok(gas_left) => {
self.substate.contracts_created.push(address.clone());
ContractCreateResult::Created(address, gas_left)
},
_ => ContractCreateResult::Failed
}
}
fn call(&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> MessageCallResult {
let mut params = ActionParams {
sender: sender_address.clone(),
address: receive_address.clone(),
value: ActionValue::Apparent(self.origin_info.value.clone()),
code_address: code_address.clone(),
origin: self.origin_info.origin.clone(),
gas: *gas,
gas_price: self.origin_info.gas_price.clone(),
code: self.state.code(code_address),
data: Some(data.to_vec()),
};
if let Some(value) = value {
params.value = ActionValue::Transfer(value);
}
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
match ex.call(params, self.substate, BytesRef::Fixed(output)) {
Ok(gas_left) => MessageCallResult::Success(gas_left),
_ => MessageCallResult::Failed
}
}
fn extcode(&self, address: &Address) -> Bytes {
self.state.code(address).unwrap_or_else(|| vec![])
}
#[allow(match_ref_pats)]
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
match &mut self.output {
&mut OutputPolicy::Return(BytesRef::Fixed(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::Return(BytesRef::Flexible(ref mut vec)) => unsafe {
vec.clear();
vec.reserve(data.len());
ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len());
vec.set_len(data.len());
Ok(*gas)
},
&mut OutputPolicy::InitContract => {
let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas);
if return_cost > *gas {
return match self.schedule.exceptional_failed_code_deposit {
true => Err(evm::Error::OutOfGas),
false => Ok(*gas)
}
}
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.origin_info.address;
self.state.init_code(address, code);
Ok(*gas - return_cost)
}
}
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
let address = self.origin_info.address.clone();
self.substate.logs.push(LogEntry::new(address, topics, data.to_vec()));
}
fn suicide(&mut self, refund_address: &Address) {
let address = self.origin_info.address.clone();
let balance = self.balance(&address);
if &address == refund_address {
// TODO [todr] To be consisted with CPP client we set balance to 0 in that case.
self.state.sub_balance(&address, &balance);
} else {
trace!("Suiciding {} -> {} (xfer: {})", address, refund_address, balance);
self.state.transfer_balance(&address, refund_address, &balance);
}
self.substate.suicides.insert(address);
}
fn schedule(&self) -> &Schedule {
&self.schedule
}
fn env_info(&self) -> &EnvInfo {
&self.env_info
}
fn depth(&self) -> usize {
self.depth
}
fn inc_sstore_clears(&mut self) {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
}
}

276
ethcore/src/extras.rs Normal file
View File

@@ -0,0 +1,276 @@
use util::*;
use header::BlockNumber;
use rocksdb::{DB, Writable};
/// Represents index of extra data in database
#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)]
pub enum ExtrasIndex {
/// TODO [debris] Please document me
BlockDetails = 0,
/// TODO [debris] Please document me
BlockHash = 1,
/// TODO [debris] Please document me
TransactionAddress = 2,
/// TODO [debris] Please document me
BlockLogBlooms = 3,
/// TODO [debris] Please document me
BlocksBlooms = 4
}
/// trait used to write Extras data to db
pub trait ExtrasWritable {
/// TODO [debris] Please document me
fn put_extras<K, T>(&self, hash: &K, value: &T) where
T: ExtrasIndexable + Encodable,
K: ExtrasSliceConvertable;
}
/// trait used to read Extras data from db
pub trait ExtrasReadable {
/// TODO [debris] Please document me
fn get_extras<K, T>(&self, hash: &K) -> Option<T> where
T: ExtrasIndexable + Decodable,
K: ExtrasSliceConvertable;
/// TODO [debris] Please document me
fn extras_exists<K, T>(&self, hash: &K) -> bool where
T: ExtrasIndexable,
K: ExtrasSliceConvertable;
}
impl<W> ExtrasWritable for W where W: Writable {
fn put_extras<K, T>(&self, hash: &K, value: &T) where
T: ExtrasIndexable + Encodable,
K: ExtrasSliceConvertable {
self.put(&hash.to_extras_slice(T::extras_index()), &encode(value)).unwrap()
}
}
impl ExtrasReadable for DB {
fn get_extras<K, T>(&self, hash: &K) -> Option<T> where
T: ExtrasIndexable + Decodable,
K: ExtrasSliceConvertable {
self.get(&hash.to_extras_slice(T::extras_index())).unwrap()
.map(|v| decode(&v))
}
fn extras_exists<K, T>(&self, hash: &K) -> bool where
T: ExtrasIndexable,
K: ExtrasSliceConvertable {
self.get(&hash.to_extras_slice(T::extras_index())).unwrap().is_some()
}
}
/// Implementations should convert arbitrary type to database key slice
pub trait ExtrasSliceConvertable {
/// TODO [Gav Wood] Please document me
fn to_extras_slice(&self, i: ExtrasIndex) -> H264;
/// TODO [debris] Please document me
fn as_h256(&self) -> Option<&H256> { None }
}
impl ExtrasSliceConvertable for H256 {
fn to_extras_slice(&self, i: ExtrasIndex) -> H264 {
let mut slice = H264::from_slice(self);
slice[32] = i as u8;
slice
}
fn as_h256(&self) -> Option<&H256> { Some(self) }
}
impl ExtrasSliceConvertable for U256 {
fn to_extras_slice(&self, i: ExtrasIndex) -> H264 {
H256::from(self).to_extras_slice(i)
}
}
// NICE: make less horrible.
impl ExtrasSliceConvertable for BlockNumber {
fn to_extras_slice(&self, i: ExtrasIndex) -> H264 {
U256::from(*self).to_extras_slice(i)
}
}
/// Types implementing this trait can be indexed in extras database
pub trait ExtrasIndexable {
/// TODO [debris] Please document me
fn extras_index() -> ExtrasIndex;
}
impl ExtrasIndexable for H256 {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockHash
}
}
/// Familial details concerning a block
#[derive(Debug, Clone)]
pub struct BlockDetails {
/// TODO [debris] Please document me
pub number: BlockNumber,
/// TODO [debris] Please document me
pub total_difficulty: U256,
/// TODO [debris] Please document me
pub parent: H256,
/// TODO [debris] Please document me
pub children: Vec<H256>
}
impl ExtrasIndexable for BlockDetails {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockDetails
}
}
impl HeapSizeOf for BlockDetails {
fn heap_size_of_children(&self) -> usize {
self.children.heap_size_of_children()
}
}
impl Decodable for BlockDetails {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let details = BlockDetails {
number: try!(d.val_at(0)),
total_difficulty: try!(d.val_at(1)),
parent: try!(d.val_at(2)),
children: try!(d.val_at(3)),
};
Ok(details)
}
}
impl Encodable for BlockDetails {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.number);
s.append(&self.total_difficulty);
s.append(&self.parent);
s.append(&self.children);
}
}
/// Log blooms of certain block
#[derive(Clone)]
pub struct BlockLogBlooms {
/// TODO [debris] Please document me
pub blooms: Vec<H2048>
}
impl ExtrasIndexable for BlockLogBlooms {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockLogBlooms
}
}
impl HeapSizeOf for BlockLogBlooms {
fn heap_size_of_children(&self) -> usize {
self.blooms.heap_size_of_children()
}
}
impl Decodable for BlockLogBlooms {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let block_blooms = BlockLogBlooms {
blooms: try!(Decodable::decode(decoder))
};
Ok(block_blooms)
}
}
impl Encodable for BlockLogBlooms {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.blooms);
}
}
/// Neighboring log blooms on certain level
pub struct BlocksBlooms {
/// TODO [debris] Please document me
pub blooms: [H2048; 16]
}
impl ExtrasIndexable for BlocksBlooms {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlocksBlooms
}
}
impl HeapSizeOf for BlocksBlooms {
fn heap_size_of_children(&self) -> usize { 0 }
}
impl Clone for BlocksBlooms {
fn clone(&self) -> Self {
let mut blooms: [H2048; 16] = unsafe { ::std::mem::uninitialized() };
for i in 0..self.blooms.len() {
blooms[i] = self.blooms[i].clone();
}
BlocksBlooms {
blooms: blooms
}
}
}
impl Decodable for BlocksBlooms {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let blocks_blooms = BlocksBlooms {
blooms: try!(Decodable::decode(decoder))
};
Ok(blocks_blooms)
}
}
impl Encodable for BlocksBlooms {
fn rlp_append(&self, s: &mut RlpStream) {
let blooms_ref: &[H2048] = &self.blooms;
s.append(&blooms_ref);
}
}
/// Represents address of certain transaction within block
#[derive(Clone)]
pub struct TransactionAddress {
/// TODO [debris] Please document me
pub block_hash: H256,
/// TODO [debris] Please document me
pub index: u64
}
impl ExtrasIndexable for TransactionAddress {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::TransactionAddress
}
}
impl HeapSizeOf for TransactionAddress {
fn heap_size_of_children(&self) -> usize { 0 }
}
impl Decodable for TransactionAddress {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let tx_address = TransactionAddress {
block_hash: try!(d.val_at(0)),
index: try!(d.val_at(1)),
};
Ok(tx_address)
}
}
impl Encodable for TransactionAddress {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
s.append(&self.block_hash);
s.append(&self.index);
}
}

231
ethcore/src/header.rs Normal file
View File

@@ -0,0 +1,231 @@
use util::*;
use basic_types::*;
use time::now_utc;
/// Type for Block number
pub type BlockNumber = u64;
/// A block header.
///
/// Reflects the specific RLP fields of a block in the chain with additional room for the seal
/// which is non-specific.
///
/// Doesn't do all that much on its own.
#[derive(Default, Debug, Clone)]
pub struct Header {
// TODO: make all private.
/// TODO [Gav Wood] Please document me
pub parent_hash: H256,
/// TODO [arkpar] Please document me
pub timestamp: u64,
/// TODO [debris] Please document me
pub number: BlockNumber,
/// TODO [Gav Wood] Please document me
pub author: Address,
/// TODO [debris] Please document me
pub transactions_root: H256,
/// TODO [debris] Please document me
pub uncles_hash: H256,
/// TODO [Gav Wood] Please document me
pub extra_data: Bytes,
/// TODO [debris] Please document me
pub state_root: H256,
/// TODO [debris] Please document me
pub receipts_root: H256,
/// TODO [debris] Please document me
pub log_bloom: LogBloom,
/// TODO [debris] Please document me
pub gas_used: U256,
/// TODO [Gav Wood] Please document me
pub gas_limit: U256,
/// TODO [debris] Please document me
pub difficulty: U256,
/// TODO [arkpar] Please document me
pub seal: Vec<Bytes>,
/// TODO [arkpar] Please document me
pub hash: RefCell<Option<H256>>,
/// TODO [Gav Wood] Please document me
pub bare_hash: RefCell<Option<H256>>,
}
impl Header {
/// Create a new, default-valued, header.
pub fn new() -> Header {
Header {
parent_hash: ZERO_H256.clone(),
timestamp: 0,
number: 0,
author: ZERO_ADDRESS.clone(),
transactions_root: SHA3_NULL_RLP,
uncles_hash: SHA3_EMPTY_LIST_RLP,
extra_data: vec![],
state_root: SHA3_NULL_RLP,
receipts_root: SHA3_NULL_RLP,
log_bloom: ZERO_LOGBLOOM.clone(),
gas_used: ZERO_U256,
gas_limit: ZERO_U256,
difficulty: ZERO_U256,
seal: vec![],
hash: RefCell::new(None),
bare_hash: RefCell::new(None),
}
}
/// TODO [Gav Wood] Please document me
pub fn number(&self) -> BlockNumber { self.number }
/// TODO [Gav Wood] Please document me
pub fn timestamp(&self) -> u64 { self.timestamp }
/// TODO [Gav Wood] Please document me
pub fn author(&self) -> &Address { &self.author }
/// TODO [Gav Wood] Please document me
pub fn extra_data(&self) -> &Bytes { &self.extra_data }
/// TODO [Gav Wood] Please document me
pub fn state_root(&self) -> &H256 { &self.state_root }
/// TODO [Gav Wood] Please document me
pub fn receipts_root(&self) -> &H256 { &self.receipts_root }
/// TODO [Gav Wood] Please document me
pub fn gas_limit(&self) -> &U256 { &self.gas_limit }
/// TODO [Gav Wood] Please document me
pub fn difficulty(&self) -> &U256 { &self.difficulty }
/// TODO [Gav Wood] Please document me
pub fn seal(&self) -> &Vec<Bytes> { &self.seal }
// TODO: seal_at, set_seal_at &c.
/// TODO [Gav Wood] Please document me
pub fn set_number(&mut self, a: BlockNumber) { self.number = a; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_timestamp(&mut self, a: u64) { self.timestamp = a; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_timestamp_now(&mut self) { self.timestamp = now_utc().to_timespec().sec as u64; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } }
/// TODO [Gav Wood] Please document me
pub fn set_extra_data(&mut self, a: Bytes) { if a != self.extra_data { self.extra_data = a; self.note_dirty(); } }
/// TODO [Gav Wood] Please document me
pub fn set_gas_used(&mut self, a: U256) { self.gas_used = a; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_gas_limit(&mut self, a: U256) { self.gas_limit = a; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_difficulty(&mut self, a: U256) { self.difficulty = a; self.note_dirty(); }
/// TODO [Gav Wood] Please document me
pub fn set_seal(&mut self, a: Vec<Bytes>) { self.seal = a; self.note_dirty(); }
/// Get the hash of this header (sha3 of the RLP).
pub fn hash(&self) -> H256 {
let mut hash = self.hash.borrow_mut();
match &mut *hash {
&mut Some(ref h) => h.clone(),
hash @ &mut None => {
*hash = Some(self.rlp_sha3(Seal::With));
hash.as_ref().unwrap().clone()
}
}
}
/// Get the hash of the header excluding the seal
pub fn bare_hash(&self) -> H256 {
let mut hash = self.bare_hash.borrow_mut();
match &mut *hash {
&mut Some(ref h) => h.clone(),
hash @ &mut None => {
*hash = Some(self.rlp_sha3(Seal::Without));
hash.as_ref().unwrap().clone()
}
}
}
/// Note that some fields have changed. Resets the memoised hash.
pub fn note_dirty(&self) {
*self.hash.borrow_mut() = None;
*self.bare_hash.borrow_mut() = None;
}
// TODO: make these functions traity
/// TODO [Gav Wood] Please document me
pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) {
s.begin_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 });
s.append(&self.parent_hash);
s.append(&self.uncles_hash);
s.append(&self.author);
s.append(&self.state_root);
s.append(&self.transactions_root);
s.append(&self.receipts_root);
s.append(&self.log_bloom);
s.append(&self.difficulty);
s.append(&self.number);
s.append(&self.gas_limit);
s.append(&self.gas_used);
s.append(&self.timestamp);
s.append(&self.extra_data);
if let Seal::With = with_seal {
for b in &self.seal {
s.append_raw(&b, 1);
}
}
}
/// TODO [Gav Wood] Please document me
pub fn rlp(&self, with_seal: Seal) -> Bytes {
let mut s = RlpStream::new();
self.stream_rlp(&mut s, with_seal);
s.out()
}
/// TODO [debris] Please document me
pub fn rlp_sha3(&self, with_seal: Seal) -> H256 { self.rlp(with_seal).sha3() }
}
impl Decodable for Header {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let r = decoder.as_rlp();
let mut blockheader = Header {
parent_hash: try!(r.val_at(0)),
uncles_hash: try!(r.val_at(1)),
author: try!(r.val_at(2)),
state_root: try!(r.val_at(3)),
transactions_root: try!(r.val_at(4)),
receipts_root: try!(r.val_at(5)),
log_bloom: try!(r.val_at(6)),
difficulty: try!(r.val_at(7)),
number: try!(r.val_at(8)),
gas_limit: try!(r.val_at(9)),
gas_used: try!(r.val_at(10)),
timestamp: try!(r.val_at(11)),
extra_data: try!(r.val_at(12)),
seal: vec![],
hash: RefCell::new(Some(r.as_raw().sha3())),
bare_hash: RefCell::new(None),
};
for i in 13..r.item_count() {
blockheader.seal.push(try!(r.at(i)).as_raw().to_vec())
}
Ok(blockheader)
}
}
impl Encodable for Header {
fn rlp_append(&self, s: &mut RlpStream) {
self.stream_rlp(s, Seal::With);
}
}
#[cfg(test)]
mod tests {
}

158
ethcore/src/lib.rs Normal file
View File

@@ -0,0 +1,158 @@
#![warn(missing_docs)]
#![feature(cell_extras)]
#![feature(augmented_assignments)]
#![feature(plugin)]
//#![plugin(interpolate_idents)]
#![plugin(clippy)]
#![allow(needless_range_loop, match_bool)]
//! Ethcore's ethereum implementation
//!
//! ### Rust version
//! - beta
//! - nightly
//!
//! ### Supported platforms:
//! - OSX
//! - Linux/Ubuntu
//!
//! ### Dependencies:
//! - RocksDB 3.13
//! - LLVM 3.7 (optional, required for `jit`)
//! - evmjit (optional, required for `jit`)
//!
//! ### Dependencies Installation
//!
//! - OSX
//!
//! - rocksdb
//! ```bash
//! brew install rocksdb
//! ```
//!
//! - llvm
//!
//! - download llvm 3.7 from http://llvm.org/apt/
//!
//! ```bash
//! cd llvm-3.7.0.src
//! mkdir build && cd $_
//! cmake -G "Unix Makefiles" .. -DCMAKE_C_FLAGS_RELEASE= -DCMAKE_CXX_FLAGS_RELEASE= -DCMAKE_INSTALL_PREFIX=/usr/local/Cellar/llvm/3.7 -DCMAKE_BUILD_TYPE=Release
//! make && make install
//! ```
//! - evmjit
//!
//! - download from https://github.com/debris/evmjit
//!
//! ```bash
//! cd evmjit
//! mkdir build && cd $_
//! cmake -DLLVM_DIR=/usr/local/lib/llvm-3.7/share/llvm/cmake ..
//! make && make install
//! ```
//!
//! - Linux/Ubuntu
//!
//! - rocksdb
//!
//! ```bash
//! wget https://github.com/facebook/rocksdb/archive/rocksdb-3.13.tar.gz
//! tar xvf rocksdb-3.13.tar.gz && cd rocksdb-rocksdb-3.13 && make shared_lib
//! sudo make install
//! ```
//!
//! - llvm
//!
//! - install using packages from http://llvm.org/apt/
//!
//! - evmjit
//!
//! - download from https://github.com/debris/evmjit
//!
//! ```bash
//! cd evmjit
//! mkdir build && cd $_
//! cmake .. && make
//! sudo make install
//! sudo ldconfig
//! ```
#[macro_use]
extern crate log;
extern crate rustc_serialize;
extern crate flate2;
extern crate rocksdb;
extern crate heapsize;
extern crate crypto;
extern crate time;
extern crate env_logger;
extern crate num_cpus;
#[cfg(feature = "jit" )]
extern crate evmjit;
#[macro_use]
extern crate ethcore_util as util;
extern crate crossbeam;
#[macro_use]
extern crate lazy_static;
// NOTE: Add doc parser exception for these pub declarations.
/// TODO [Gav Wood] Please document me
pub mod common;
/// TODO [Tomusdrw] Please document me
pub mod basic_types;
#[macro_use]
pub mod evm;
pub mod error;
/// TODO [Gav Wood] Please document me
pub mod log_entry;
/// TODO [Gav Wood] Please document me
pub mod env_info;
/// TODO [Gav Wood] Please document me
pub mod pod_account;
/// TODO [Gav Wood] Please document me
pub mod pod_state;
/// TODO [Gav Wood] Please document me
pub mod account_diff;
/// TODO [Gav Wood] Please document me
pub mod state_diff;
/// TODO [Gav Wood] Please document me
pub mod engine;
/// TODO [Gav Wood] Please document me
pub mod state;
/// TODO [Gav Wood] Please document me
pub mod account;
pub mod action_params;
/// TODO [debris] Please document me
pub mod header;
/// TODO [Gav Wood] Please document me
pub mod transaction;
/// TODO [Gav Wood] Please document me
pub mod receipt;
/// TODO [Gav Wood] Please document me
pub mod null_engine;
/// TODO [Gav Wood] Please document me
pub mod builtin;
/// TODO [debris] Please document me
pub mod spec;
pub mod views;
pub mod blockchain;
/// TODO [Gav Wood] Please document me
pub mod extras;
/// TODO [arkpar] Please document me
pub mod substate;
/// TODO [Gav Wood] Please document me
pub mod service;
pub mod executive;
pub mod externalities;
#[cfg(test)]
mod tests;
/// TODO [arkpar] Please document me
pub mod client;
/// TODO [arkpar] Please document me
pub mod block;
/// TODO [arkpar] Please document me
pub mod verification;
pub mod block_queue;
pub mod ethereum;

79
ethcore/src/log_entry.rs Normal file
View File

@@ -0,0 +1,79 @@
use util::*;
use basic_types::LogBloom;
/// A single log's entry.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct LogEntry {
/// TODO [Gav Wood] Please document me
pub address: Address,
/// TODO [Gav Wood] Please document me
pub topics: Vec<H256>,
/// TODO [Gav Wood] Please document me
pub data: Bytes,
}
impl Encodable for LogEntry {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.address);
s.append(&self.topics);
s.append(&self.data);
}
}
impl LogEntry {
/// Create a new log entry.
pub fn new(address: Address, topics: Vec<H256>, data: Bytes) -> LogEntry {
LogEntry {
address: address,
topics: topics,
data: data
}
}
/// Returns reference to address.
pub fn address(&self) -> &Address {
&self.address
}
/// Returns reference to topics.
pub fn topics(&self) -> &Vec<H256> {
&self.topics
}
/// Returns reference to data.
pub fn data(&self) -> &Bytes {
&self.data
}
/// Calculates the bloom of this log entry.
pub fn bloom(&self) -> LogBloom {
self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3()))
}
}
impl FromJson for LogEntry {
/// Convert given JSON object to a LogEntry.
fn from_json(json: &Json) -> LogEntry {
// TODO: check bloom.
LogEntry {
address: xjson!(&json["address"]),
topics: xjson!(&json["topics"]),
data: xjson!(&json["data"]),
}
}
}
#[cfg(test)]
mod tests {
use util::*;
use super::LogEntry;
#[test]
fn test_empty_log_bloom() {
let bloom = H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let log = LogEntry::new(address, vec![], vec![]);
assert_eq!(log.bloom(), bloom);
}
}

View File

@@ -0,0 +1,31 @@
use engine::Engine;
use spec::Spec;
use evm::Schedule;
use evm::Factory;
use env_info::EnvInfo;
/// An engine which does not provide any consensus mechanism.
pub struct NullEngine {
spec: Spec,
factory: Factory
}
impl NullEngine {
/// Returns new instance of NullEngine with default VM Factory
pub fn new_boxed(spec: Spec) -> Box<Engine> {
Box::new(NullEngine{
spec: spec,
// TODO [todr] should this return any specific factory?
factory: Factory::default()
})
}
}
impl Engine for NullEngine {
fn vm_factory(&self) -> &Factory {
&self.factory
}
fn name(&self) -> &str { "NullEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule { Schedule::new_frontier() }
}

132
ethcore/src/pod_account.rs Normal file
View File

@@ -0,0 +1,132 @@
use util::*;
use account::*;
#[derive(Debug,Clone,PartialEq,Eq)]
/// Genesis account data. Does not have a DB overlay cache.
pub struct PodAccount {
/// TODO [Gav Wood] Please document me
pub balance: U256,
/// TODO [Gav Wood] Please document me
pub nonce: U256,
/// TODO [Gav Wood] Please document me
pub code: Bytes,
/// TODO [Gav Wood] Please document me
pub storage: BTreeMap<H256, H256>,
}
impl PodAccount {
/// Construct new object.
pub fn new(balance: U256, nonce: U256, code: Bytes, storage: BTreeMap<H256, H256>) -> PodAccount {
PodAccount { balance: balance, nonce: nonce, code: code, storage: storage }
}
/// Convert Account to a PodAccount.
/// NOTE: This will silently fail unless the account is fully cached.
pub fn from_account(acc: &Account) -> PodAccount {
PodAccount {
balance: acc.balance().clone(),
nonce: acc.nonce().clone(),
storage: acc.storage_overlay().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
code: acc.code().unwrap().to_vec(),
}
}
/// Returns the RLP for this account.
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&self.nonce);
stream.append(&self.balance);
stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k.to_vec(), encode(&U256::from(v.as_slice())).to_vec())).collect()));
stream.append(&self.code.sha3());
stream.out()
}
/// Place additional data into given hash DB.
pub fn insert_additional(&self, db: &mut HashDB) {
if !self.code.is_empty() {
db.insert(&self.code);
}
let mut r = H256::new();
let mut t = SecTrieDBMut::new(db, &mut r);
for (k, v) in &self.storage {
t.insert(k, &encode(&U256::from(v.as_slice())));
}
}
}
impl fmt::Display for PodAccount {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len())
}
}
#[cfg(test)]
mod test {
use common::*;
use account_diff::*;
use super::*;
#[test]
fn existence() {
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None);
assert_eq!(AccountDiff::diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)),
code: Diff::Born(vec![]),
storage: map![],
}));
}
#[test]
fn basic() {
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
let b = PodAccount{balance: x!(42), nonce: x!(1), code: vec![], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Changed(x!(69), x!(42)),
nonce: Diff::Changed(x!(0), x!(1)),
code: Diff::Same,
storage: map![],
}));
}
#[test]
fn code() {
let a = PodAccount{balance: x!(0), nonce: x!(0), code: vec![], storage: map![]};
let b = PodAccount{balance: x!(0), nonce: x!(1), code: vec![0], storage: map![]};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Changed(x!(0), x!(1)),
code: Diff::Changed(vec![], vec![0]),
storage: map![],
}));
}
#[test]
fn storage() {
let a = PodAccount {
balance: x!(0),
nonce: x!(0),
code: vec![],
storage: mapx![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0]
};
let b = PodAccount {
balance: x!(0),
nonce: x!(0),
code: vec![],
storage: mapx![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9]
};
assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same,
nonce: Diff::Same,
code: Diff::Same,
storage: map![
x!(2) => Diff::new(x!(2), x!(3)),
x!(3) => Diff::new(x!(3), x!(0)),
x!(4) => Diff::new(x!(4), x!(0)),
x!(7) => Diff::new(x!(0), x!(7)),
x!(9) => Diff::new(x!(0), x!(9))
],
}));
}
}

83
ethcore/src/pod_state.rs Normal file
View File

@@ -0,0 +1,83 @@
use util::*;
use pod_account::*;
#[derive(Debug,Clone,PartialEq,Eq,Default)]
/// TODO [Gav Wood] Please document me
pub struct PodState (BTreeMap<Address, PodAccount>);
impl PodState {
/// Contruct a new object from the `m`.
pub fn new() -> PodState { Default::default() }
/// Contruct a new object from the `m`.
pub fn from(m: BTreeMap<Address, PodAccount>) -> PodState { PodState(m) }
/// Get the underlying map.
pub fn get(&self) -> &BTreeMap<Address, PodAccount> { &self.0 }
/// Get the root hash of the trie of the RLP of this.
pub fn root(&self) -> H256 {
sec_trie_root(self.0.iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect())
}
/// Drain object to get the underlying map.
pub fn drain(self) -> BTreeMap<Address, PodAccount> { self.0 }
}
impl FromJson for PodState {
/// Translate the JSON object into a hash map of account information ready for insertion into State.
fn from_json(json: &Json) -> PodState {
PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| {
let balance = acc.find("balance").map(&U256::from_json);
let nonce = acc.find("nonce").map(&U256::from_json);
let storage = acc.find("storage").map(&BTreeMap::from_json);
let code = acc.find("code").map(&Bytes::from_json);
if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() {
state.insert(address_from_hex(address), PodAccount{
balance: balance.unwrap_or_else(U256::zero),
nonce: nonce.unwrap_or_else(U256::zero),
storage: storage.unwrap_or_else(BTreeMap::new),
code: code.unwrap_or_else(Vec::new)
});
}
state
}))
}
}
impl fmt::Display for PodState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 {
try!(writeln!(f, "{} => {}", add, acc));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate rustc_serialize;
use super::*;
use rustc_serialize::*;
use util::from_json::FromJson;
use util::hash::*;
#[test]
fn it_serializes_form_json() {
let pod_state = PodState::from_json(&json::Json::from_str(
r#"
{
"0000000000000000000000000000000000000000": {
"balance": "1000",
"nonce": "100",
"storage": {},
"code" : []
}
}
"#
).unwrap());
assert!(pod_state.get().get(&ZERO_ADDRESS).is_some());
}
}

54
ethcore/src/receipt.rs Normal file
View File

@@ -0,0 +1,54 @@
use util::*;
use basic_types::LogBloom;
use log_entry::LogEntry;
/// Information describing execution of a transaction.
#[derive(Default, Debug, Clone)]
pub struct Receipt {
/// TODO [Gav Wood] Please document me
pub state_root: H256,
/// TODO [Gav Wood] Please document me
pub gas_used: U256,
/// TODO [Gav Wood] Please document me
pub log_bloom: LogBloom,
/// TODO [Gav Wood] Please document me
pub logs: Vec<LogEntry>,
}
impl Receipt {
/// TODO [Gav Wood] Please document me
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 Encodable for Receipt {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.state_root);
s.append(&self.gas_used);
s.append(&self.log_bloom);
s.append(&self.logs);
}
}
#[cfg(test)]
mod tests {
use super::*;
use util::*;
#[test]
fn it_can_be_encoded() {
let mut rlp_stream = RlpStream::new();
let receipt = Receipt::new(H256::zero(), U256::zero(), vec![]);
rlp_stream.append(&receipt);
let out = rlp_stream.out();
assert_eq!(out.len(), 297);
}
}

112
ethcore/src/service.rs Normal file
View File

@@ -0,0 +1,112 @@
use util::*;
use spec::Spec;
use error::*;
use std::env;
use client::Client;
/// Message type for external and internal events
#[derive(Clone)]
pub enum SyncMessage {
/// New block has been imported into the blockchain
NewChainBlock(Bytes), //TODO: use Cow
/// A block is ready
BlockVerified,
}
/// TODO [arkpar] Please document me
pub type NetSyncMessage = NetworkIoMessage<SyncMessage>;
/// Client service setup. Creates and registers client and network services with the IO subsystem.
pub struct ClientService {
net_service: NetworkService<SyncMessage>,
client: Arc<Client>,
}
impl ClientService {
/// Start the service in a separate thread.
pub fn start(spec: Spec, net_config: NetworkConfiguration) -> Result<ClientService, Error> {
let mut net_service = try!(NetworkService::start(net_config));
info!("Starting {}", net_service.host_info());
info!("Configured for {} using {} engine", spec.name, spec.engine_name);
let mut dir = env::home_dir().unwrap();
dir.push(".parity");
dir.push(H64::from(spec.genesis_header().hash()).hex());
let client = try!(Client::new(spec, &dir, net_service.io().channel()));
let client_io = Arc::new(ClientIoHandler {
client: client.clone()
});
try!(net_service.io().register_handler(client_io));
Ok(ClientService {
net_service: net_service,
client: client,
})
}
/// Add a node to network
pub fn add_node(&mut self, _enode: &str) {
unimplemented!();
}
/// Get general IO interface
pub fn io(&mut self) -> &mut IoService<NetSyncMessage> {
self.net_service.io()
}
/// Get client interface
pub fn client(&self) -> Arc<Client> {
self.client.clone()
}
/// Get network service component
pub fn network(&mut self) -> &mut NetworkService<SyncMessage> {
&mut self.net_service
}
}
/// IO interface for the Client handler
struct ClientIoHandler {
client: Arc<Client>
}
const CLIENT_TICK_TIMER: TimerToken = 0;
const CLIENT_TICK_MS: u64 = 5000;
impl IoHandler<NetSyncMessage> for ClientIoHandler {
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
io.register_timer(CLIENT_TICK_TIMER, CLIENT_TICK_MS).expect("Error registering client timer");
}
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
if timer == CLIENT_TICK_TIMER {
self.client.tick();
}
}
#[allow(match_ref_pats)]
#[allow(single_match)]
fn message(&self, io: &IoContext<NetSyncMessage>, net_message: &NetSyncMessage) {
if let &UserMessage(ref message) = net_message {
match message {
&SyncMessage::BlockVerified => {
self.client.import_verified_blocks(&io.channel());
},
_ => {}, // ignore other messages
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tests::helpers::*;
use util::network::*;
#[test]
fn it_can_be_started() {
let spec = get_test_spec();
let service = ClientService::start(spec, NetworkConfiguration::new());
assert!(service.is_ok());
}
}

319
ethcore/src/spec.rs Normal file
View File

@@ -0,0 +1,319 @@
use common::*;
use flate2::read::GzDecoder;
use engine::*;
use pod_state::*;
use null_engine::*;
/// Converts file from base64 gzipped bytes to json
pub fn gzip64res_to_json(source: &[u8]) -> Json {
// there is probably no need to store genesis in based64 gzip,
// but that's what go does, and it was easy to load it this way
let data = source.from_base64().expect("Genesis block is malformed!");
let data_ref: &[u8] = &data;
let mut decoder = GzDecoder::new(data_ref).expect("Gzip is invalid");
let mut s: String = "".to_owned();
decoder.read_to_string(&mut s).expect("Gzip is invalid");
Json::from_str(&s).expect("Json is invalid")
}
/// Convert JSON value to equivalent RLP representation.
// TODO: handle container types.
fn json_to_rlp(json: &Json) -> Bytes {
match *json {
Json::Boolean(o) => encode(&(if o {1u64} else {0})).to_vec(),
Json::I64(o) => encode(&(o as u64)).to_vec(),
Json::U64(o) => encode(&o).to_vec(),
Json::String(ref s) if s.len() >= 2 && &s[0..2] == "0x" && U256::from_str(&s[2..]).is_ok() => {
encode(&U256::from_str(&s[2..]).unwrap()).to_vec()
},
Json::String(ref s) => {
encode(s).to_vec()
},
_ => panic!()
}
}
/// Convert JSON to a string->RLP map.
fn json_to_rlp_map(json: &Json) -> HashMap<String, Bytes> {
json.as_object().unwrap().iter().map(|(k, v)| (k, json_to_rlp(v))).fold(HashMap::new(), |mut acc, kv| {
acc.insert(kv.0.clone(), kv.1);
acc
})
}
/// Parameters for a block chain; includes both those intrinsic to the design of the
/// chain and those to be interpreted by the active chain engine.
#[derive(Debug)]
pub struct Spec {
// User friendly spec name
/// TODO [Gav Wood] Please document me
pub name: String,
// What engine are we using for this?
/// TODO [Gav Wood] Please document me
pub engine_name: String,
/// Known nodes on the network in enode format.
pub nodes: Vec<String>,
// Parameters concerning operation of the specific engine we're using.
// Name -> RLP-encoded value
/// TODO [Gav Wood] Please document me
pub engine_params: HashMap<String, Bytes>,
// Builtin-contracts are here for now but would like to abstract into Engine API eventually.
/// TODO [Gav Wood] Please document me
pub builtins: BTreeMap<Address, Builtin>,
// Genesis params.
/// TODO [Gav Wood] Please document me
pub parent_hash: H256,
/// TODO [Gav Wood] Please document me
pub author: Address,
/// TODO [Gav Wood] Please document me
pub difficulty: U256,
/// TODO [Gav Wood] Please document me
pub gas_limit: U256,
/// TODO [Gav Wood] Please document me
pub gas_used: U256,
/// TODO [Gav Wood] Please document me
pub timestamp: u64,
/// Transactions root of the genesis block. Should be SHA3_NULL_RLP.
pub transactions_root: H256,
/// Receipts root of the genesis block. Should be SHA3_NULL_RLP.
pub receipts_root: H256,
/// TODO [arkpar] Please document me
pub extra_data: Bytes,
/// TODO [Gav Wood] Please document me
genesis_state: PodState,
/// TODO [Gav Wood] Please document me
pub seal_fields: usize,
/// TODO [Gav Wood] Please document me
pub seal_rlp: Bytes,
// May be prepopulated if we know this in advance.
state_root_memo: RwLock<Option<H256>>,
}
#[allow(wrong_self_convention)] // because to_engine(self) should be to_engine(&self)
impl Spec {
/// Convert this object into a boxed Engine of the right underlying type.
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
pub fn to_engine(self) -> Result<Box<Engine>, Error> {
match self.engine_name.as_ref() {
"NullEngine" => Ok(NullEngine::new_boxed(self)),
"Ethash" => Ok(super::ethereum::Ethash::new_boxed(self)),
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
}
}
/// Return the state root for the genesis state, memoising accordingly.
pub fn state_root(&self) -> H256 {
if self.state_root_memo.read().unwrap().is_none() {
*self.state_root_memo.write().unwrap() = Some(self.genesis_state.root());
}
self.state_root_memo.read().unwrap().as_ref().unwrap().clone()
}
/// Get the known knodes of the network in enode format.
pub fn nodes(&self) -> &Vec<String> { &self.nodes }
/// TODO [Gav Wood] Please document me
pub fn genesis_header(&self) -> Header {
Header {
parent_hash: self.parent_hash.clone(),
timestamp: self.timestamp,
number: 0,
author: self.author.clone(),
transactions_root: self.transactions_root.clone(),
uncles_hash: RlpStream::new_list(0).out().sha3(),
extra_data: self.extra_data.clone(),
state_root: self.state_root().clone(),
receipts_root: self.receipts_root.clone(),
log_bloom: H2048::new().clone(),
gas_used: self.gas_used.clone(),
gas_limit: self.gas_limit.clone(),
difficulty: self.difficulty.clone(),
seal: {
let seal = {
let mut s = RlpStream::new_list(self.seal_fields);
s.append_raw(&self.seal_rlp, self.seal_fields);
s.out()
};
let r = Rlp::new(&seal);
(0..self.seal_fields).map(|i| r.at(i).as_raw().to_vec()).collect()
},
hash: RefCell::new(None),
bare_hash: RefCell::new(None),
}
}
/// Compose the genesis block for this chain.
pub fn genesis_block(&self) -> Bytes {
let empty_list = RlpStream::new_list(0).out();
let header = self.genesis_header();
let mut ret = RlpStream::new_list(3);
ret.append(&header);
ret.append_raw(&empty_list, 1);
ret.append_raw(&empty_list, 1);
ret.out()
}
/// Overwrite the genesis components with the given JSON, assuming standard Ethereum test format.
pub fn overwrite_genesis(&mut self, genesis: &Json) {
let (seal_fields, seal_rlp) = {
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
let mut s = RlpStream::new();
s.append(&H256::from_json(&genesis["mixHash"]));
s.append(&H64::from_json(&genesis["nonce"]));
(2, s.out())
} else {
// backup algo that will work with sealFields/sealRlp (and without).
(
u64::from_json(&genesis["sealFields"]) as usize,
Bytes::from_json(&genesis["sealRlp"])
)
}
};
self.parent_hash = H256::from_json(&genesis["parentHash"]);
self.transactions_root = genesis.find("transactionsTrie").and_then(|_| Some(H256::from_json(&genesis["transactionsTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
self.receipts_root = genesis.find("receiptTrie").and_then(|_| Some(H256::from_json(&genesis["receiptTrie"]))).unwrap_or(SHA3_NULL_RLP.clone());
self.author = Address::from_json(&genesis["coinbase"]);
self.difficulty = U256::from_json(&genesis["difficulty"]);
self.gas_limit = U256::from_json(&genesis["gasLimit"]);
self.gas_used = U256::from_json(&genesis["gasUsed"]);
self.timestamp = u64::from_json(&genesis["timestamp"]);
self.extra_data = Bytes::from_json(&genesis["extraData"]);
self.seal_fields = seal_fields;
self.seal_rlp = seal_rlp;
self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"]))));
}
/// Alter the value of the genesis state.
pub fn set_genesis_state(&mut self, s: PodState) {
self.genesis_state = s;
*self.state_root_memo.write().unwrap() = None;
}
/// Returns `false` if the memoized state root is invalid. `true` otherwise.
pub fn is_state_root_valid(&self) -> bool {
self.state_root_memo.read().unwrap().clone().map_or(true, |sr| sr == self.genesis_state.root())
}
}
impl FromJson for Spec {
/// Loads a chain-specification from a json data structure
fn from_json(json: &Json) -> Spec {
// once we commit ourselves to some json parsing library (serde?)
// move it to proper data structure
let mut builtins = BTreeMap::new();
let mut state = PodState::new();
if let Some(&Json::Object(ref accounts)) = json.find("accounts") {
for (address, acc) in accounts.iter() {
let addr = Address::from_str(address).unwrap();
if let Some(ref builtin_json) = acc.find("builtin") {
if let Some(builtin) = Builtin::from_json(builtin_json) {
builtins.insert(addr.clone(), builtin);
}
}
}
state = xjson!(&json["accounts"]);
}
let nodes = if let Some(&Json::Array(ref ns)) = json.find("nodes") {
ns.iter().filter_map(|n| if let Json::String(ref s) = *n { Some(s.clone()) } else {None}).collect()
} else { Vec::new() };
let genesis = &json["genesis"];//.as_object().expect("No genesis object in JSON");
let (seal_fields, seal_rlp) = {
if genesis.find("mixHash").is_some() && genesis.find("nonce").is_some() {
let mut s = RlpStream::new();
s.append(&H256::from_str(&genesis["mixHash"].as_string().expect("mixHash not a string.")[2..]).expect("Invalid mixHash string value"));
s.append(&H64::from_str(&genesis["nonce"].as_string().expect("nonce not a string.")[2..]).expect("Invalid nonce string value"));
(2, s.out())
} else {
// backup algo that will work with sealFields/sealRlp (and without).
(
usize::from_str(&genesis["sealFields"].as_string().unwrap_or("0x")[2..]).expect("Invalid sealFields integer data"),
genesis["sealRlp"].as_string().unwrap_or("0x")[2..].from_hex().expect("Invalid sealRlp hex data")
)
}
};
Spec {
name: json.find("name").map_or("unknown", |j| j.as_string().unwrap()).to_owned(),
engine_name: json["engineName"].as_string().unwrap().to_owned(),
engine_params: json_to_rlp_map(&json["params"]),
nodes: nodes,
builtins: builtins,
parent_hash: H256::from_str(&genesis["parentHash"].as_string().unwrap()[2..]).unwrap(),
author: Address::from_str(&genesis["author"].as_string().unwrap()[2..]).unwrap(),
difficulty: U256::from_str(&genesis["difficulty"].as_string().unwrap()[2..]).unwrap(),
gas_limit: U256::from_str(&genesis["gasLimit"].as_string().unwrap()[2..]).unwrap(),
gas_used: U256::from(0u8),
timestamp: u64::from_str(&genesis["timestamp"].as_string().unwrap()[2..]).unwrap(),
transactions_root: SHA3_NULL_RLP.clone(),
receipts_root: SHA3_NULL_RLP.clone(),
extra_data: genesis["extraData"].as_string().unwrap()[2..].from_hex().unwrap(),
genesis_state: state,
seal_fields: seal_fields,
seal_rlp: seal_rlp,
state_root_memo: RwLock::new(genesis.find("stateRoot").and_then(|_| genesis["stateRoot"].as_string()).map(|s| H256::from_str(&s[2..]).unwrap())),
}
}
}
impl Spec {
/// Ensure that the given state DB has the trie nodes in for the genesis state.
pub fn ensure_db_good(&self, db: &mut HashDB) -> bool {
if !db.contains(&self.state_root()) {
let mut root = H256::new();
{
let mut t = SecTrieDBMut::new(db, &mut root);
for (address, account) in self.genesis_state.get().iter() {
t.insert(address.as_slice(), &account.rlp());
}
}
for (_, account) in self.genesis_state.get().iter() {
account.insert_additional(db);
}
assert!(db.contains(&self.state_root()));
true
} else { false }
}
/// Create a new Spec from a JSON UTF-8 data resource `data`.
pub fn from_json_utf8(data: &[u8]) -> Spec {
Self::from_json_str(::std::str::from_utf8(data).unwrap())
}
/// Create a new Spec from a JSON string.
pub fn from_json_str(s: &str) -> Spec {
Self::from_json(&Json::from_str(s).expect("Json is invalid"))
}
/// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus.
pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../res/null_morden.json")) }
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use util::hash::*;
use util::sha3::*;
use views::*;
use super::*;
#[test]
fn test_chain() {
let test_spec = Spec::new_test();
assert_eq!(test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
let genesis = test_spec.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
let _ = test_spec.to_engine();
}
}

448
ethcore/src/state.rs Normal file
View File

@@ -0,0 +1,448 @@
use common::*;
use engine::Engine;
use executive::Executive;
use pod_account::*;
use pod_state::*;
//use state_diff::*; // TODO: uncomment once to_pod() works correctly.
/// TODO [Gav Wood] Please document me
pub type ApplyResult = Result<Receipt, Error>;
/// Representation of the entire state of all accounts in the system.
#[derive(Clone)]
pub struct State {
db: JournalDB,
root: H256,
cache: RefCell<HashMap<Address, Option<Account>>>,
account_start_nonce: U256,
}
impl State {
/// Creates new state with empty state root
pub fn new(mut db: JournalDB, account_start_nonce: U256) -> State {
let mut root = H256::new();
{
// init trie and reset root too null
let _ = SecTrieDBMut::new(&mut db, &mut root);
}
State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
account_start_nonce: account_start_nonce,
}
}
/// Creates new state with existing state root
pub fn from_existing(db: JournalDB, root: H256, account_start_nonce: U256) -> State {
{
// trie should panic! if root does not exist
let _ = SecTrieDB::new(&db, &root);
}
State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
account_start_nonce: account_start_nonce,
}
}
/// Destroy the current object and return root and database.
pub fn drop(self) -> (H256, JournalDB) {
(self.root, self.db)
}
/// Return reference to root
pub fn root(&self) -> &H256 {
&self.root
}
/// Expose the underlying database; good to use for calling `state.db().commit()`.
pub fn db(&mut self) -> &mut JournalDB {
&mut self.db
}
/// Create a new contract at address `contract`. If there is already an account at the address
/// it will have its code reset, ready for `init_code()`.
pub fn new_contract(&mut self, contract: &Address, balance: U256) {
self.cache.borrow_mut().insert(contract.clone(), Some(Account::new_contract(balance)));
}
/// Remove an existing account.
pub fn kill_account(&mut self, account: &Address) {
self.cache.borrow_mut().insert(account.clone(), None);
}
/// Determine whether an account exists.
pub fn exists(&self, a: &Address) -> bool {
self.cache.borrow().get(&a).unwrap_or(&None).is_some() || SecTrieDB::new(&self.db, &self.root).contains(&a)
}
/// Get the balance of account `a`.
pub fn balance(&self, a: &Address) -> U256 {
self.get(a, false).as_ref().map_or(U256::zero(), |account| account.balance().clone())
}
/// Get the nonce of account `a`.
pub fn nonce(&self, a: &Address) -> U256 {
self.get(a, false).as_ref().map_or(U256::zero(), |account| account.nonce().clone())
}
/// Mutate storage of account `a` so that it is `value` for `key`.
pub fn storage_at(&self, a: &Address, key: &H256) -> H256 {
self.get(a, false).as_ref().map_or(H256::new(), |a|a.storage_at(&self.db, key))
}
/// Mutate storage of account `a` so that it is `value` for `key`.
pub fn code(&self, a: &Address) -> Option<Bytes> {
self.get(a, true).as_ref().map_or(None, |a|a.code().map(|x|x.to_vec()))
}
/// Add `incr` to the balance of account `a`.
pub fn add_balance(&mut self, a: &Address, incr: &U256) {
let old = self.balance(a);
self.require(a, false).add_balance(incr);
trace!("state: add_balance({}, {}): {} -> {}\n", a, incr, old, self.balance(a));
}
/// Subtract `decr` from the balance of account `a`.
pub fn sub_balance(&mut self, a: &Address, decr: &U256) {
let old = self.balance(a);
self.require(a, false).sub_balance(decr);
trace!("state: sub_balance({}, {}): {} -> {}\n", a, decr, old, self.balance(a));
}
/// Subtracts `by` from the balance of `from` and adds it to that of `to`.
pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256) {
self.sub_balance(from, by);
self.add_balance(to, by);
}
/// Increment the nonce of account `a` by 1.
pub fn inc_nonce(&mut self, a: &Address) {
self.require(a, false).inc_nonce()
}
/// Mutate storage of account `a` so that it is `value` for `key`.
pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) {
self.require(a, false).set_storage(key, value)
}
/// Initialise the code of account `a` so that it is `value` for `key`.
/// NOTE: Account should have been created with `new_contract`.
pub fn init_code(&mut self, a: &Address, code: Bytes) {
self.require_or_from(a, true, || Account::new_contract(U256::from(0u8)), |_|{}).init_code(code);
}
/// Execute a given transaction.
/// This will change the state accordingly.
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
// let old = self.to_pod();
let e = try!(Executive::new(self, env_info, engine).transact(t));
//println!("Executed: {:?}", e);
// TODO uncomment once to_pod() works correctly.
// trace!("Applied transaction. Diff:\n{}\n", StateDiff::diff_pod(&old, &self.to_pod()));
self.commit();
let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs);
// trace!("Transaction receipt: {:?}", receipt);
Ok(receipt)
}
/// TODO [debris] Please document me
pub fn revert(&mut self, backup: State) {
self.cache = backup.cache;
}
/// Convert into a JSON representation.
pub fn as_json(&self) -> String {
unimplemented!();
}
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
/// `accounts` is mutable because we may need to commit the code or storage and record that.
#[allow(match_ref_pats)]
pub fn commit_into(db: &mut HashDB, root: &mut H256, accounts: &mut HashMap<Address, Option<Account>>) {
// first, commit the sub trees.
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`?
for (_, ref mut a) in accounts.iter_mut() {
match a {
&mut&mut Some(ref mut account) => {
account.commit_storage(db);
account.commit_code(db);
}
&mut&mut None => {}
}
}
{
let mut trie = SecTrieDBMut::from_existing(db, root);
for (address, ref a) in accounts.iter() {
match **a {
Some(ref account) => trie.insert(address, &account.rlp()),
None => trie.remove(address),
}
}
}
}
/// Commits our cached account changes into the trie.
pub fn commit(&mut self) {
Self::commit_into(&mut self.db, &mut self.root, self.cache.borrow_mut().deref_mut());
}
/// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: PodState) {
for (add, acc) in accounts.drain().into_iter() {
self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc)));
}
}
/// Populate a PodAccount map from this state.
pub fn to_hashmap_pod(&self) -> HashMap<Address, PodAccount> {
// TODO: handle database rather than just the cache.
self.cache.borrow().iter().fold(HashMap::new(), |mut m, (add, opt)| {
if let Some(ref acc) = *opt {
m.insert(add.clone(), PodAccount::from_account(acc));
}
m
})
}
/// Populate a PodAccount map from this state.
pub fn to_pod(&self) -> PodState {
// TODO: handle database rather than just the cache.
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
if let Some(ref acc) = *opt {
m.insert(add.clone(), PodAccount::from_account(acc));
}
m
}))
}
/// Pull account `a` in our cache from the trie DB and return it.
/// `require_code` requires that the code be cached, too.
fn get(&self, a: &Address, require_code: bool) -> Ref<Option<Account>> {
self.cache.borrow_mut().entry(a.clone()).or_insert_with(|| {
SecTrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))
});
if require_code {
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
account.cache_code(&self.db);
}
}
Ref::map(self.cache.borrow(), |m| m.get(a).unwrap())
}
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
fn require(&self, a: &Address, require_code: bool) -> RefMut<Account> {
self.require_or_from(a, require_code, || Account::new_basic(U256::from(0u8), self.account_start_nonce), |_|{})
}
/// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too.
/// If it doesn't exist, make account equal the evaluation of `default`.
fn require_or_from<F: FnOnce() -> Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> RefMut<Account> {
self.cache.borrow_mut().entry(a.clone()).or_insert_with(||
SecTrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp)));
let preexists = self.cache.borrow().get(a).unwrap().is_none();
if preexists {
self.cache.borrow_mut().insert(a.clone(), Some(default()));
} else {
not_default(self.cache.borrow_mut().get_mut(a).unwrap().as_mut().unwrap());
}
let b = self.cache.borrow_mut();
RefMut::map(b, |m| m.get_mut(a).unwrap().as_mut().map(|account| {
if require_code {
account.cache_code(&self.db);
}
account
}).unwrap())
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.cache.borrow())
}
}
#[cfg(test)]
mod tests {
use super::*;
use util::hash::*;
use util::trie::*;
use util::rlp::*;
use util::uint::*;
use account::*;
use tests::helpers::*;
#[test]
fn code_from_database() {
let a = Address::zero();
let temp = RandomTempPath::new();
let (root, db) = {
let mut state = get_temp_state_in(temp.as_path());
state.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32)), |_|{});
state.init_code(&a, vec![1, 2, 3]);
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
state.commit();
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8));
assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec()));
}
#[test]
fn storage_at_from_database() {
let a = Address::zero();
let temp = RandomTempPath::new();
let (root, db) = {
let mut state = get_temp_state_in(temp.as_path());
state.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64)));
state.commit();
state.drop()
};
let s = State::from_existing(db, root, U256::from(0u8));
assert_eq!(s.storage_at(&a, &H256::from(&U256::from(01u64))), H256::from(&U256::from(69u64)));
}
#[test]
fn get_from_database() {
let a = Address::zero();
let temp = RandomTempPath::new();
let (root, db) = {
let mut state = get_temp_state_in(temp.as_path());
state.inc_nonce(&a);
state.add_balance(&a, &U256::from(69u64));
state.commit();
assert_eq!(state.balance(&a), U256::from(69u64));
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8));
assert_eq!(state.balance(&a), U256::from(69u64));
assert_eq!(state.nonce(&a), U256::from(1u64));
}
#[test]
fn remove() {
let a = Address::zero();
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
assert_eq!(state.exists(&a), false);
state.inc_nonce(&a);
assert_eq!(state.exists(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64));
state.kill_account(&a);
assert_eq!(state.exists(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64));
}
#[test]
fn remove_from_database() {
let a = Address::zero();
let temp = RandomTempPath::new();
let (root, db) = {
let mut state = get_temp_state_in(temp.as_path());
state.inc_nonce(&a);
state.commit();
assert_eq!(state.exists(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64));
state.drop()
};
let (root, db) = {
let mut state = State::from_existing(db, root, U256::from(0u8));
assert_eq!(state.exists(&a), true);
assert_eq!(state.nonce(&a), U256::from(1u64));
state.kill_account(&a);
state.commit();
assert_eq!(state.exists(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64));
state.drop()
};
let state = State::from_existing(db, root, U256::from(0u8));
assert_eq!(state.exists(&a), false);
assert_eq!(state.nonce(&a), U256::from(0u64));
}
#[test]
fn alter_balance() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
let b = address_from_u64(1u64);
state.add_balance(&a, &U256::from(69u64));
assert_eq!(state.balance(&a), U256::from(69u64));
state.commit();
assert_eq!(state.balance(&a), U256::from(69u64));
state.sub_balance(&a, &U256::from(42u64));
assert_eq!(state.balance(&a), U256::from(27u64));
state.commit();
assert_eq!(state.balance(&a), U256::from(27u64));
state.transfer_balance(&a, &b, &U256::from(18u64));
assert_eq!(state.balance(&a), U256::from(9u64));
assert_eq!(state.balance(&b), U256::from(18u64));
state.commit();
assert_eq!(state.balance(&a), U256::from(9u64));
assert_eq!(state.balance(&b), U256::from(18u64));
}
#[test]
fn alter_nonce() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
state.inc_nonce(&a);
assert_eq!(state.nonce(&a), U256::from(1u64));
state.inc_nonce(&a);
assert_eq!(state.nonce(&a), U256::from(2u64));
state.commit();
assert_eq!(state.nonce(&a), U256::from(2u64));
state.inc_nonce(&a);
assert_eq!(state.nonce(&a), U256::from(3u64));
state.commit();
assert_eq!(state.nonce(&a), U256::from(3u64));
}
#[test]
fn balance_nonce() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
assert_eq!(state.balance(&a), U256::from(0u64));
assert_eq!(state.nonce(&a), U256::from(0u64));
state.commit();
assert_eq!(state.balance(&a), U256::from(0u64));
assert_eq!(state.nonce(&a), U256::from(0u64));
}
#[test]
fn ensure_cached() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
state.require(&a, false);
state.commit();
assert_eq!(state.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785");
}
#[test]
fn create_empty() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.commit();
assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421");
}
}

99
ethcore/src/state_diff.rs Normal file
View File

@@ -0,0 +1,99 @@
use util::*;
use pod_state::*;
use account_diff::*;
#[derive(Debug,Clone,PartialEq,Eq)]
/// TODO [Gav Wood] Please document me
pub struct StateDiff (BTreeMap<Address, AccountDiff>);
impl StateDiff {
/// Calculate and return diff between `pre` state and `post` state.
pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| AccountDiff::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect())
}
}
impl fmt::Display for StateDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (add, acc) in &self.0 {
try!(write!(f, "{} {}: {}", acc.existance(), add, acc));
}
Ok(())
}
}
#[cfg(test)]
mod test {
use common::*;
use pod_state::*;
use account_diff::*;
use pod_account::*;
use super::*;
#[test]
fn create_delete() {
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
assert_eq!(StateDiff::diff_pod(&a, &PodState::new()), StateDiff(map![
x!(1) => AccountDiff{
balance: Diff::Died(x!(69)),
nonce: Diff::Died(x!(0)),
code: Diff::Died(vec![]),
storage: map![],
}
]));
assert_eq!(StateDiff::diff_pod(&PodState::new(), &a), StateDiff(map![
x!(1) => AccountDiff{
balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)),
code: Diff::Born(vec![]),
storage: map![],
}
]));
}
#[test]
fn create_delete_with_unchanged() {
let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]);
let b = PodState::from(map![
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
]);
assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![
x!(2) => AccountDiff{
balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)),
code: Diff::Born(vec![]),
storage: map![],
}
]));
assert_eq!(StateDiff::diff_pod(&b, &a), StateDiff(map![
x!(2) => AccountDiff{
balance: Diff::Died(x!(69)),
nonce: Diff::Died(x!(0)),
code: Diff::Died(vec![]),
storage: map![],
}
]));
}
#[test]
fn change_with_unchanged() {
let a = PodState::from(map![
x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]),
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
]);
let b = PodState::from(map![
x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]),
x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![])
]);
assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![
x!(1) => AccountDiff{
balance: Diff::Same,
nonce: Diff::Changed(x!(0), x!(1)),
code: Diff::Same,
storage: map![],
}
]));
}
}

51
ethcore/src/substate.rs Normal file
View File

@@ -0,0 +1,51 @@
use common::*;
/// State changes which should be applied in finalize,
/// after transaction is fully executed.
#[derive(Debug)]
pub struct Substate {
/// Any accounts that have suicided.
pub suicides: HashSet<Address>,
/// Any logs.
pub logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero -> zero.
pub sstore_clears_count: U256,
/// Created contracts.
pub contracts_created: Vec<Address>
}
impl Substate {
/// Creates new substate.
pub fn new() -> Self {
Substate {
suicides: HashSet::new(),
logs: vec![],
sstore_clears_count: U256::zero(),
contracts_created: vec![]
}
}
/// TODO [Gav Wood] Please document me
pub fn accrue(&mut self, s: Substate) {
self.suicides.extend(s.suicides.into_iter());
self.logs.extend(s.logs.into_iter());
self.sstore_clears_count = self.sstore_clears_count + s.sstore_clears_count;
self.contracts_created.extend(s.contracts_created.into_iter());
}
}
#[cfg(test)]
mod tests {
use super::*;
use util::hash::*;
#[test]
fn accrue() {
let mut sub_state = Substate::new();
sub_state.contracts_created.push(address_from_u64(1u64));
let mut sub_state_2 = Substate::new();
sub_state_2.contracts_created.push(address_from_u64(2u64));
sub_state.accrue(sub_state_2);
assert_eq!(sub_state.contracts_created.len(), 2);
}
}

View File

@@ -0,0 +1,81 @@
use super::test_common::*;
use client::{BlockChainClient,Client};
use pod_state::*;
use block::Block;
use ethereum;
use super::helpers::*;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
for (name, test) in json.as_object().unwrap() {
let mut fail = false;
{
let mut fail_unless = |cond: bool| if !cond && !fail {
failed.push(name.clone());
flush(format!("FAIL\n"));
fail = true;
true
} else {false};
flush(format!(" - {}...", name));
let blocks: Vec<(Bytes, bool)> = test["blocks"].as_array().unwrap().iter().map(|e| (xjson!(&e["rlp"]), e.find("blockHeader").is_some())).collect();
let mut spec = match era {
ChainEra::Frontier => ethereum::new_frontier_test(),
ChainEra::Homestead => ethereum::new_homestead_test(),
};
let s = PodState::from_json(test.find("pre").unwrap());
spec.set_genesis_state(s);
spec.overwrite_genesis(test.find("genesisBlockHeader").unwrap());
assert!(spec.is_state_root_valid());
let genesis_hash = spec.genesis_header().hash();
assert_eq!(genesis_hash, H256::from_json(&test.find("genesisBlockHeader").unwrap()["hash"]));
let temp = RandomTempPath::new();
{
let client = Client::new(spec, temp.as_path(), IoChannel::disconnected()).unwrap();
assert_eq!(client.chain_info().best_block_hash, genesis_hash);
for (b, is_valid) in blocks.into_iter() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
}
client.flush_queue();
let imported_ok = client.import_verified_blocks(&IoChannel::disconnected()) > 0;
assert_eq!(imported_ok, is_valid);
}
fail_unless(client.chain_info().best_block_hash == H256::from_json(&test["lastblockhash"]));
}
}
if !fail {
flush(format!("ok\n"));
}
}
println!("!!! {:?} tests from failed.", failed.len());
failed
}
fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data, ChainEra::Frontier)
}
declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"}
declare_test!{BlockchainTests_bcForkBlockTest, "BlockchainTests/bcForkBlockTest"}
declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"}
declare_test!{BlockchainTests_bcForkUncle, "BlockchainTests/bcForkUncle"}
declare_test!{BlockchainTests_bcGasPricerTest, "BlockchainTests/bcGasPricerTest"}
declare_test!{BlockchainTests_bcInvalidHeaderTest, "BlockchainTests/bcInvalidHeaderTest"}
declare_test!{BlockchainTests_bcInvalidRLPTest, "BlockchainTests/bcInvalidRLPTest"}
declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"}
declare_test!{BlockchainTests_bcRPC_API_Test, "BlockchainTests/bcRPC_API_Test"}
declare_test!{BlockchainTests_bcStateTest, "BlockchainTests/bcStateTest"}
declare_test!{BlockchainTests_bcTotalDifficultyTest, "BlockchainTests/bcTotalDifficultyTest"}
declare_test!{BlockchainTests_bcUncleHeaderValiditiy, "BlockchainTests/bcUncleHeaderValiditiy"}
declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"}
declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"}
declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"}
declare_test!{BlockchainTests_RandomTests_bl10251623GO, "BlockchainTests/RandomTests/bl10251623GO"}
declare_test!{BlockchainTests_RandomTests_bl201507071825GO, "BlockchainTests/RandomTests/bl201507071825GO"}

View File

@@ -0,0 +1,78 @@
use client::{BlockChainClient,Client};
use super::test_common::*;
use super::helpers::*;
#[test]
fn created() {
let dir = RandomTempPath::new();
let client_result = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected());
assert!(client_result.is_ok());
}
#[test]
fn imports_from_empty() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue();
}
#[test]
fn imports_good_block() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition");
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
let block = client.block_header_at(1).unwrap();
assert!(!block.is_empty());
}
#[test]
fn query_none_block() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header_at(188);
assert!(non_existant.is_none());
}
#[test]
fn query_bad_block() {
let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]);
let client = client_result.reference();
let bad_block:Option<Bytes> = client.block_header_at(1);
assert!(bad_block.is_none());
}
#[test]
fn returns_chain_info() {
let dummy_block = get_good_dummy_block();
let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]);
let client = client_result.reference();
let block = BlockView::new(&dummy_block);
let info = client.chain_info();
assert_eq!(info.best_block_hash, block.header().hash());
}
#[test]
fn imports_block_sequence() {
let client_result = generate_dummy_client(6);
let client = client_result.reference();
let block = client.block_header_at(5).unwrap();
assert!(!block.is_empty());
}
#[test]
fn can_collect_garbage() {
let client_result = generate_dummy_client(100);
let client = client_result.reference();
client.tick();
assert!(client.cache_info().blocks < 100 * 1024);
}

View File

@@ -0,0 +1,285 @@
use super::test_common::*;
use state::*;
use executive::*;
use spec::*;
use engine::*;
use evm;
use evm::{Schedule, Ext, Factory, VMType, ContractCreateResult, MessageCallResult};
use ethereum;
use externalities::*;
use substate::*;
use tests::helpers::*;
struct TestEngine {
vm_factory: Factory,
spec: Spec,
max_depth: usize
}
impl TestEngine {
fn new(max_depth: usize, vm_type: VMType) -> TestEngine {
TestEngine {
vm_factory: Factory::new(vm_type),
spec: ethereum::new_frontier_test(),
max_depth: max_depth
}
}
}
impl Engine for TestEngine {
fn name(&self) -> &str { "TestEngine" }
fn spec(&self) -> &Spec { &self.spec }
fn vm_factory(&self) -> &Factory { &self.vm_factory }
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
let mut schedule = Schedule::new_frontier();
schedule.max_depth = self.max_depth;
schedule
}
}
struct CallCreate {
data: Bytes,
destination: Option<Address>,
gas_limit: U256,
value: U256
}
/// Tiny wrapper around executive externalities.
/// Stores callcreates.
struct TestExt<'a> {
ext: Externalities<'a>,
callcreates: Vec<CallCreate>,
contract_address: Address
}
impl<'a> TestExt<'a> {
fn new(state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
output: OutputPolicy<'a>,
address: Address) -> Self {
TestExt {
contract_address: contract_address(&address, &state.nonce(&address)),
ext: Externalities::new(state, info, engine, depth, origin_info, substate, output),
callcreates: vec![]
}
}
}
impl<'a> Ext for TestExt<'a> {
fn storage_at(&self, key: &H256) -> H256 {
self.ext.storage_at(key)
}
fn set_storage(&mut self, key: H256, value: H256) {
self.ext.set_storage(key, value)
}
fn exists(&self, address: &Address) -> bool {
self.ext.exists(address)
}
fn balance(&self, address: &Address) -> U256 {
self.ext.balance(address)
}
fn blockhash(&self, number: &U256) -> H256 {
self.ext.blockhash(number)
}
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult {
self.callcreates.push(CallCreate {
data: code.to_vec(),
destination: None,
gas_limit: *gas,
value: *value
});
ContractCreateResult::Created(self.contract_address.clone(), *gas)
}
fn call(&mut self,
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
self.callcreates.push(CallCreate {
data: data.to_vec(),
destination: Some(receive_address.clone()),
gas_limit: *gas,
value: value.unwrap()
});
MessageCallResult::Success(*gas)
}
fn extcode(&self, address: &Address) -> Bytes {
self.ext.extcode(address)
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
self.ext.log(topics, data)
}
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
self.ext.ret(gas, data)
}
fn suicide(&mut self, refund_address: &Address) {
self.ext.suicide(refund_address)
}
fn schedule(&self) -> &Schedule {
self.ext.schedule()
}
fn env_info(&self) -> &EnvInfo {
self.ext.env_info()
}
fn depth(&self) -> usize {
0
}
fn inc_sstore_clears(&mut self) {
self.ext.inc_sstore_clears()
}
}
fn do_json_test(json_data: &[u8]) -> Vec<String> {
let vms = VMType::all();
vms
.iter()
.flat_map(|vm| do_json_test_for(vm, json_data))
.collect()
}
fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
for (name, test) in json.as_object().unwrap() {
println!("name: {:?}", name);
// sync io is usefull when something crashes in jit
// ::std::io::stdout().write(&name.as_bytes());
// ::std::io::stdout().write(b"\n");
// ::std::io::stdout().flush();
let mut fail = false;
//let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true };
let mut fail_unless = |cond: bool, s: &str | if !cond && !fail {
failed.push(format!("[{}] {}: {}", vm, name, s));
fail = true
};
// test env
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() {
let address = Address::from(addr.as_ref());
let balance = xjson!(&s["balance"]);
let code = xjson!(&s["code"]);
let _nonce: U256 = xjson!(&s["nonce"]);
state.new_contract(&address, balance);
state.init_code(&address, code);
BTreeMap::from_json(&s["storage"]).into_iter().foreach(|(k, v)| state.set_storage(&address, k, v));
});
let info = test.find("env").map(|env| {
EnvInfo::from_json(env)
}).unwrap_or_default();
let engine = TestEngine::new(1, vm.clone());
// params
let mut params = ActionParams::default();
test.find("exec").map(|exec| {
params.address = xjson!(&exec["address"]);
params.sender = xjson!(&exec["caller"]);
params.origin = xjson!(&exec["origin"]);
params.code = xjson!(&exec["code"]);
params.data = xjson!(&exec["data"]);
params.gas = xjson!(&exec["gas"]);
params.gas_price = xjson!(&exec["gasPrice"]);
params.value = ActionValue::Transfer(xjson!(&exec["value"]));
});
let out_of_gas = test.find("callcreates").map(|_calls| {
}).is_none();
let mut substate = Substate::new();
let mut output = vec![];
// execute
let (res, callcreates) = {
let mut ex = TestExt::new(&mut state,
&info,
&engine,
0,
OriginInfo::from(&params),
&mut substate,
OutputPolicy::Return(BytesRef::Flexible(&mut output)),
params.address.clone());
let evm = engine.vm_factory().create();
let res = evm.exec(params, &mut ex);
(res, ex.callcreates)
};
// then validate
match res {
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
Ok(gas_left) => {
// println!("name: {}, gas_left : {:?}", name, gas_left);
fail_unless(!out_of_gas, "expected to run out of gas.");
fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect");
fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect");
test.find("post").map(|pre| for (addr, s) in pre.as_object().unwrap() {
let address = Address::from(addr.as_ref());
fail_unless(state.code(&address).unwrap_or_else(|| vec![]) == Bytes::from_json(&s["code"]), "code is incorrect");
fail_unless(state.balance(&address) == xjson!(&s["balance"]), "balance is incorrect");
fail_unless(state.nonce(&address) == xjson!(&s["nonce"]), "nonce is incorrect");
BTreeMap::from_json(&s["storage"]).iter().foreach(|(k, v)| fail_unless(&state.storage_at(&address, &k) == v, "storage is incorrect"));
});
let cc = test["callcreates"].as_array().unwrap();
fail_unless(callcreates.len() == cc.len(), "callcreates does not match");
for i in 0..cc.len() {
let callcreate = &callcreates[i];
let expected = &cc[i];
fail_unless(callcreate.data == Bytes::from_json(&expected["data"]), "callcreates data is incorrect");
fail_unless(callcreate.destination == xjson!(&expected["destination"]), "callcreates destination is incorrect");
fail_unless(callcreate.value == xjson!(&expected["value"]), "callcreates value is incorrect");
fail_unless(callcreate.gas_limit == xjson!(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
}
}
}
}
for f in &failed {
println!("FAILED: {:?}", f);
}
//assert!(false);
failed
}
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
// TODO [todr] Fails with Signal 11 when using JIT
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}
declare_test!{ExecutiveTests_vmLogTest, "VMTests/vmLogTest"}
declare_test!{ExecutiveTests_vmPerformanceTest, "VMTests/vmPerformanceTest"}
declare_test!{ExecutiveTests_vmPushDupSwapTest, "VMTests/vmPushDupSwapTest"}
declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"}
declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperationsTest"}
declare_test!{ExecutiveTests_vmtests, "VMTests/vmtests"}

View File

@@ -0,0 +1,261 @@
use client::{BlockChainClient,Client};
use std::env;
use super::test_common::*;
use std::path::PathBuf;
use spec::*;
use std::fs::{remove_dir_all};
use blockchain::{BlockChain};
use state::*;
use rocksdb::*;
pub enum ChainEra {
Frontier,
Homestead,
}
pub struct RandomTempPath {
path: PathBuf
}
impl RandomTempPath {
pub fn new() -> RandomTempPath {
let mut dir = env::temp_dir();
dir.push(H32::random().hex());
RandomTempPath {
path: dir.clone()
}
}
pub fn as_path(&self) -> &PathBuf {
&self.path
}
pub fn as_str(&self) -> &str {
self.path.to_str().unwrap()
}
}
impl Drop for RandomTempPath {
fn drop(&mut self) {
if let Err(e) = remove_dir_all(self.as_path()) {
panic!("failed to remove temp directory, probably something failed to destroyed ({})", e);
}
}
}
#[allow(dead_code)]
pub struct GuardedTempResult<T> {
result: T,
temp: RandomTempPath
}
impl<T> GuardedTempResult<T> {
pub fn reference(&self) -> &T {
&self.result
}
pub fn reference_mut(&mut self) -> &mut T {
&mut self.result
}
}
pub fn get_test_spec() -> Spec {
Spec::new_test()
}
pub fn create_test_block(header: &Header) -> Bytes {
let mut rlp = RlpStream::new_list(3);
rlp.append(header);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.out()
}
fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header {
let mut header = Header::new();
header.gas_limit = x!(0);
header.difficulty = x!(order * 100);
header.timestamp = (order * 10) as u64;
header.number = order as u64;
header.parent_hash = parent_hash;
header.state_root = H256::zero();
header
}
fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option<Bytes>) -> Bytes {
let mut header = create_unverifiable_block_header(order, parent_hash);
header.extra_data = match extra {
Some(extra_data) => extra_data,
None => {
let base = (order & 0x000000ff) as u8;
let generated: Vec<u8> = vec![base + 1, base + 2, base + 3];
generated
}
};
create_test_block(&header)
}
fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes {
create_test_block(&create_unverifiable_block_header(order, parent_hash))
}
pub fn create_test_block_with_data(header: &Header, transactions: &[&Transaction], uncles: &[Header]) -> Bytes {
let mut rlp = RlpStream::new_list(3);
rlp.append(header);
rlp.begin_list(transactions.len());
for t in transactions {
rlp.append_raw(&t.rlp_bytes_opt(Seal::With), 1);
}
rlp.append(&uncles);
rlp.out()
}
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
let state_root = test_engine.spec().genesis_header().state_root;
let mut rolling_hash = test_engine.spec().genesis_header().hash();
let mut rolling_block_number = 1;
let mut rolling_timestamp = 40;
for _ in 0..block_number {
let mut header = Header::new();
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
header.timestamp = rolling_timestamp;
header.number = rolling_block_number;
header.parent_hash = rolling_hash;
header.state_root = state_root.clone();
rolling_hash = header.hash();
rolling_block_number = rolling_block_number + 1;
rolling_timestamp = rolling_timestamp + 10;
if let Err(_) = client.import_block(create_test_block(&header)) {
panic!("error importing block which is valid by definition");
}
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
GuardedTempResult::<Arc<Client>> {
temp: dir,
result: client
}
}
pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
for block in &blocks {
if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed");
}
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
GuardedTempResult::<Arc<Client>> {
temp: dir,
result: client
}
}
pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash()));
}
GuardedTempResult::<BlockChain> {
temp: temp,
result: bc
}
}
pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None));
}
GuardedTempResult::<BlockChain> {
temp: temp,
result: bc
}
}
pub fn generate_dummy_empty_blockchain() -> GuardedTempResult<BlockChain> {
let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
GuardedTempResult::<BlockChain> {
temp: temp,
result: bc
}
}
pub fn get_temp_journal_db() -> GuardedTempResult<JournalDB> {
let temp = RandomTempPath::new();
let db = DB::open_default(temp.as_str()).unwrap();
let journal_db = JournalDB::new(db);
GuardedTempResult {
temp: temp,
result: journal_db
}
}
pub fn get_temp_state() -> GuardedTempResult<State> {
let temp = RandomTempPath::new();
let journal_db = get_temp_journal_db_in(temp.as_path());
GuardedTempResult {
temp: temp,
result: State::new(journal_db, U256::from(0u8))
}
}
pub fn get_temp_journal_db_in(path: &Path) -> JournalDB {
let db = DB::open_default(path.to_str().unwrap()).unwrap();
JournalDB::new(db)
}
pub fn get_temp_state_in(path: &Path) -> State {
let journal_db = get_temp_journal_db_in(path);
State::new(journal_db, U256::from(0u8))
}
pub fn get_good_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_engine.spec().genesis_header().hash();
block_header.state_root = test_engine.spec().genesis_header().state_root;
create_test_block(&block_header)
}
pub fn get_bad_state_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_engine.spec().genesis_header().hash();
block_header.state_root = x!(0xbad);
create_test_block(&block_header)
}

View File

@@ -0,0 +1,21 @@
use super::test_common::*;
use super::helpers::*;
use super::chain::json_chain_test;
fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data, ChainEra::Homestead)
}
declare_test!{BlockchainTests_Homestead_bcBlockGasLimitTest, "BlockchainTests/Homestead/bcBlockGasLimitTest"}
declare_test!{BlockchainTests_Homestead_bcForkStressTest, "BlockchainTests/Homestead/bcForkStressTest"}
declare_test!{BlockchainTests_Homestead_bcGasPricerTest, "BlockchainTests/Homestead/bcGasPricerTest"}
declare_test!{BlockchainTests_Homestead_bcInvalidHeaderTest, "BlockchainTests/Homestead/bcInvalidHeaderTest"}
declare_test!{BlockchainTests_Homestead_bcInvalidRLPTest, "BlockchainTests/Homestead/bcInvalidRLPTest"}
declare_test!{BlockchainTests_Homestead_bcMultiChainTest, "BlockchainTests/Homestead/bcMultiChainTest"}
declare_test!{BlockchainTests_Homestead_bcRPC_API_Test, "BlockchainTests/Homestead/bcRPC_API_Test"}
declare_test!{BlockchainTests_Homestead_bcStateTest, "BlockchainTests/Homestead/bcStateTest"}
declare_test!{BlockchainTests_Homestead_bcTotalDifficultyTest, "BlockchainTests/Homestead/bcTotalDifficultyTest"}
declare_test!{BlockchainTests_Homestead_bcUncleHeaderValiditiy, "BlockchainTests/Homestead/bcUncleHeaderValiditiy"}
declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/bcUncleTest"}
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}

View File

@@ -0,0 +1,23 @@
use super::test_common::*;
use super::helpers::*;
use super::state::json_chain_test;
fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data, ChainEra::Homestead)
}
declare_test!{StateTests_Homestead_stCallCodes, "StateTests/Homestead/stCallCodes"}
declare_test!{StateTests_Homestead_stCallCreateCallCodeTest, "StateTests/Homestead/stCallCreateCallCodeTest"}
declare_test!{StateTests_Homestead_stDelegatecallTest, "StateTests/Homestead/stDelegatecallTest"}
declare_test!{StateTests_Homestead_stInitCodeTest, "StateTests/Homestead/stInitCodeTest"}
declare_test!{StateTests_Homestead_stLogTests, "StateTests/Homestead/stLogTests"}
declare_test!{heavy => StateTests_Homestead_stMemoryStressTest, "StateTests/Homestead/stMemoryStressTest"}
declare_test!{heavy => StateTests_Homestead_stMemoryTest, "StateTests/Homestead/stMemoryTest"}
declare_test!{StateTests_Homestead_stPreCompiledContracts, "StateTests/Homestead/stPreCompiledContracts"}
declare_test!{heavy => StateTests_Homestead_stQuadraticComplexityTest, "StateTests/Homestead/stQuadraticComplexityTest"}
declare_test!{StateTests_Homestead_stRecursiveCreate, "StateTests/Homestead/stRecursiveCreate"}
declare_test!{StateTests_Homestead_stRefundTest, "StateTests/Homestead/stRefundTest"}
declare_test!{StateTests_Homestead_stSpecialTest, "StateTests/Homestead/stSpecialTest"}
declare_test!{StateTests_Homestead_stSystemOperationsTest, "StateTests/Homestead/stSystemOperationsTest"}
declare_test!{StateTests_Homestead_stTransactionTest, "StateTests/Homestead/stTransactionTest"}
declare_test!{StateTests_Homestead_stWalletTest, "StateTests/Homestead/stWalletTest"}

11
ethcore/src/tests/mod.rs Normal file
View File

@@ -0,0 +1,11 @@
#[macro_use]
mod test_common;
mod transaction;
mod executive;
mod state;
mod client;
mod chain;
pub mod helpers;
mod homestead_state;
mod homestead_chain;

747
ethcore/src/tests/state.rs Normal file
View File

@@ -0,0 +1,747 @@
use super::test_common::*;
use super::helpers::*;
use pod_state::*;
use state_diff::*;
use ethereum;
fn do_json_test(json_data: &[u8]) -> Vec<String> {
json_chain_test(json_data, ChainEra::Frontier)
}
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
let engine = match era {
ChainEra::Frontier => ethereum::new_mainnet_like(),
ChainEra::Homestead => ethereum::new_homestead_test(),
}.to_engine().unwrap();
flush(format!("\n"));
for (name, test) in json.as_object().unwrap() {
let mut fail = false;
{
let mut fail_unless = |cond: bool| if !cond && !fail {
failed.push(name.clone());
flush(format!("FAIL\n"));
fail = true;
true
} else {false};
flush(format!(" - {}...", name));
let t = Transaction::from_json(&test["transaction"]);
let env = EnvInfo::from_json(&test["env"]);
let _out = Bytes::from_json(&test["out"]);
let post_state_root = xjson!(&test["postStateRoot"]);
let pre = PodState::from_json(&test["pre"]);
let post = PodState::from_json(&test["post"]);
let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect();
//println!("Transaction: {:?}", t);
//println!("Env: {:?}", env);
let calc_post = sec_trie_root(post.get().iter().map(|(k, v)| (k.to_vec(), v.rlp())).collect());
if fail_unless(post_state_root == calc_post) {
println!("!!! {}: Trie root mismatch (got: {}, expect: {}):", name, calc_post, post_state_root);
println!("!!! Post:\n{}", post);
} else {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
state.populate_from(pre);
state.commit();
let res = state.apply(&env, engine.deref(), &t);
if fail_unless(state.root() == &post_state_root) {
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);
let our_post = state.to_pod();
println!("Got:\n{}", our_post);
println!("Expect:\n{}", post);
println!("Diff ---expect -> +++got:\n{}", StateDiff::diff_pod(&post, &our_post));
}
if let Ok(r) = res {
if fail_unless(logs == r.logs) {
println!("!!! {}: Logs mismatch:", name);
println!("Got:\n{:?}", r.logs);
println!("Expect:\n{:?}", logs);
}
}
}
}
if !fail {
flush(format!("ok\n"));
}
// TODO: Add extra APIs for output
//if fail_unless(out == r.)
}
println!("!!! {:?} tests from failed.", failed.len());
failed
}
declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"}
declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"}
//declare_test!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"}
declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"}
declare_test!{StateTests_stExample, "StateTests/stExample"}
declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"}
declare_test!{StateTests_stLogTests, "StateTests/stLogTests"}
declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"}
declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"}
declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"}
declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"}
declare_test!{StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"}
declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"}
declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"}
declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"}
declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"}
declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"}
declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"}
declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}
declare_test!{StateTests_RandomTests_st201503121803PYTHON, "StateTests/RandomTests/st201503121803PYTHON"}
declare_test!{StateTests_RandomTests_st201503121806PYTHON, "StateTests/RandomTests/st201503121806PYTHON"}
declare_test!{StateTests_RandomTests_st201503121848GO, "StateTests/RandomTests/st201503121848GO"}
declare_test!{StateTests_RandomTests_st201503121849GO, "StateTests/RandomTests/st201503121849GO"}
declare_test!{StateTests_RandomTests_st201503121850GO, "StateTests/RandomTests/st201503121850GO"}
declare_test!{StateTests_RandomTests_st201503121851GO, "StateTests/RandomTests/st201503121851GO"}
declare_test!{StateTests_RandomTests_st201503121953GO, "StateTests/RandomTests/st201503121953GO"}
declare_test!{StateTests_RandomTests_st201503122023GO, "StateTests/RandomTests/st201503122023GO"}
declare_test!{StateTests_RandomTests_st201503122023PYTHON, "StateTests/RandomTests/st201503122023PYTHON"}
declare_test!{StateTests_RandomTests_st201503122027GO, "StateTests/RandomTests/st201503122027GO"}
declare_test!{StateTests_RandomTests_st201503122054GO, "StateTests/RandomTests/st201503122054GO"}
declare_test!{StateTests_RandomTests_st201503122055GO, "StateTests/RandomTests/st201503122055GO"}
declare_test!{StateTests_RandomTests_st201503122115CPPJIT, "StateTests/RandomTests/st201503122115CPPJIT"}
declare_test!{StateTests_RandomTests_st201503122115GO, "StateTests/RandomTests/st201503122115GO"}
declare_test!{StateTests_RandomTests_st201503122123GO, "StateTests/RandomTests/st201503122123GO"}
declare_test!{StateTests_RandomTests_st201503122124GO, "StateTests/RandomTests/st201503122124GO"}
declare_test!{StateTests_RandomTests_st201503122128PYTHON, "StateTests/RandomTests/st201503122128PYTHON"}
declare_test!{StateTests_RandomTests_st201503122140GO, "StateTests/RandomTests/st201503122140GO"}
declare_test!{StateTests_RandomTests_st201503122159GO, "StateTests/RandomTests/st201503122159GO"}
declare_test!{StateTests_RandomTests_st201503122204GO, "StateTests/RandomTests/st201503122204GO"}
declare_test!{StateTests_RandomTests_st201503122212GO, "StateTests/RandomTests/st201503122212GO"}
declare_test!{StateTests_RandomTests_st201503122231GO, "StateTests/RandomTests/st201503122231GO"}
declare_test!{StateTests_RandomTests_st201503122238GO, "StateTests/RandomTests/st201503122238GO"}
declare_test!{StateTests_RandomTests_st201503122252GO, "StateTests/RandomTests/st201503122252GO"}
declare_test!{StateTests_RandomTests_st201503122316GO, "StateTests/RandomTests/st201503122316GO"}
declare_test!{StateTests_RandomTests_st201503122324GO, "StateTests/RandomTests/st201503122324GO"}
declare_test!{StateTests_RandomTests_st201503122358GO, "StateTests/RandomTests/st201503122358GO"}
declare_test!{StateTests_RandomTests_st201503130002GO, "StateTests/RandomTests/st201503130002GO"}
declare_test!{StateTests_RandomTests_st201503130005GO, "StateTests/RandomTests/st201503130005GO"}
declare_test!{StateTests_RandomTests_st201503130007GO, "StateTests/RandomTests/st201503130007GO"}
declare_test!{StateTests_RandomTests_st201503130010GO, "StateTests/RandomTests/st201503130010GO"}
declare_test!{StateTests_RandomTests_st201503130023PYTHON, "StateTests/RandomTests/st201503130023PYTHON"}
declare_test!{StateTests_RandomTests_st201503130059GO, "StateTests/RandomTests/st201503130059GO"}
declare_test!{StateTests_RandomTests_st201503130101GO, "StateTests/RandomTests/st201503130101GO"}
declare_test!{StateTests_RandomTests_st201503130109GO, "StateTests/RandomTests/st201503130109GO"}
declare_test!{StateTests_RandomTests_st201503130117GO, "StateTests/RandomTests/st201503130117GO"}
declare_test!{StateTests_RandomTests_st201503130122GO, "StateTests/RandomTests/st201503130122GO"}
declare_test!{StateTests_RandomTests_st201503130156GO, "StateTests/RandomTests/st201503130156GO"}
declare_test!{StateTests_RandomTests_st201503130156PYTHON, "StateTests/RandomTests/st201503130156PYTHON"}
declare_test!{StateTests_RandomTests_st201503130207GO, "StateTests/RandomTests/st201503130207GO"}
declare_test!{StateTests_RandomTests_st201503130219CPPJIT, "StateTests/RandomTests/st201503130219CPPJIT"}
declare_test!{StateTests_RandomTests_st201503130219GO, "StateTests/RandomTests/st201503130219GO"}
declare_test!{StateTests_RandomTests_st201503130243GO, "StateTests/RandomTests/st201503130243GO"}
declare_test!{StateTests_RandomTests_st201503130246GO, "StateTests/RandomTests/st201503130246GO"}
declare_test!{StateTests_RandomTests_st201503130321GO, "StateTests/RandomTests/st201503130321GO"}
declare_test!{StateTests_RandomTests_st201503130322GO, "StateTests/RandomTests/st201503130322GO"}
declare_test!{StateTests_RandomTests_st201503130332GO, "StateTests/RandomTests/st201503130332GO"}
declare_test!{StateTests_RandomTests_st201503130359GO, "StateTests/RandomTests/st201503130359GO"}
declare_test!{StateTests_RandomTests_st201503130405GO, "StateTests/RandomTests/st201503130405GO"}
declare_test!{StateTests_RandomTests_st201503130408GO, "StateTests/RandomTests/st201503130408GO"}
declare_test!{StateTests_RandomTests_st201503130411GO, "StateTests/RandomTests/st201503130411GO"}
declare_test!{StateTests_RandomTests_st201503130431GO, "StateTests/RandomTests/st201503130431GO"}
declare_test!{StateTests_RandomTests_st201503130437GO, "StateTests/RandomTests/st201503130437GO"}
declare_test!{StateTests_RandomTests_st201503130450GO, "StateTests/RandomTests/st201503130450GO"}
declare_test!{StateTests_RandomTests_st201503130512CPPJIT, "StateTests/RandomTests/st201503130512CPPJIT"}
declare_test!{StateTests_RandomTests_st201503130512GO, "StateTests/RandomTests/st201503130512GO"}
declare_test!{StateTests_RandomTests_st201503130615GO, "StateTests/RandomTests/st201503130615GO"}
declare_test!{StateTests_RandomTests_st201503130705GO, "StateTests/RandomTests/st201503130705GO"}
declare_test!{StateTests_RandomTests_st201503130733CPPJIT, "StateTests/RandomTests/st201503130733CPPJIT"}
declare_test!{StateTests_RandomTests_st201503130733GO, "StateTests/RandomTests/st201503130733GO"}
declare_test!{StateTests_RandomTests_st201503130747GO, "StateTests/RandomTests/st201503130747GO"}
declare_test!{StateTests_RandomTests_st201503130751GO, "StateTests/RandomTests/st201503130751GO"}
declare_test!{StateTests_RandomTests_st201503130752PYTHON, "StateTests/RandomTests/st201503130752PYTHON"}
declare_test!{StateTests_RandomTests_st201503130757PYTHON, "StateTests/RandomTests/st201503130757PYTHON"}
declare_test!{StateTests_RandomTests_st201503131658GO, "StateTests/RandomTests/st201503131658GO"}
declare_test!{StateTests_RandomTests_st201503131739GO, "StateTests/RandomTests/st201503131739GO"}
declare_test!{StateTests_RandomTests_st201503131755CPPJIT, "StateTests/RandomTests/st201503131755CPPJIT"}
declare_test!{StateTests_RandomTests_st201503131755GO, "StateTests/RandomTests/st201503131755GO"}
declare_test!{StateTests_RandomTests_st201503132001CPPJIT, "StateTests/RandomTests/st201503132001CPPJIT"}
declare_test!{StateTests_RandomTests_st201503132127PYTHON, "StateTests/RandomTests/st201503132127PYTHON"}
declare_test!{StateTests_RandomTests_st201503132201CPPJIT, "StateTests/RandomTests/st201503132201CPPJIT"}
declare_test!{StateTests_RandomTests_st201503132201GO, "StateTests/RandomTests/st201503132201GO"}
declare_test!{StateTests_RandomTests_st201503132202PYTHON, "StateTests/RandomTests/st201503132202PYTHON"}
declare_test!{StateTests_RandomTests_st201503140002PYTHON, "StateTests/RandomTests/st201503140002PYTHON"}
declare_test!{StateTests_RandomTests_st201503140240PYTHON, "StateTests/RandomTests/st201503140240PYTHON"}
declare_test!{StateTests_RandomTests_st201503140522PYTHON, "StateTests/RandomTests/st201503140522PYTHON"}
declare_test!{StateTests_RandomTests_st201503140756PYTHON, "StateTests/RandomTests/st201503140756PYTHON"}
declare_test!{StateTests_RandomTests_st201503141144PYTHON, "StateTests/RandomTests/st201503141144PYTHON"}
declare_test!{StateTests_RandomTests_st201503141510PYTHON, "StateTests/RandomTests/st201503141510PYTHON"}
declare_test!{StateTests_RandomTests_st201503150427PYTHON, "StateTests/RandomTests/st201503150427PYTHON"}
declare_test!{StateTests_RandomTests_st201503150716PYTHON, "StateTests/RandomTests/st201503150716PYTHON"}
declare_test!{StateTests_RandomTests_st201503151450PYTHON, "StateTests/RandomTests/st201503151450PYTHON"}
declare_test!{StateTests_RandomTests_st201503151516PYTHON, "StateTests/RandomTests/st201503151516PYTHON"}
declare_test!{StateTests_RandomTests_st201503151753PYTHON, "StateTests/RandomTests/st201503151753PYTHON"}
declare_test!{StateTests_RandomTests_st201503152057PYTHON, "StateTests/RandomTests/st201503152057PYTHON"}
declare_test!{StateTests_RandomTests_st201503152241PYTHON, "StateTests/RandomTests/st201503152241PYTHON"}
declare_test!{StateTests_RandomTests_st201503160014PYTHON, "StateTests/RandomTests/st201503160014PYTHON"}
declare_test!{StateTests_RandomTests_st201503160733PYTHON, "StateTests/RandomTests/st201503160733PYTHON"}
declare_test!{StateTests_RandomTests_st201503170051PYTHON, "StateTests/RandomTests/st201503170051PYTHON"}
declare_test!{StateTests_RandomTests_st201503170433PYTHON, "StateTests/RandomTests/st201503170433PYTHON"}
declare_test!{StateTests_RandomTests_st201503170523PYTHON, "StateTests/RandomTests/st201503170523PYTHON"}
declare_test!{StateTests_RandomTests_st201503171108PYTHON, "StateTests/RandomTests/st201503171108PYTHON"}
declare_test!{StateTests_RandomTests_st201503181223GO, "StateTests/RandomTests/st201503181223GO"}
declare_test!{StateTests_RandomTests_st201503181225GO, "StateTests/RandomTests/st201503181225GO"}
declare_test!{StateTests_RandomTests_st201503181226CPPJIT, "StateTests/RandomTests/st201503181226CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181227CPPJIT, "StateTests/RandomTests/st201503181227CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181227GO, "StateTests/RandomTests/st201503181227GO"}
declare_test!{StateTests_RandomTests_st201503181229GO, "StateTests/RandomTests/st201503181229GO"}
declare_test!{StateTests_RandomTests_st201503181230CPPJIT, "StateTests/RandomTests/st201503181230CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181230GO, "StateTests/RandomTests/st201503181230GO"}
declare_test!{StateTests_RandomTests_st201503181231GO, "StateTests/RandomTests/st201503181231GO"}
declare_test!{StateTests_RandomTests_st201503181232CPPJIT, "StateTests/RandomTests/st201503181232CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181232GO, "StateTests/RandomTests/st201503181232GO"}
declare_test!{StateTests_RandomTests_st201503181233GO, "StateTests/RandomTests/st201503181233GO"}
declare_test!{StateTests_RandomTests_st201503181234CPPJIT, "StateTests/RandomTests/st201503181234CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181234GO, "StateTests/RandomTests/st201503181234GO"}
declare_test!{StateTests_RandomTests_st201503181235CPPJIT, "StateTests/RandomTests/st201503181235CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181235GO, "StateTests/RandomTests/st201503181235GO"}
declare_test!{StateTests_RandomTests_st201503181236GO, "StateTests/RandomTests/st201503181236GO"}
declare_test!{StateTests_RandomTests_st201503181237GO, "StateTests/RandomTests/st201503181237GO"}
declare_test!{StateTests_RandomTests_st201503181239GO, "StateTests/RandomTests/st201503181239GO"}
declare_test!{StateTests_RandomTests_st201503181241CPPJIT, "StateTests/RandomTests/st201503181241CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181241GO, "StateTests/RandomTests/st201503181241GO"}
declare_test!{StateTests_RandomTests_st201503181243GO, "StateTests/RandomTests/st201503181243GO"}
declare_test!{StateTests_RandomTests_st201503181244GO, "StateTests/RandomTests/st201503181244GO"}
declare_test!{StateTests_RandomTests_st201503181245CPPJIT, "StateTests/RandomTests/st201503181245CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181245GO, "StateTests/RandomTests/st201503181245GO"}
declare_test!{StateTests_RandomTests_st201503181246CPPJIT, "StateTests/RandomTests/st201503181246CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181246GO, "StateTests/RandomTests/st201503181246GO"}
declare_test!{StateTests_RandomTests_st201503181247GO, "StateTests/RandomTests/st201503181247GO"}
declare_test!{StateTests_RandomTests_st201503181248GO, "StateTests/RandomTests/st201503181248GO"}
declare_test!{StateTests_RandomTests_st201503181249GO, "StateTests/RandomTests/st201503181249GO"}
declare_test!{StateTests_RandomTests_st201503181250CPPJIT, "StateTests/RandomTests/st201503181250CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181250GO, "StateTests/RandomTests/st201503181250GO"}
declare_test!{StateTests_RandomTests_st201503181251GO, "StateTests/RandomTests/st201503181251GO"}
declare_test!{StateTests_RandomTests_st201503181252CPPJIT, "StateTests/RandomTests/st201503181252CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181253GO, "StateTests/RandomTests/st201503181253GO"}
declare_test!{StateTests_RandomTests_st201503181255CPPJIT, "StateTests/RandomTests/st201503181255CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181255GO, "StateTests/RandomTests/st201503181255GO"}
declare_test!{StateTests_RandomTests_st201503181257GO, "StateTests/RandomTests/st201503181257GO"}
declare_test!{StateTests_RandomTests_st201503181258CPPJIT, "StateTests/RandomTests/st201503181258CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181258GO, "StateTests/RandomTests/st201503181258GO"}
declare_test!{StateTests_RandomTests_st201503181301CPPJIT, "StateTests/RandomTests/st201503181301CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181301GO, "StateTests/RandomTests/st201503181301GO"}
declare_test!{StateTests_RandomTests_st201503181303GO, "StateTests/RandomTests/st201503181303GO"}
declare_test!{StateTests_RandomTests_st201503181304GO, "StateTests/RandomTests/st201503181304GO"}
declare_test!{StateTests_RandomTests_st201503181305GO, "StateTests/RandomTests/st201503181305GO"}
declare_test!{StateTests_RandomTests_st201503181306GO, "StateTests/RandomTests/st201503181306GO"}
declare_test!{StateTests_RandomTests_st201503181307CPPJIT, "StateTests/RandomTests/st201503181307CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181307GO, "StateTests/RandomTests/st201503181307GO"}
declare_test!{StateTests_RandomTests_st201503181308GO, "StateTests/RandomTests/st201503181308GO"}
declare_test!{StateTests_RandomTests_st201503181309GO, "StateTests/RandomTests/st201503181309GO"}
declare_test!{StateTests_RandomTests_st201503181310GO, "StateTests/RandomTests/st201503181310GO"}
declare_test!{StateTests_RandomTests_st201503181311GO, "StateTests/RandomTests/st201503181311GO"}
declare_test!{StateTests_RandomTests_st201503181313GO, "StateTests/RandomTests/st201503181313GO"}
declare_test!{StateTests_RandomTests_st201503181314GO, "StateTests/RandomTests/st201503181314GO"}
declare_test!{StateTests_RandomTests_st201503181315CPPJIT, "StateTests/RandomTests/st201503181315CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181315GO, "StateTests/RandomTests/st201503181315GO"}
declare_test!{StateTests_RandomTests_st201503181316CPPJIT, "StateTests/RandomTests/st201503181316CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181316PYTHON, "StateTests/RandomTests/st201503181316PYTHON"}
declare_test!{StateTests_RandomTests_st201503181317GO, "StateTests/RandomTests/st201503181317GO"}
declare_test!{StateTests_RandomTests_st201503181318CPPJIT, "StateTests/RandomTests/st201503181318CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181318GO, "StateTests/RandomTests/st201503181318GO"}
declare_test!{StateTests_RandomTests_st201503181319GO, "StateTests/RandomTests/st201503181319GO"}
declare_test!{StateTests_RandomTests_st201503181319PYTHON, "StateTests/RandomTests/st201503181319PYTHON"}
declare_test!{StateTests_RandomTests_st201503181322GO, "StateTests/RandomTests/st201503181322GO"}
declare_test!{StateTests_RandomTests_st201503181323CPPJIT, "StateTests/RandomTests/st201503181323CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181323GO, "StateTests/RandomTests/st201503181323GO"}
declare_test!{StateTests_RandomTests_st201503181324GO, "StateTests/RandomTests/st201503181324GO"}
declare_test!{StateTests_RandomTests_st201503181325GO, "StateTests/RandomTests/st201503181325GO"}
declare_test!{StateTests_RandomTests_st201503181326CPPJIT, "StateTests/RandomTests/st201503181326CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181326GO, "StateTests/RandomTests/st201503181326GO"}
declare_test!{StateTests_RandomTests_st201503181327GO, "StateTests/RandomTests/st201503181327GO"}
declare_test!{StateTests_RandomTests_st201503181329CPPJIT, "StateTests/RandomTests/st201503181329CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181329GO, "StateTests/RandomTests/st201503181329GO"}
declare_test!{StateTests_RandomTests_st201503181330GO, "StateTests/RandomTests/st201503181330GO"}
declare_test!{StateTests_RandomTests_st201503181332GO, "StateTests/RandomTests/st201503181332GO"}
declare_test!{StateTests_RandomTests_st201503181333GO, "StateTests/RandomTests/st201503181333GO"}
declare_test!{StateTests_RandomTests_st201503181334GO, "StateTests/RandomTests/st201503181334GO"}
declare_test!{StateTests_RandomTests_st201503181336CPPJIT, "StateTests/RandomTests/st201503181336CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181337GO, "StateTests/RandomTests/st201503181337GO"}
declare_test!{StateTests_RandomTests_st201503181338GO, "StateTests/RandomTests/st201503181338GO"}
declare_test!{StateTests_RandomTests_st201503181339CPPJIT, "StateTests/RandomTests/st201503181339CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181339GO, "StateTests/RandomTests/st201503181339GO"}
declare_test!{StateTests_RandomTests_st201503181340GO, "StateTests/RandomTests/st201503181340GO"}
declare_test!{StateTests_RandomTests_st201503181341CPPJIT, "StateTests/RandomTests/st201503181341CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181342CPPJIT, "StateTests/RandomTests/st201503181342CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181342GO, "StateTests/RandomTests/st201503181342GO"}
declare_test!{StateTests_RandomTests_st201503181345GO, "StateTests/RandomTests/st201503181345GO"}
declare_test!{StateTests_RandomTests_st201503181346GO, "StateTests/RandomTests/st201503181346GO"}
declare_test!{StateTests_RandomTests_st201503181347CPPJIT, "StateTests/RandomTests/st201503181347CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181347GO, "StateTests/RandomTests/st201503181347GO"}
declare_test!{StateTests_RandomTests_st201503181347PYTHON, "StateTests/RandomTests/st201503181347PYTHON"}
declare_test!{StateTests_RandomTests_st201503181350CPPJIT, "StateTests/RandomTests/st201503181350CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181352GO, "StateTests/RandomTests/st201503181352GO"}
declare_test!{StateTests_RandomTests_st201503181353GO, "StateTests/RandomTests/st201503181353GO"}
declare_test!{StateTests_RandomTests_st201503181354CPPJIT, "StateTests/RandomTests/st201503181354CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181354GO, "StateTests/RandomTests/st201503181354GO"}
declare_test!{StateTests_RandomTests_st201503181355GO, "StateTests/RandomTests/st201503181355GO"}
declare_test!{StateTests_RandomTests_st201503181356CPPJIT, "StateTests/RandomTests/st201503181356CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181357CPPJIT, "StateTests/RandomTests/st201503181357CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181358CPPJIT, "StateTests/RandomTests/st201503181358CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181358GO, "StateTests/RandomTests/st201503181358GO"}
declare_test!{StateTests_RandomTests_st201503181359GO, "StateTests/RandomTests/st201503181359GO"}
//declare_test!{StateTests_RandomTests_st201503181402CPPJIT, "StateTests/RandomTests/st201503181402CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181403GO, "StateTests/RandomTests/st201503181403GO"}
declare_test!{StateTests_RandomTests_st201503181406CPPJIT, "StateTests/RandomTests/st201503181406CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181406GO, "StateTests/RandomTests/st201503181406GO"}
declare_test!{StateTests_RandomTests_st201503181410GO, "StateTests/RandomTests/st201503181410GO"}
declare_test!{StateTests_RandomTests_st201503181412CPPJIT, "StateTests/RandomTests/st201503181412CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181413GO, "StateTests/RandomTests/st201503181413GO"}
declare_test!{StateTests_RandomTests_st201503181415GO, "StateTests/RandomTests/st201503181415GO"}
declare_test!{StateTests_RandomTests_st201503181416GO, "StateTests/RandomTests/st201503181416GO"}
declare_test!{StateTests_RandomTests_st201503181417CPPJIT, "StateTests/RandomTests/st201503181417CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181417GO, "StateTests/RandomTests/st201503181417GO"}
declare_test!{StateTests_RandomTests_st201503181418CPPJIT, "StateTests/RandomTests/st201503181418CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181422GO, "StateTests/RandomTests/st201503181422GO"}
declare_test!{StateTests_RandomTests_st201503181423CPPJIT, "StateTests/RandomTests/st201503181423CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181424GO, "StateTests/RandomTests/st201503181424GO"}
declare_test!{StateTests_RandomTests_st201503181426CPPJIT, "StateTests/RandomTests/st201503181426CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181426GO, "StateTests/RandomTests/st201503181426GO"}
declare_test!{StateTests_RandomTests_st201503181428GO, "StateTests/RandomTests/st201503181428GO"}
declare_test!{StateTests_RandomTests_st201503181430CPPJIT, "StateTests/RandomTests/st201503181430CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181435GO, "StateTests/RandomTests/st201503181435GO"}
declare_test!{StateTests_RandomTests_st201503181436GO, "StateTests/RandomTests/st201503181436GO"}
declare_test!{StateTests_RandomTests_st201503181437CPPJIT, "StateTests/RandomTests/st201503181437CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181437GO, "StateTests/RandomTests/st201503181437GO"}
declare_test!{StateTests_RandomTests_st201503181438CPPJIT, "StateTests/RandomTests/st201503181438CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181438GO, "StateTests/RandomTests/st201503181438GO"}
declare_test!{StateTests_RandomTests_st201503181439CPPJIT, "StateTests/RandomTests/st201503181439CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181439GO, "StateTests/RandomTests/st201503181439GO"}
declare_test!{StateTests_RandomTests_st201503181439PYTHON, "StateTests/RandomTests/st201503181439PYTHON"}
declare_test!{StateTests_RandomTests_st201503181440GO, "StateTests/RandomTests/st201503181440GO"}
declare_test!{StateTests_RandomTests_st201503181441GO, "StateTests/RandomTests/st201503181441GO"}
declare_test!{StateTests_RandomTests_st201503181442GO, "StateTests/RandomTests/st201503181442GO"}
declare_test!{StateTests_RandomTests_st201503181445CPPJIT, "StateTests/RandomTests/st201503181445CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181446GO, "StateTests/RandomTests/st201503181446GO"}
declare_test!{StateTests_RandomTests_st201503181447GO, "StateTests/RandomTests/st201503181447GO"}
declare_test!{StateTests_RandomTests_st201503181450GO, "StateTests/RandomTests/st201503181450GO"}
declare_test!{StateTests_RandomTests_st201503181451CPPJIT, "StateTests/RandomTests/st201503181451CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181453GO, "StateTests/RandomTests/st201503181453GO"}
declare_test!{StateTests_RandomTests_st201503181455GO, "StateTests/RandomTests/st201503181455GO"}
declare_test!{StateTests_RandomTests_st201503181456CPPJIT, "StateTests/RandomTests/st201503181456CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181457GO, "StateTests/RandomTests/st201503181457GO"}
declare_test!{StateTests_RandomTests_st201503181458GO, "StateTests/RandomTests/st201503181458GO"}
declare_test!{StateTests_RandomTests_st201503181459GO, "StateTests/RandomTests/st201503181459GO"}
declare_test!{StateTests_RandomTests_st201503181500GO, "StateTests/RandomTests/st201503181500GO"}
declare_test!{StateTests_RandomTests_st201503181501GO, "StateTests/RandomTests/st201503181501GO"}
declare_test!{StateTests_RandomTests_st201503181503GO, "StateTests/RandomTests/st201503181503GO"}
declare_test!{StateTests_RandomTests_st201503181504GO, "StateTests/RandomTests/st201503181504GO"}
declare_test!{StateTests_RandomTests_st201503181505GO, "StateTests/RandomTests/st201503181505GO"}
declare_test!{StateTests_RandomTests_st201503181506CPPJIT, "StateTests/RandomTests/st201503181506CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181507GO, "StateTests/RandomTests/st201503181507GO"}
declare_test!{StateTests_RandomTests_st201503181509CPPJIT, "StateTests/RandomTests/st201503181509CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181509GO, "StateTests/RandomTests/st201503181509GO"}
declare_test!{StateTests_RandomTests_st201503181510GO, "StateTests/RandomTests/st201503181510GO"}
declare_test!{StateTests_RandomTests_st201503181511GO, "StateTests/RandomTests/st201503181511GO"}
declare_test!{StateTests_RandomTests_st201503181512GO, "StateTests/RandomTests/st201503181512GO"}
declare_test!{StateTests_RandomTests_st201503181513CPPJIT, "StateTests/RandomTests/st201503181513CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181513GO, "StateTests/RandomTests/st201503181513GO"}
declare_test!{StateTests_RandomTests_st201503181514CPPJIT, "StateTests/RandomTests/st201503181514CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181514GO, "StateTests/RandomTests/st201503181514GO"}
declare_test!{StateTests_RandomTests_st201503181517CPPJIT, "StateTests/RandomTests/st201503181517CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181517GO, "StateTests/RandomTests/st201503181517GO"}
declare_test!{StateTests_RandomTests_st201503181519CPPJIT, "StateTests/RandomTests/st201503181519CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181519GO, "StateTests/RandomTests/st201503181519GO"}
declare_test!{StateTests_RandomTests_st201503181520CPPJIT, "StateTests/RandomTests/st201503181520CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181520GO, "StateTests/RandomTests/st201503181520GO"}
declare_test!{StateTests_RandomTests_st201503181521GO, "StateTests/RandomTests/st201503181521GO"}
declare_test!{StateTests_RandomTests_st201503181522GO, "StateTests/RandomTests/st201503181522GO"}
declare_test!{StateTests_RandomTests_st201503181524CPPJIT, "StateTests/RandomTests/st201503181524CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181524GO, "StateTests/RandomTests/st201503181524GO"}
declare_test!{StateTests_RandomTests_st201503181526GO, "StateTests/RandomTests/st201503181526GO"}
declare_test!{StateTests_RandomTests_st201503181527GO, "StateTests/RandomTests/st201503181527GO"}
declare_test!{StateTests_RandomTests_st201503181528CPPJIT, "StateTests/RandomTests/st201503181528CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181528GO, "StateTests/RandomTests/st201503181528GO"}
declare_test!{StateTests_RandomTests_st201503181528PYTHON, "StateTests/RandomTests/st201503181528PYTHON"}
declare_test!{StateTests_RandomTests_st201503181529GO, "StateTests/RandomTests/st201503181529GO"}
declare_test!{StateTests_RandomTests_st201503181531CPPJIT, "StateTests/RandomTests/st201503181531CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181533GO, "StateTests/RandomTests/st201503181533GO"}
declare_test!{StateTests_RandomTests_st201503181534CPPJIT, "StateTests/RandomTests/st201503181534CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181534GO, "StateTests/RandomTests/st201503181534GO"}
declare_test!{StateTests_RandomTests_st201503181536CPPJIT, "StateTests/RandomTests/st201503181536CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181536GO, "StateTests/RandomTests/st201503181536GO"}
declare_test!{StateTests_RandomTests_st201503181537GO, "StateTests/RandomTests/st201503181537GO"}
declare_test!{StateTests_RandomTests_st201503181538GO, "StateTests/RandomTests/st201503181538GO"}
declare_test!{StateTests_RandomTests_st201503181539GO, "StateTests/RandomTests/st201503181539GO"}
declare_test!{StateTests_RandomTests_st201503181540CPPJIT, "StateTests/RandomTests/st201503181540CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181540PYTHON, "StateTests/RandomTests/st201503181540PYTHON"}
declare_test!{StateTests_RandomTests_st201503181543GO, "StateTests/RandomTests/st201503181543GO"}
declare_test!{StateTests_RandomTests_st201503181544CPPJIT, "StateTests/RandomTests/st201503181544CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181544GO, "StateTests/RandomTests/st201503181544GO"}
declare_test!{StateTests_RandomTests_st201503181547GO, "StateTests/RandomTests/st201503181547GO"}
declare_test!{StateTests_RandomTests_st201503181548CPPJIT, "StateTests/RandomTests/st201503181548CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181548GO, "StateTests/RandomTests/st201503181548GO"}
declare_test!{StateTests_RandomTests_st201503181551GO, "StateTests/RandomTests/st201503181551GO"}
declare_test!{StateTests_RandomTests_st201503181552CPPJIT, "StateTests/RandomTests/st201503181552CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181553GO, "StateTests/RandomTests/st201503181553GO"}
declare_test!{StateTests_RandomTests_st201503181555CPPJIT, "StateTests/RandomTests/st201503181555CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181555GO, "StateTests/RandomTests/st201503181555GO"}
declare_test!{StateTests_RandomTests_st201503181557GO, "StateTests/RandomTests/st201503181557GO"}
declare_test!{StateTests_RandomTests_st201503181559GO, "StateTests/RandomTests/st201503181559GO"}
declare_test!{StateTests_RandomTests_st201503181601CPPJIT, "StateTests/RandomTests/st201503181601CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181601GO, "StateTests/RandomTests/st201503181601GO"}
declare_test!{StateTests_RandomTests_st201503181602GO, "StateTests/RandomTests/st201503181602GO"}
declare_test!{StateTests_RandomTests_st201503181603GO, "StateTests/RandomTests/st201503181603GO"}
declare_test!{StateTests_RandomTests_st201503181604GO, "StateTests/RandomTests/st201503181604GO"}
declare_test!{StateTests_RandomTests_st201503181605GO, "StateTests/RandomTests/st201503181605GO"}
declare_test!{StateTests_RandomTests_st201503181606GO, "StateTests/RandomTests/st201503181606GO"}
declare_test!{StateTests_RandomTests_st201503181607GO, "StateTests/RandomTests/st201503181607GO"}
declare_test!{StateTests_RandomTests_st201503181608CPPJIT, "StateTests/RandomTests/st201503181608CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181608GO, "StateTests/RandomTests/st201503181608GO"}
declare_test!{StateTests_RandomTests_st201503181609GO, "StateTests/RandomTests/st201503181609GO"}
declare_test!{StateTests_RandomTests_st201503181610CPPJIT, "StateTests/RandomTests/st201503181610CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181610GO, "StateTests/RandomTests/st201503181610GO"}
declare_test!{StateTests_RandomTests_st201503181611CPPJIT, "StateTests/RandomTests/st201503181611CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181611GO, "StateTests/RandomTests/st201503181611GO"}
declare_test!{StateTests_RandomTests_st201503181612GO, "StateTests/RandomTests/st201503181612GO"}
declare_test!{StateTests_RandomTests_st201503181614CPPJIT, "StateTests/RandomTests/st201503181614CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181614GO, "StateTests/RandomTests/st201503181614GO"}
declare_test!{StateTests_RandomTests_st201503181616CPPJIT, "StateTests/RandomTests/st201503181616CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181616GO, "StateTests/RandomTests/st201503181616GO"}
declare_test!{StateTests_RandomTests_st201503181617GO, "StateTests/RandomTests/st201503181617GO"}
declare_test!{StateTests_RandomTests_st201503181618GO, "StateTests/RandomTests/st201503181618GO"}
declare_test!{StateTests_RandomTests_st201503181619GO, "StateTests/RandomTests/st201503181619GO"}
declare_test!{StateTests_RandomTests_st201503181620CPPJIT, "StateTests/RandomTests/st201503181620CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181620GO, "StateTests/RandomTests/st201503181620GO"}
declare_test!{StateTests_RandomTests_st201503181621GO, "StateTests/RandomTests/st201503181621GO"}
declare_test!{StateTests_RandomTests_st201503181625GO, "StateTests/RandomTests/st201503181625GO"}
declare_test!{StateTests_RandomTests_st201503181626CPPJIT, "StateTests/RandomTests/st201503181626CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181626GO, "StateTests/RandomTests/st201503181626GO"}
declare_test!{StateTests_RandomTests_st201503181627GO, "StateTests/RandomTests/st201503181627GO"}
declare_test!{StateTests_RandomTests_st201503181628GO, "StateTests/RandomTests/st201503181628GO"}
declare_test!{StateTests_RandomTests_st201503181629GO, "StateTests/RandomTests/st201503181629GO"}
declare_test!{StateTests_RandomTests_st201503181630CPPJIT, "StateTests/RandomTests/st201503181630CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181630GO, "StateTests/RandomTests/st201503181630GO"}
declare_test!{StateTests_RandomTests_st201503181630PYTHON, "StateTests/RandomTests/st201503181630PYTHON"}
declare_test!{StateTests_RandomTests_st201503181632GO, "StateTests/RandomTests/st201503181632GO"}
declare_test!{StateTests_RandomTests_st201503181634CPPJIT, "StateTests/RandomTests/st201503181634CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181635GO, "StateTests/RandomTests/st201503181635GO"}
declare_test!{StateTests_RandomTests_st201503181636GO, "StateTests/RandomTests/st201503181636GO"}
declare_test!{StateTests_RandomTests_st201503181638GO, "StateTests/RandomTests/st201503181638GO"}
declare_test!{StateTests_RandomTests_st201503181639CPPJIT, "StateTests/RandomTests/st201503181639CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181641GO, "StateTests/RandomTests/st201503181641GO"}
declare_test!{StateTests_RandomTests_st201503181645GO, "StateTests/RandomTests/st201503181645GO"}
declare_test!{StateTests_RandomTests_st201503181646GO, "StateTests/RandomTests/st201503181646GO"}
declare_test!{StateTests_RandomTests_st201503181647CPPJIT, "StateTests/RandomTests/st201503181647CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181649CPPJIT, "StateTests/RandomTests/st201503181649CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181650GO, "StateTests/RandomTests/st201503181650GO"}
declare_test!{StateTests_RandomTests_st201503181652CPPJIT, "StateTests/RandomTests/st201503181652CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181653GO, "StateTests/RandomTests/st201503181653GO"}
declare_test!{StateTests_RandomTests_st201503181654GO, "StateTests/RandomTests/st201503181654GO"}
declare_test!{StateTests_RandomTests_st201503181655CPPJIT, "StateTests/RandomTests/st201503181655CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181655GO, "StateTests/RandomTests/st201503181655GO"}
declare_test!{StateTests_RandomTests_st201503181656CPPJIT, "StateTests/RandomTests/st201503181656CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181656GO, "StateTests/RandomTests/st201503181656GO"}
declare_test!{StateTests_RandomTests_st201503181657GO, "StateTests/RandomTests/st201503181657GO"}
declare_test!{StateTests_RandomTests_st201503181658GO, "StateTests/RandomTests/st201503181658GO"}
declare_test!{StateTests_RandomTests_st201503181700GO, "StateTests/RandomTests/st201503181700GO"}
declare_test!{StateTests_RandomTests_st201503181702GO, "StateTests/RandomTests/st201503181702GO"}
declare_test!{StateTests_RandomTests_st201503181703CPPJIT, "StateTests/RandomTests/st201503181703CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181703GO, "StateTests/RandomTests/st201503181703GO"}
declare_test!{StateTests_RandomTests_st201503181704GO, "StateTests/RandomTests/st201503181704GO"}
declare_test!{StateTests_RandomTests_st201503181706GO, "StateTests/RandomTests/st201503181706GO"}
declare_test!{StateTests_RandomTests_st201503181709GO, "StateTests/RandomTests/st201503181709GO"}
declare_test!{StateTests_RandomTests_st201503181711CPPJIT, "StateTests/RandomTests/st201503181711CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181711GO, "StateTests/RandomTests/st201503181711GO"}
declare_test!{StateTests_RandomTests_st201503181713CPPJIT, "StateTests/RandomTests/st201503181713CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181713GO, "StateTests/RandomTests/st201503181713GO"}
declare_test!{StateTests_RandomTests_st201503181714GO, "StateTests/RandomTests/st201503181714GO"}
declare_test!{StateTests_RandomTests_st201503181715CPPJIT, "StateTests/RandomTests/st201503181715CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181715GO, "StateTests/RandomTests/st201503181715GO"}
declare_test!{StateTests_RandomTests_st201503181716GO, "StateTests/RandomTests/st201503181716GO"}
declare_test!{StateTests_RandomTests_st201503181717GO, "StateTests/RandomTests/st201503181717GO"}
declare_test!{StateTests_RandomTests_st201503181720CPPJIT, "StateTests/RandomTests/st201503181720CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181722GO, "StateTests/RandomTests/st201503181722GO"}
declare_test!{StateTests_RandomTests_st201503181723CPPJIT, "StateTests/RandomTests/st201503181723CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181723GO, "StateTests/RandomTests/st201503181723GO"}
declare_test!{StateTests_RandomTests_st201503181724CPPJIT, "StateTests/RandomTests/st201503181724CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181724GO, "StateTests/RandomTests/st201503181724GO"}
declare_test!{StateTests_RandomTests_st201503181725GO, "StateTests/RandomTests/st201503181725GO"}
declare_test!{StateTests_RandomTests_st201503181728GO, "StateTests/RandomTests/st201503181728GO"}
declare_test!{StateTests_RandomTests_st201503181729GO, "StateTests/RandomTests/st201503181729GO"}
declare_test!{StateTests_RandomTests_st201503181730GO, "StateTests/RandomTests/st201503181730GO"}
declare_test!{StateTests_RandomTests_st201503181731CPPJIT, "StateTests/RandomTests/st201503181731CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181732GO, "StateTests/RandomTests/st201503181732GO"}
declare_test!{StateTests_RandomTests_st201503181734CPPJIT, "StateTests/RandomTests/st201503181734CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181734GO, "StateTests/RandomTests/st201503181734GO"}
declare_test!{StateTests_RandomTests_st201503181735GO, "StateTests/RandomTests/st201503181735GO"}
declare_test!{StateTests_RandomTests_st201503181737CPPJIT, "StateTests/RandomTests/st201503181737CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181737GO, "StateTests/RandomTests/st201503181737GO"}
declare_test!{StateTests_RandomTests_st201503181738CPPJIT, "StateTests/RandomTests/st201503181738CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181738GO, "StateTests/RandomTests/st201503181738GO"}
declare_test!{StateTests_RandomTests_st201503181739GO, "StateTests/RandomTests/st201503181739GO"}
declare_test!{StateTests_RandomTests_st201503181740CPPJIT, "StateTests/RandomTests/st201503181740CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181740GO, "StateTests/RandomTests/st201503181740GO"}
declare_test!{StateTests_RandomTests_st201503181742CPPJIT, "StateTests/RandomTests/st201503181742CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181743GO, "StateTests/RandomTests/st201503181743GO"}
declare_test!{StateTests_RandomTests_st201503181744GO, "StateTests/RandomTests/st201503181744GO"}
declare_test!{StateTests_RandomTests_st201503181745CPPJIT, "StateTests/RandomTests/st201503181745CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181746GO, "StateTests/RandomTests/st201503181746GO"}
declare_test!{StateTests_RandomTests_st201503181747GO, "StateTests/RandomTests/st201503181747GO"}
declare_test!{StateTests_RandomTests_st201503181748GO, "StateTests/RandomTests/st201503181748GO"}
declare_test!{StateTests_RandomTests_st201503181749GO, "StateTests/RandomTests/st201503181749GO"}
declare_test!{StateTests_RandomTests_st201503181750CPPJIT, "StateTests/RandomTests/st201503181750CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181750GO, "StateTests/RandomTests/st201503181750GO"}
declare_test!{StateTests_RandomTests_st201503181752GO, "StateTests/RandomTests/st201503181752GO"}
declare_test!{StateTests_RandomTests_st201503181753CPPJIT, "StateTests/RandomTests/st201503181753CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181754CPPJIT, "StateTests/RandomTests/st201503181754CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181754GO, "StateTests/RandomTests/st201503181754GO"}
declare_test!{StateTests_RandomTests_st201503181755CPPJIT, "StateTests/RandomTests/st201503181755CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181755GO, "StateTests/RandomTests/st201503181755GO"}
declare_test!{StateTests_RandomTests_st201503181756GO, "StateTests/RandomTests/st201503181756GO"}
declare_test!{StateTests_RandomTests_st201503181757CPPJIT, "StateTests/RandomTests/st201503181757CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181757GO, "StateTests/RandomTests/st201503181757GO"}
declare_test!{StateTests_RandomTests_st201503181759GO, "StateTests/RandomTests/st201503181759GO"}
declare_test!{StateTests_RandomTests_st201503181800GO, "StateTests/RandomTests/st201503181800GO"}
declare_test!{StateTests_RandomTests_st201503181801GO, "StateTests/RandomTests/st201503181801GO"}
declare_test!{StateTests_RandomTests_st201503181802GO, "StateTests/RandomTests/st201503181802GO"}
declare_test!{StateTests_RandomTests_st201503181803CPPJIT, "StateTests/RandomTests/st201503181803CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181803GO, "StateTests/RandomTests/st201503181803GO"}
declare_test!{StateTests_RandomTests_st201503181804GO, "StateTests/RandomTests/st201503181804GO"}
declare_test!{StateTests_RandomTests_st201503181806GO, "StateTests/RandomTests/st201503181806GO"}
declare_test!{StateTests_RandomTests_st201503181808GO, "StateTests/RandomTests/st201503181808GO"}
declare_test!{StateTests_RandomTests_st201503181809CPPJIT, "StateTests/RandomTests/st201503181809CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181812CPPJIT, "StateTests/RandomTests/st201503181812CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181812GO, "StateTests/RandomTests/st201503181812GO"}
declare_test!{StateTests_RandomTests_st201503181814CPPJIT, "StateTests/RandomTests/st201503181814CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181815GO, "StateTests/RandomTests/st201503181815GO"}
declare_test!{StateTests_RandomTests_st201503181816CPPJIT, "StateTests/RandomTests/st201503181816CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181817CPPJIT, "StateTests/RandomTests/st201503181817CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181819GO, "StateTests/RandomTests/st201503181819GO"}
declare_test!{StateTests_RandomTests_st201503181821GO, "StateTests/RandomTests/st201503181821GO"}
declare_test!{StateTests_RandomTests_st201503181822GO, "StateTests/RandomTests/st201503181822GO"}
declare_test!{StateTests_RandomTests_st201503181823GO, "StateTests/RandomTests/st201503181823GO"}
declare_test!{StateTests_RandomTests_st201503181824GO, "StateTests/RandomTests/st201503181824GO"}
declare_test!{StateTests_RandomTests_st201503181825GO, "StateTests/RandomTests/st201503181825GO"}
declare_test!{StateTests_RandomTests_st201503181829GO, "StateTests/RandomTests/st201503181829GO"}
declare_test!{StateTests_RandomTests_st201503181830CPPJIT, "StateTests/RandomTests/st201503181830CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181833GO, "StateTests/RandomTests/st201503181833GO"}
declare_test!{StateTests_RandomTests_st201503181834CPPJIT, "StateTests/RandomTests/st201503181834CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181834GO, "StateTests/RandomTests/st201503181834GO"}
declare_test!{StateTests_RandomTests_st201503181837GO, "StateTests/RandomTests/st201503181837GO"}
declare_test!{StateTests_RandomTests_st201503181840GO, "StateTests/RandomTests/st201503181840GO"}
declare_test!{StateTests_RandomTests_st201503181842GO, "StateTests/RandomTests/st201503181842GO"}
declare_test!{StateTests_RandomTests_st201503181843GO, "StateTests/RandomTests/st201503181843GO"}
declare_test!{StateTests_RandomTests_st201503181844GO, "StateTests/RandomTests/st201503181844GO"}
declare_test!{StateTests_RandomTests_st201503181845GO, "StateTests/RandomTests/st201503181845GO"}
declare_test!{StateTests_RandomTests_st201503181846GO, "StateTests/RandomTests/st201503181846GO"}
declare_test!{StateTests_RandomTests_st201503181847GO, "StateTests/RandomTests/st201503181847GO"}
declare_test!{StateTests_RandomTests_st201503181848GO, "StateTests/RandomTests/st201503181848GO"}
declare_test!{StateTests_RandomTests_st201503181849GO, "StateTests/RandomTests/st201503181849GO"}
declare_test!{StateTests_RandomTests_st201503181850GO, "StateTests/RandomTests/st201503181850GO"}
declare_test!{StateTests_RandomTests_st201503181851CPPJIT, "StateTests/RandomTests/st201503181851CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181851GO, "StateTests/RandomTests/st201503181851GO"}
declare_test!{StateTests_RandomTests_st201503181852CPPJIT, "StateTests/RandomTests/st201503181852CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181854GO, "StateTests/RandomTests/st201503181854GO"}
declare_test!{StateTests_RandomTests_st201503181855CPPJIT, "StateTests/RandomTests/st201503181855CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181857PYTHON, "StateTests/RandomTests/st201503181857PYTHON"}
declare_test!{StateTests_RandomTests_st201503181859GO, "StateTests/RandomTests/st201503181859GO"}
declare_test!{StateTests_RandomTests_st201503181900GO, "StateTests/RandomTests/st201503181900GO"}
declare_test!{StateTests_RandomTests_st201503181903GO, "StateTests/RandomTests/st201503181903GO"}
declare_test!{StateTests_RandomTests_st201503181904GO, "StateTests/RandomTests/st201503181904GO"}
declare_test!{StateTests_RandomTests_st201503181906GO, "StateTests/RandomTests/st201503181906GO"}
declare_test!{StateTests_RandomTests_st201503181907GO, "StateTests/RandomTests/st201503181907GO"}
declare_test!{StateTests_RandomTests_st201503181910GO, "StateTests/RandomTests/st201503181910GO"}
declare_test!{StateTests_RandomTests_st201503181915GO, "StateTests/RandomTests/st201503181915GO"}
declare_test!{StateTests_RandomTests_st201503181919CPPJIT, "StateTests/RandomTests/st201503181919CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181919PYTHON, "StateTests/RandomTests/st201503181919PYTHON"}
declare_test!{StateTests_RandomTests_st201503181920GO, "StateTests/RandomTests/st201503181920GO"}
declare_test!{StateTests_RandomTests_st201503181922GO, "StateTests/RandomTests/st201503181922GO"}
declare_test!{StateTests_RandomTests_st201503181926GO, "StateTests/RandomTests/st201503181926GO"}
declare_test!{StateTests_RandomTests_st201503181929GO, "StateTests/RandomTests/st201503181929GO"}
declare_test!{StateTests_RandomTests_st201503181931CPPJIT, "StateTests/RandomTests/st201503181931CPPJIT"}
declare_test!{StateTests_RandomTests_st201503181931GO, "StateTests/RandomTests/st201503181931GO"}
declare_test!{StateTests_RandomTests_st201503181931PYTHON, "StateTests/RandomTests/st201503181931PYTHON"}
//declare_test!{StateTests_RandomTests_st201503191646GO, "StateTests/RandomTests/st201503191646GO"}
declare_test!{StateTests_RandomTests_st201503200837JS, "StateTests/RandomTests/st201503200837JS"}
declare_test!{StateTests_RandomTests_st201503200838JS, "StateTests/RandomTests/st201503200838JS"}
declare_test!{StateTests_RandomTests_st201503200841JS, "StateTests/RandomTests/st201503200841JS"}
declare_test!{StateTests_RandomTests_st201503200848JS, "StateTests/RandomTests/st201503200848JS"}
declare_test!{StateTests_RandomTests_st201503240609JS, "StateTests/RandomTests/st201503240609JS"}
declare_test!{StateTests_RandomTests_st201503302200JS, "StateTests/RandomTests/st201503302200JS"}
declare_test!{StateTests_RandomTests_st201503302202JS, "StateTests/RandomTests/st201503302202JS"}
declare_test!{StateTests_RandomTests_st201503302206JS, "StateTests/RandomTests/st201503302206JS"}
declare_test!{StateTests_RandomTests_st201503302208JS, "StateTests/RandomTests/st201503302208JS"}
declare_test!{StateTests_RandomTests_st201503302210JS, "StateTests/RandomTests/st201503302210JS"}
declare_test!{StateTests_RandomTests_st201503302211JS, "StateTests/RandomTests/st201503302211JS"}
declare_test!{StateTests_RandomTests_st201504011535GO, "StateTests/RandomTests/st201504011535GO"}
declare_test!{StateTests_RandomTests_st201504011536GO, "StateTests/RandomTests/st201504011536GO"}
declare_test!{StateTests_RandomTests_st201504011547GO, "StateTests/RandomTests/st201504011547GO"}
declare_test!{StateTests_RandomTests_st201504011916JS, "StateTests/RandomTests/st201504011916JS"}
declare_test!{StateTests_RandomTests_st201504012130JS, "StateTests/RandomTests/st201504012130JS"}
declare_test!{StateTests_RandomTests_st201504012259JS, "StateTests/RandomTests/st201504012259JS"}
declare_test!{StateTests_RandomTests_st201504012359JS, "StateTests/RandomTests/st201504012359JS"}
declare_test!{StateTests_RandomTests_st201504020305JS, "StateTests/RandomTests/st201504020305JS"}
declare_test!{StateTests_RandomTests_st201504020400JS, "StateTests/RandomTests/st201504020400JS"}
declare_test!{StateTests_RandomTests_st201504020428JS, "StateTests/RandomTests/st201504020428JS"}
declare_test!{StateTests_RandomTests_st201504020431JS, "StateTests/RandomTests/st201504020431JS"}
declare_test!{StateTests_RandomTests_st201504020444JS, "StateTests/RandomTests/st201504020444JS"}
declare_test!{StateTests_RandomTests_st201504020538JS, "StateTests/RandomTests/st201504020538JS"}
declare_test!{StateTests_RandomTests_st201504020639JS, "StateTests/RandomTests/st201504020639JS"}
declare_test!{StateTests_RandomTests_st201504020836JS, "StateTests/RandomTests/st201504020836JS"}
declare_test!{StateTests_RandomTests_st201504020910JS, "StateTests/RandomTests/st201504020910JS"}
declare_test!{StateTests_RandomTests_st201504021057JS, "StateTests/RandomTests/st201504021057JS"}
declare_test!{StateTests_RandomTests_st201504021104JS, "StateTests/RandomTests/st201504021104JS"}
declare_test!{StateTests_RandomTests_st201504021237CPPJIT, "StateTests/RandomTests/st201504021237CPPJIT"}
declare_test!{StateTests_RandomTests_st201504021237GO, "StateTests/RandomTests/st201504021237GO"}
declare_test!{StateTests_RandomTests_st201504021237JS, "StateTests/RandomTests/st201504021237JS"}
declare_test!{StateTests_RandomTests_st201504021237PYTHON, "StateTests/RandomTests/st201504021237PYTHON"}
declare_test!{StateTests_RandomTests_st201504021949JS, "StateTests/RandomTests/st201504021949JS"}
declare_test!{StateTests_RandomTests_st201504022003CPPJIT, "StateTests/RandomTests/st201504022003CPPJIT"}
declare_test!{StateTests_RandomTests_st201504022124JS, "StateTests/RandomTests/st201504022124JS"}
declare_test!{StateTests_RandomTests_st201504030138JS, "StateTests/RandomTests/st201504030138JS"}
declare_test!{StateTests_RandomTests_st201504030646JS, "StateTests/RandomTests/st201504030646JS"}
declare_test!{StateTests_RandomTests_st201504030709JS, "StateTests/RandomTests/st201504030709JS"}
declare_test!{StateTests_RandomTests_st201504031133JS, "StateTests/RandomTests/st201504031133JS"}
declare_test!{StateTests_RandomTests_st201504031446JS, "StateTests/RandomTests/st201504031446JS"}
declare_test!{StateTests_RandomTests_st201504031841JS, "StateTests/RandomTests/st201504031841JS"}
declare_test!{StateTests_RandomTests_st201504041605JS, "StateTests/RandomTests/st201504041605JS"}
declare_test!{StateTests_RandomTests_st201504042052JS, "StateTests/RandomTests/st201504042052JS"}
declare_test!{StateTests_RandomTests_st201504042226CPPJIT, "StateTests/RandomTests/st201504042226CPPJIT"}
declare_test!{StateTests_RandomTests_st201504042355CPPJIT, "StateTests/RandomTests/st201504042355CPPJIT"}
declare_test!{StateTests_RandomTests_st201504050059JS, "StateTests/RandomTests/st201504050059JS"}
declare_test!{StateTests_RandomTests_st201504050733JS, "StateTests/RandomTests/st201504050733JS"}
declare_test!{StateTests_RandomTests_st201504051540JS, "StateTests/RandomTests/st201504051540JS"}
declare_test!{StateTests_RandomTests_st201504051944CPPJIT, "StateTests/RandomTests/st201504051944CPPJIT"}
declare_test!{StateTests_RandomTests_st201504052008CPPJIT, "StateTests/RandomTests/st201504052008CPPJIT"}
declare_test!{StateTests_RandomTests_st201504052014GO, "StateTests/RandomTests/st201504052014GO"}
declare_test!{StateTests_RandomTests_st201504052031CPPJIT, "StateTests/RandomTests/st201504052031CPPJIT"}
declare_test!{StateTests_RandomTests_st201504060057CPPJIT, "StateTests/RandomTests/st201504060057CPPJIT"}
declare_test!{StateTests_RandomTests_st201504060418CPPJIT, "StateTests/RandomTests/st201504060418CPPJIT"}
declare_test!{StateTests_RandomTests_st201504061106CPPJIT, "StateTests/RandomTests/st201504061106CPPJIT"}
declare_test!{StateTests_RandomTests_st201504061134CPPJIT, "StateTests/RandomTests/st201504061134CPPJIT"}
declare_test!{StateTests_RandomTests_st201504062033CPPJIT, "StateTests/RandomTests/st201504062033CPPJIT"}
declare_test!{StateTests_RandomTests_st201504062046CPPJIT, "StateTests/RandomTests/st201504062046CPPJIT"}
declare_test!{StateTests_RandomTests_st201504062314CPPJIT, "StateTests/RandomTests/st201504062314CPPJIT"}
declare_test!{StateTests_RandomTests_st201504070746JS, "StateTests/RandomTests/st201504070746JS"}
declare_test!{StateTests_RandomTests_st201504070816CPPJIT, "StateTests/RandomTests/st201504070816CPPJIT"}
declare_test!{StateTests_RandomTests_st201504070836CPPJIT, "StateTests/RandomTests/st201504070836CPPJIT"}
declare_test!{StateTests_RandomTests_st201504070839CPPJIT, "StateTests/RandomTests/st201504070839CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071041CPPJIT, "StateTests/RandomTests/st201504071041CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071056CPPJIT, "StateTests/RandomTests/st201504071056CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071621CPPJIT, "StateTests/RandomTests/st201504071621CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071653CPPJIT, "StateTests/RandomTests/st201504071653CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071750CPPJIT, "StateTests/RandomTests/st201504071750CPPJIT"}
declare_test!{StateTests_RandomTests_st201504071905CPPJIT, "StateTests/RandomTests/st201504071905CPPJIT"}
declare_test!{StateTests_RandomTests_st201504080454CPPJIT, "StateTests/RandomTests/st201504080454CPPJIT"}
declare_test!{StateTests_RandomTests_st201504080457CPPJIT, "StateTests/RandomTests/st201504080457CPPJIT"}
declare_test!{StateTests_RandomTests_st201504080650CPPJIT, "StateTests/RandomTests/st201504080650CPPJIT"}
declare_test!{StateTests_RandomTests_st201504080840CPPJIT, "StateTests/RandomTests/st201504080840CPPJIT"}
declare_test!{StateTests_RandomTests_st201504080948CPPJIT, "StateTests/RandomTests/st201504080948CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081100CPPJIT, "StateTests/RandomTests/st201504081100CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081134CPPJIT, "StateTests/RandomTests/st201504081134CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081138CPPJIT, "StateTests/RandomTests/st201504081138CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081611CPPJIT, "StateTests/RandomTests/st201504081611CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081841JAVA, "StateTests/RandomTests/st201504081841JAVA"}
declare_test!{StateTests_RandomTests_st201504081842JAVA, "StateTests/RandomTests/st201504081842JAVA"}
declare_test!{StateTests_RandomTests_st201504081843JAVA, "StateTests/RandomTests/st201504081843JAVA"}
declare_test!{StateTests_RandomTests_st201504081928CPPJIT, "StateTests/RandomTests/st201504081928CPPJIT"}
declare_test!{StateTests_RandomTests_st201504081953JAVA, "StateTests/RandomTests/st201504081953JAVA"}
declare_test!{StateTests_RandomTests_st201504081954JAVA, "StateTests/RandomTests/st201504081954JAVA"}
declare_test!{StateTests_RandomTests_st201504081955JAVA, "StateTests/RandomTests/st201504081955JAVA"}
declare_test!{StateTests_RandomTests_st201504081956JAVA, "StateTests/RandomTests/st201504081956JAVA"}
declare_test!{StateTests_RandomTests_st201504081957JAVA, "StateTests/RandomTests/st201504081957JAVA"}
declare_test!{StateTests_RandomTests_st201504082000JAVA, "StateTests/RandomTests/st201504082000JAVA"}
declare_test!{StateTests_RandomTests_st201504082001JAVA, "StateTests/RandomTests/st201504082001JAVA"}
declare_test!{StateTests_RandomTests_st201504082002JAVA, "StateTests/RandomTests/st201504082002JAVA"}
declare_test!{StateTests_RandomTests_st201504090553CPPJIT, "StateTests/RandomTests/st201504090553CPPJIT"}
declare_test!{StateTests_RandomTests_st201504090657CPPJIT, "StateTests/RandomTests/st201504090657CPPJIT"}
declare_test!{StateTests_RandomTests_st201504091403CPPJIT, "StateTests/RandomTests/st201504091403CPPJIT"}
declare_test!{StateTests_RandomTests_st201504091641CPPJIT, "StateTests/RandomTests/st201504091641CPPJIT"}
declare_test!{StateTests_RandomTests_st201504092303CPPJIT, "StateTests/RandomTests/st201504092303CPPJIT"}
declare_test!{StateTests_RandomTests_st201504100125CPPJIT, "StateTests/RandomTests/st201504100125CPPJIT"}
declare_test!{StateTests_RandomTests_st201504100215CPPJIT, "StateTests/RandomTests/st201504100215CPPJIT"}
declare_test!{StateTests_RandomTests_st201504100226PYTHON, "StateTests/RandomTests/st201504100226PYTHON"}
declare_test!{StateTests_RandomTests_st201504100308CPPJIT, "StateTests/RandomTests/st201504100308CPPJIT"}
declare_test!{StateTests_RandomTests_st201504100337CPPJIT, "StateTests/RandomTests/st201504100337CPPJIT"}
declare_test!{StateTests_RandomTests_st201504100341CPPJIT, "StateTests/RandomTests/st201504100341CPPJIT"}
declare_test!{StateTests_RandomTests_st201504101009CPPJIT, "StateTests/RandomTests/st201504101009CPPJIT"}
declare_test!{StateTests_RandomTests_st201504101150CPPJIT, "StateTests/RandomTests/st201504101150CPPJIT"}
declare_test!{StateTests_RandomTests_st201504101223CPPJIT, "StateTests/RandomTests/st201504101223CPPJIT"}
declare_test!{StateTests_RandomTests_st201504101338CPPJIT, "StateTests/RandomTests/st201504101338CPPJIT"}
declare_test!{StateTests_RandomTests_st201504101754PYTHON, "StateTests/RandomTests/st201504101754PYTHON"}
declare_test!{StateTests_RandomTests_st201504111554CPPJIT, "StateTests/RandomTests/st201504111554CPPJIT"}
declare_test!{StateTests_RandomTests_st201504130653JS, "StateTests/RandomTests/st201504130653JS"}
declare_test!{StateTests_RandomTests_st201504131821CPPJIT, "StateTests/RandomTests/st201504131821CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140229CPPJIT, "StateTests/RandomTests/st201504140229CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140236CPPJIT, "StateTests/RandomTests/st201504140236CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140359CPPJIT, "StateTests/RandomTests/st201504140359CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140750CPPJIT, "StateTests/RandomTests/st201504140750CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140818CPPJIT, "StateTests/RandomTests/st201504140818CPPJIT"}
declare_test!{StateTests_RandomTests_st201504140900CPPJIT, "StateTests/RandomTests/st201504140900CPPJIT"}
declare_test!{StateTests_RandomTests_st201504150854CPPJIT, "StateTests/RandomTests/st201504150854CPPJIT"}
declare_test!{StateTests_RandomTests_st201504151057CPPJIT, "StateTests/RandomTests/st201504151057CPPJIT"}
declare_test!{StateTests_RandomTests_st201504202124CPPJIT, "StateTests/RandomTests/st201504202124CPPJIT"}
declare_test!{StateTests_RandomTests_st201504210245CPPJIT, "StateTests/RandomTests/st201504210245CPPJIT"}
declare_test!{StateTests_RandomTests_st201504210957CPPJIT, "StateTests/RandomTests/st201504210957CPPJIT"}
declare_test!{StateTests_RandomTests_st201504211739CPPJIT, "StateTests/RandomTests/st201504211739CPPJIT"}
declare_test!{StateTests_RandomTests_st201504212038CPPJIT, "StateTests/RandomTests/st201504212038CPPJIT"}
declare_test!{StateTests_RandomTests_st201504230729CPPJIT, "StateTests/RandomTests/st201504230729CPPJIT"}
declare_test!{StateTests_RandomTests_st201504231639CPPJIT, "StateTests/RandomTests/st201504231639CPPJIT"}
declare_test!{StateTests_RandomTests_st201504231710CPPJIT, "StateTests/RandomTests/st201504231710CPPJIT"}
declare_test!{StateTests_RandomTests_st201504231742CPPJIT, "StateTests/RandomTests/st201504231742CPPJIT"}
declare_test!{StateTests_RandomTests_st201504232350CPPJIT, "StateTests/RandomTests/st201504232350CPPJIT"}
declare_test!{StateTests_RandomTests_st201504240140CPPJIT, "StateTests/RandomTests/st201504240140CPPJIT"}
declare_test!{StateTests_RandomTests_st201504240220CPPJIT, "StateTests/RandomTests/st201504240220CPPJIT"}
declare_test!{StateTests_RandomTests_st201504240351CPPJIT, "StateTests/RandomTests/st201504240351CPPJIT"}
declare_test!{StateTests_RandomTests_st201504240817CPPJIT, "StateTests/RandomTests/st201504240817CPPJIT"}
declare_test!{StateTests_RandomTests_st201504241118CPPJIT, "StateTests/RandomTests/st201504241118CPPJIT"}
declare_test!{StateTests_RandomTests_st201505021810CPPJIT, "StateTests/RandomTests/st201505021810CPPJIT"}
declare_test!{StateTests_RandomTests_st201505050557JS, "StateTests/RandomTests/st201505050557JS"}
declare_test!{StateTests_RandomTests_st201505050929GO, "StateTests/RandomTests/st201505050929GO"}
declare_test!{StateTests_RandomTests_st201505050942PYTHON, "StateTests/RandomTests/st201505050942PYTHON"}
declare_test!{StateTests_RandomTests_st201505051004PYTHON, "StateTests/RandomTests/st201505051004PYTHON"}
declare_test!{StateTests_RandomTests_st201505051016PYTHON, "StateTests/RandomTests/st201505051016PYTHON"}
declare_test!{StateTests_RandomTests_st201505051114GO, "StateTests/RandomTests/st201505051114GO"}
declare_test!{StateTests_RandomTests_st201505051238GO, "StateTests/RandomTests/st201505051238GO"}
declare_test!{StateTests_RandomTests_st201505051249GO, "StateTests/RandomTests/st201505051249GO"}
declare_test!{StateTests_RandomTests_st201505051558PYTHON, "StateTests/RandomTests/st201505051558PYTHON"}
declare_test!{StateTests_RandomTests_st201505051611PYTHON, "StateTests/RandomTests/st201505051611PYTHON"}
declare_test!{StateTests_RandomTests_st201505051648JS, "StateTests/RandomTests/st201505051648JS"}
declare_test!{StateTests_RandomTests_st201505051710GO, "StateTests/RandomTests/st201505051710GO"}
declare_test!{StateTests_RandomTests_st201505052013GO, "StateTests/RandomTests/st201505052013GO"}
declare_test!{StateTests_RandomTests_st201505052102JS, "StateTests/RandomTests/st201505052102JS"}
declare_test!{StateTests_RandomTests_st201505052235GO, "StateTests/RandomTests/st201505052235GO"}
declare_test!{StateTests_RandomTests_st201505052238JS, "StateTests/RandomTests/st201505052238JS"}
declare_test!{StateTests_RandomTests_st201505052242PYTHON, "StateTests/RandomTests/st201505052242PYTHON"}
declare_test!{StateTests_RandomTests_st201505052343PYTHON, "StateTests/RandomTests/st201505052343PYTHON"}
declare_test!{StateTests_RandomTests_st201505060120GO, "StateTests/RandomTests/st201505060120GO"}
declare_test!{StateTests_RandomTests_st201505060121GO, "StateTests/RandomTests/st201505060121GO"}
declare_test!{StateTests_RandomTests_st201505060136PYTHON, "StateTests/RandomTests/st201505060136PYTHON"}
declare_test!{StateTests_RandomTests_st201505060646JS, "StateTests/RandomTests/st201505060646JS"}
declare_test!{StateTests_RandomTests_st201505252314CPPJIT, "StateTests/RandomTests/st201505252314CPPJIT"}
declare_test!{StateTests_RandomTests_st201505272131CPPJIT, "StateTests/RandomTests/st201505272131CPPJIT"}
declare_test!{StateTests_RandomTests_st201506040034GO, "StateTests/RandomTests/st201506040034GO"}
declare_test!{StateTests_RandomTests_st201506040157GO, "StateTests/RandomTests/st201506040157GO"}
declare_test!{StateTests_RandomTests_st201506052130GO, "StateTests/RandomTests/st201506052130GO"}
declare_test!{StateTests_RandomTests_st201506060929GO, "StateTests/RandomTests/st201506060929GO"}
declare_test!{StateTests_RandomTests_st201506061255GO, "StateTests/RandomTests/st201506061255GO"}
declare_test!{StateTests_RandomTests_st201506062331GO, "StateTests/RandomTests/st201506062331GO"}
declare_test!{StateTests_RandomTests_st201506070548GO, "StateTests/RandomTests/st201506070548GO"}
declare_test!{StateTests_RandomTests_st201506071050GO, "StateTests/RandomTests/st201506071050GO"}
declare_test!{StateTests_RandomTests_st201506071624GO, "StateTests/RandomTests/st201506071624GO"}
declare_test!{StateTests_RandomTests_st201506071819GO, "StateTests/RandomTests/st201506071819GO"}
declare_test!{StateTests_RandomTests_st201506072007GO, "StateTests/RandomTests/st201506072007GO"}
declare_test!{StateTests_RandomTests_st201506080556GO, "StateTests/RandomTests/st201506080556GO"}
declare_test!{StateTests_RandomTests_st201506080721GO, "StateTests/RandomTests/st201506080721GO"}
declare_test!{StateTests_RandomTests_st201506091836GO, "StateTests/RandomTests/st201506091836GO"}
declare_test!{StateTests_RandomTests_st201506092032GO, "StateTests/RandomTests/st201506092032GO"}
declare_test!{StateTests_RandomTests_st201506101359JS, "StateTests/RandomTests/st201506101359JS"}
declare_test!{StateTests_RandomTests_st201507030359GO, "StateTests/RandomTests/st201507030359GO"}

View File

@@ -0,0 +1,34 @@
pub use common::*;
macro_rules! test {
($name: expr) => {
assert!(do_json_test(include_bytes!(concat!("../../res/ethereum/tests/", $name, ".json"))).is_empty());
}
}
#[macro_export]
macro_rules! declare_test {
(ignore => $id: ident, $name: expr) => {
#[ignore]
#[test]
#[allow(non_snake_case)]
fn $id() {
test!($name);
}
};
(heavy => $id: ident, $name: expr) => {
#[cfg(feature = "test-heavy")]
#[test]
#[allow(non_snake_case)]
fn $id() {
test!($name);
}
};
($id: ident, $name: expr) => {
#[test]
#[allow(non_snake_case)]
fn $id() {
test!($name);
}
}
}

View File

@@ -0,0 +1,78 @@
use super::test_common::*;
use evm;
fn do_json_test(json_data: &[u8]) -> Vec<String> {
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
let old_schedule = evm::Schedule::new_frontier();
let new_schedule = evm::Schedule::new_homestead();
let ot = RefCell::new(Transaction::new());
for (name, test) in json.as_object().unwrap() {
let mut fail = false;
let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.clone()); println!("Transaction: {:?}", ot.borrow()); fail = true };
let schedule = match test.find("blocknumber")
.and_then(|j| j.as_string())
.and_then(|s| BlockNumber::from_str(s).ok())
.unwrap_or(0) { x if x < 1_000_000 => &old_schedule, _ => &new_schedule };
let rlp = Bytes::from_json(&test["rlp"]);
let res = UntrustedRlp::new(&rlp).as_val().map_err(From::from).and_then(|t: Transaction| t.validate(schedule, schedule.have_delegate_call));
fail_unless(test.find("transaction").is_none() == res.is_err());
if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) {
let t = res.unwrap();
fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender)));
fail_unless(t.data == Bytes::from_json(&tx["data"]));
fail_unless(t.gas == xjson!(&tx["gasLimit"]));
fail_unless(t.gas_price == xjson!(&tx["gasPrice"]));
fail_unless(t.nonce == xjson!(&tx["nonce"]));
fail_unless(t.value == xjson!(&tx["value"]));
if let Action::Call(ref to) = t.action {
*ot.borrow_mut() = t.clone();
fail_unless(to == &xjson!(&tx["to"]));
} else {
*ot.borrow_mut() = t.clone();
fail_unless(Bytes::from_json(&tx["to"]).is_empty());
}
}
}
for f in &failed {
println!("FAILED: {:?}", f);
}
failed
}
// Once we have interpolate idents.
/*macro_rules! declare_test {
($test_set_name: ident / $name: ident) => {
#[test]
#[allow(non_snake_case)]
fn $name() {
assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($name), ".json"))).len() == 0);
}
};
($test_set_name: ident / $prename: ident / $name: ident) => {
#[test]
#[allow(non_snake_case)]
interpolate_idents! { fn [$prename _ $name]()
{
let json = include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($prename), "/", stringify!($name), ".json"));
assert!(do_json_test(json).len() == 0);
}
}
};
}
declare_test!{TransactionTests/ttTransactionTest}
declare_test!{TransactionTests/tt10mbDataField}
declare_test!{TransactionTests/ttWrongRLPTransaction}
declare_test!{TransactionTests/Homestead/ttTransactionTest}
declare_test!{heavy => TransactionTests/Homestead/tt10mbDataField}
declare_test!{TransactionTests/Homestead/ttWrongRLPTransaction}
declare_test!{TransactionTests/RandomTests/tr201506052141PYTHON}*/
declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"}
declare_test!{heavy => TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"}
declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"}
declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Homestead/ttTransactionTest"}
declare_test!{heavy => TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"}
declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"}
declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"}

309
ethcore/src/transaction.rs Normal file
View File

@@ -0,0 +1,309 @@
use util::*;
use basic_types::*;
use error::*;
use evm::Schedule;
#[derive(Debug, Clone)]
/// TODO [Gav Wood] Please document me
pub enum Action {
/// TODO [Gav Wood] Please document me
Create,
/// TODO [debris] Please document me
Call(Address),
}
impl Default for Action {
fn default() -> Action { Action::Create }
}
/// A set of information describing an externally-originating message call
/// or contract creation operation.
#[derive(Default, Debug, Clone)]
pub struct Transaction {
/// TODO [debris] Please document me
pub nonce: U256,
/// TODO [debris] Please document me
pub gas_price: U256,
/// TODO [debris] Please document me
pub gas: U256,
/// TODO [debris] Please document me
pub action: Action,
/// TODO [debris] Please document me
pub value: U256,
/// TODO [Gav Wood] Please document me
pub data: Bytes,
// signature
/// TODO [Gav Wood] Please document me
pub v: u8,
/// TODO [Gav Wood] Please document me
pub r: U256,
/// TODO [debris] Please document me
pub s: U256,
hash: RefCell<Option<H256>>,
sender: RefCell<Option<Address>>,
}
impl Transaction {
/// TODO [Gav Wood] Please document me
pub fn new() -> Self {
Transaction {
nonce: x!(0),
gas_price: x!(0),
gas: x!(0),
action: Action::Create,
value: x!(0),
data: vec![],
v: 0,
r: x!(0),
s: x!(0),
hash: RefCell::new(None),
sender: RefCell::new(None),
}
}
/// Create a new message-call transaction.
pub fn new_call(to: Address, value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
Transaction {
nonce: nonce,
gas_price: gas_price,
gas: gas,
action: Action::Call(to),
value: value,
data: data,
v: 0,
r: x!(0),
s: x!(0),
hash: RefCell::new(None),
sender: RefCell::new(None),
}
}
/// Create a new contract-creation transaction.
pub fn new_create(value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
Transaction {
nonce: nonce,
gas_price: gas_price,
gas: gas,
action: Action::Create,
value: value,
data: data,
v: 0,
r: x!(0),
s: x!(0),
hash: RefCell::new(None),
sender: RefCell::new(None),
}
}
/// Get the nonce of the transaction.
pub fn nonce(&self) -> &U256 { &self.nonce }
/// Get the gas price of the transaction.
pub fn gas_price(&self) -> &U256 { &self.gas_price }
/// Get the gas of the transaction.
pub fn gas(&self) -> &U256 { &self.gas }
/// Get the action of the transaction (Create or Call).
pub fn action(&self) -> &Action { &self.action }
/// Get the value of the transaction.
pub fn value(&self) -> &U256 { &self.value }
/// Get the data of the transaction.
pub fn data(&self) -> &Bytes { &self.data }
/// Append object into RLP stream, optionally with or without the signature.
pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) {
s.begin_list(6 + match with_seal { Seal::With => 3, _ => 0 });
s.append(&self.nonce);
s.append(&self.gas_price);
s.append(&self.gas);
match self.action {
Action::Create => s.append_empty_data(),
Action::Call(ref to) => s.append(to),
};
s.append(&self.value);
s.append(&self.data);
if let Seal::With = with_seal {
s.append(&(self.v as u16)).append(&self.r).append(&self.s);
}
}
/// Get the RLP serialisation of the object, optionally with or without the signature.
pub fn rlp_bytes_opt(&self, with_seal: Seal) -> Bytes {
let mut s = RlpStream::new();
self.rlp_append_opt(&mut s, with_seal);
s.out()
}
}
impl FromJson for Transaction {
fn from_json(json: &Json) -> Transaction {
let mut r = Transaction {
nonce: xjson!(&json["nonce"]),
gas_price: xjson!(&json["gasPrice"]),
gas: xjson!(&json["gasLimit"]),
action: match Bytes::from_json(&json["to"]) {
ref x if x.is_empty() => Action::Create,
ref x => Action::Call(Address::from_slice(x)),
},
value: xjson!(&json["value"]),
data: xjson!(&json["data"]),
v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 },
r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) },
s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) },
hash: RefCell::new(None),
sender: match json.find("sender") {
Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))),
_ => RefCell::new(None),
},
};
if let Some(&Json::String(ref secret_key)) = json.find("secretKey") {
r.sign(&h256_from_hex(clean(secret_key)));
}
r
}
}
impl Encodable for Transaction {
fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_opt(s, Seal::With) }
}
impl Transaction {
/// Get the hash of this header (sha3 of the RLP).
pub fn hash(&self) -> H256 {
let mut hash = self.hash.borrow_mut();
match &mut *hash {
&mut Some(ref h) => h.clone(),
hash @ &mut None => {
*hash = Some(self.rlp_sha3());
hash.as_ref().unwrap().clone()
}
}
}
/// Note that some fields have changed. Resets the memoised hash.
pub fn note_dirty(&self) {
*self.hash.borrow_mut() = None;
}
/// 0 is `v` is 27, 1 if 28, and 4 otherwise.
pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } }
/// Construct a signature object from the sig.
pub fn signature(&self) -> Signature { Signature::from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
/// The message hash of the transaction.
pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() }
/// Returns transaction sender.
pub fn sender(&self) -> Result<Address, Error> {
let mut sender = self.sender.borrow_mut();
match &mut *sender {
&mut Some(ref h) => Ok(h.clone()),
sender @ &mut None => {
*sender = Some(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3()));
Ok(sender.as_ref().unwrap().clone())
}
}
}
/// Signs the transaction as coming from `sender`.
pub fn sign(&mut self, secret: &Secret) {
// TODO: make always low.
let sig = ec::sign(secret, &self.message_hash());
let (r, s, v) = sig.unwrap().to_rsv();
self.r = r;
self.s = s;
self.v = v + 27;
}
/// Signs the transaction as coming from `sender`.
pub fn signed(self, secret: &Secret) -> Transaction { let mut r = self; r.sign(secret); r }
/// Get the transaction cost in gas for the given params.
pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 {
data.iter().fold(
(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64,
|g, b| g + (match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas }) as u64
)
}
/// Get the transaction cost in gas for this transaction.
pub fn gas_required(&self, schedule: &Schedule) -> u64 {
Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule)
}
/// Checks whether the signature has a low 's' value.
pub fn check_low_s(&self) -> Result<(), Error> {
if !ec::is_low_s(&self.s) {
Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)))
} else {
Ok(())
}
}
/// Do basic validation, checking for valid signature and minimum gas,
pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<Transaction, Error> {
if require_low && !ec::is_low_s(&self.s) {
return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)));
}
try!(self.sender());
if self.gas < U256::from(self.gas_required(&schedule)) {
Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas})))
} else {
Ok(self)
}
}
}
impl Decodable for Action {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
if rlp.is_empty() {
Ok(Action::Create)
} else {
Ok(Action::Call(try!(rlp.as_val())))
}
}
}
impl Decodable for Transaction {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
if d.item_count() != 9 {
return Err(DecoderError::RlpIncorrectListLen);
}
Ok(Transaction {
nonce: try!(d.val_at(0)),
gas_price: try!(d.val_at(1)),
gas: try!(d.val_at(2)),
action: try!(d.val_at(3)),
value: try!(d.val_at(4)),
data: try!(d.val_at(5)),
v: try!(d.val_at(6)),
r: try!(d.val_at(7)),
s: try!(d.val_at(8)),
hash: RefCell::new(None),
sender: RefCell::new(None),
})
}
}
#[test]
fn sender_test() {
let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
assert_eq!(t.data, b"");
assert_eq!(t.gas, U256::from(0x5208u64));
assert_eq!(t.gas_price, U256::from(0x01u64));
assert_eq!(t.nonce, U256::from(0x00u64));
if let Action::Call(ref to) = t.action {
assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87"));
} else { panic!(); }
assert_eq!(t.value, U256::from(0x0au64));
assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e"));
}
#[test]
fn signing() {
let key = KeyPair::create().unwrap();
let t = Transaction::new_create(U256::from(42u64), b"Hello!".to_vec(), U256::from(3000u64), U256::from(50_000u64), U256::from(1u64)).signed(&key.secret());
assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
}

435
ethcore/src/verification.rs Normal file
View File

@@ -0,0 +1,435 @@
/// 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::*;
/// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreVerifiedBlock {
/// Populated block header
pub header: Header,
/// Populated block transactions
pub transactions: Vec<Transaction>,
/// Block bytes
pub bytes: Bytes,
}
/// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block
pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Result<(), Error> {
try!(verify_header(&header, engine));
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, engine));
try!(engine.verify_block_basic(&u, None));
}
// Verify transactions.
// TODO: either use transaction views or cache the decoded transactions.
let v = BlockView::new(bytes);
for t in v.transactions() {
try!(engine.verify_transaction_basic(&t, &header));
}
Ok(())
}
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block
/// Returns a PreVerifiedBlock structure populated with transactions
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreVerifiedBlock, Error> {
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));
}
// Verify transactions.
let mut transactions = Vec::new();
{
let v = BlockView::new(&bytes);
for t in v.transactions() {
try!(engine.verify_transaction(&t, &header));
transactions.push(t);
}
}
Ok(PreVerifiedBlock {
header: header,
transactions: transactions,
bytes: bytes,
})
}
/// Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family<BC>(header: &Header, bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
// TODO: verify timestamp
let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
try!(verify_parent(&header, &parent));
try!(engine.verify_block_family(&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: None, max: Some(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>()) {
if excluded.contains(&uncle.hash()) {
return Err(From::from(BlockError::UncleInChain(uncle.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: Some(header.number - depth), max: Some(header.number - 1), found: uncle.number })));
}
else if depth < 1 {
return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number - depth), max: Some(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();
let uncle_parent = try!(bc.block_header(&uncle.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownUncleParent(uncle.parent_hash.clone()))));
for _ in 0..depth {
match bc.block_details(&expected_uncle_parent) {
Some(details) => {
expected_uncle_parent = details.parent;
},
None => break
}
}
if expected_uncle_parent != uncle_parent.hash() {
return Err(From::from(BlockError::UncleParentNotInChain(uncle_parent.hash())));
}
try!(verify_parent(&uncle, &uncle_parent));
try!(engine.verify_block_family(&uncle, &uncle_parent, Some(bytes)));
}
}
Ok(())
}
/// Phase 4 verification. Check block information against transaction enactment results,
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
if expected.gas_used != got.gas_used {
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used, found: got.gas_used })))
}
if expected.receipts_root != got.receipts_root {
return Err(From::from(BlockError::InvalidReceiptsStateRoot(Mismatch { expected: expected.receipts_root.clone(), found: got.receipts_root.clone() })))
}
if expected.state_root != got.state_root {
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root.clone(), found: got.state_root.clone() })))
}
if expected.log_bloom != got.log_bloom {
return Err(From::from(BlockError::InvalidLogBloom(Mismatch { expected: expected.log_bloom.clone(), found: got.log_bloom.clone() })))
}
Ok(())
}
/// Check basic header parameters.
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
if header.number >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
}
if header.gas_used > header.gas_limit {
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
}
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
if header.gas_limit < min_gas_limit {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit })));
}
let maximum_extra_data_size = engine.maximum_extra_data_size();
if header.number != 0 && header.extra_data.len() > maximum_extra_data_size {
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data.len() })));
}
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: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
}
if header.number != parent.number + 1 {
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: 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(())
}
#[cfg(test)]
mod tests {
use util::*;
use header::*;
use verification::*;
use extras::*;
use error::*;
use error::BlockError::*;
use views::*;
use blockchain::*;
use engine::*;
use spec::*;
use transaction::*;
use basic_types::*;
use tests::helpers::*;
fn check_ok(result: Result<(), Error>) {
result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e));
}
fn check_fail(result: Result<(), Error>, e: BlockError) {
match result {
Err(Error::Block(ref error)) if *error == e => (),
Err(other) => panic!("Block verification failed.\nExpected: {:?}\nGot: {:?}", e, other),
Ok(_) => panic!("Block verification failed.\nExpected: {:?}\nGot: Ok", e),
}
}
struct TestBlockChain {
blocks: HashMap<H256, Bytes>,
numbers: HashMap<BlockNumber, H256>,
}
impl TestBlockChain {
pub fn new() -> TestBlockChain {
TestBlockChain {
blocks: HashMap::new(),
numbers: HashMap::new(),
}
}
pub fn insert(&mut self, bytes: Bytes) {
let number = BlockView::new(&bytes).header_view().number();
let hash = BlockView::new(&bytes).header_view().sha3();
self.blocks.insert(hash.clone(), bytes);
self.numbers.insert(number, hash.clone());
}
}
impl BlockProvider for TestBlockChain {
fn is_known(&self, hash: &H256) -> bool {
self.blocks.contains_key(hash)
}
/// Get raw block data
fn block(&self, hash: &H256) -> Option<Bytes> {
self.blocks.get(hash).cloned()
}
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option<BlockDetails> {
self.blocks.get(hash).map(|bytes| {
let header = BlockView::new(bytes).header();
BlockDetails {
number: header.number,
total_difficulty: header.difficulty,
parent: header.parent_hash,
children: Vec::new(),
}
})
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.numbers.get(&index).cloned()
}
}
fn basic_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> {
let header = BlockView::new(bytes).header();
verify_block_basic(&header, bytes, engine)
}
fn family_test<BC>(bytes: &[u8], engine: &Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider {
let header = BlockView::new(bytes).header();
verify_block_family(&header, bytes, engine, bc)
}
#[test]
fn test_verify_block() {
// Test against morden
let mut good = Header::new();
let spec = Spec::new_test();
let engine = spec.to_engine().unwrap();
let min_gas_limit = decode(engine.spec().engine_params.get("minGasLimit").unwrap());
let min_difficulty = decode(engine.spec().engine_params.get("minimumDifficulty").unwrap());
good.gas_limit = min_gas_limit;
good.difficulty = min_difficulty;
good.timestamp = 40;
good.number = 10;
let tr1 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(1));
let tr2 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(2));
let good_transactions = [ &tr1, &tr2 ];
let diff_inc = U256::from(0x40);
let mut parent6 = good.clone();
parent6.number = 6;
let mut parent7 = good.clone();
parent7.number = 7;
parent7.parent_hash = parent6.hash();
parent7.difficulty = parent6.difficulty + diff_inc;
parent7.timestamp = parent6.timestamp + 10;
let mut parent8 = good.clone();
parent8.number = 8;
parent8.parent_hash = parent7.hash();
parent8.difficulty = parent7.difficulty + diff_inc;
parent8.timestamp = parent7.timestamp + 10;
let mut good_uncle1 = good.clone();
good_uncle1.number = 9;
good_uncle1.parent_hash = parent8.hash();
good_uncle1.difficulty = parent8.difficulty + diff_inc;
good_uncle1.timestamp = parent8.timestamp + 10;
good_uncle1.extra_data.push(1u8);
let mut good_uncle2 = good.clone();
good_uncle2.number = 8;
good_uncle2.parent_hash = parent7.hash();
good_uncle2.difficulty = parent7.difficulty + diff_inc;
good_uncle2.timestamp = parent7.timestamp + 10;
good_uncle2.extra_data.push(2u8);
let good_uncles = vec![ good_uncle1.clone(), good_uncle2.clone() ];
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append(&good_uncles);
let good_uncles_hash = uncles_rlp.as_raw().sha3();
let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| t.rlp_bytes_opt(Seal::With)).collect());
let mut parent = good.clone();
parent.number = 9;
parent.timestamp = parent8.timestamp + 10;
parent.parent_hash = parent8.hash();
parent.difficulty = parent8.difficulty + diff_inc;
good.parent_hash = parent.hash();
good.difficulty = parent.difficulty + diff_inc;
good.timestamp = parent.timestamp + 10;
let mut bc = TestBlockChain::new();
bc.insert(create_test_block(&good));
bc.insert(create_test_block(&parent));
bc.insert(create_test_block(&parent6));
bc.insert(create_test_block(&parent7));
bc.insert(create_test_block(&parent8));
check_ok(basic_test(&create_test_block(&good), engine.deref()));
let mut header = good.clone();
header.transactions_root = good_transactions_root.clone();
header.uncles_hash = good_uncles_hash.clone();
check_ok(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()));
header.gas_limit = min_gas_limit - From::from(1);
check_fail(basic_test(&create_test_block(&header), engine.deref()),
InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: header.gas_limit }));
header = good.clone();
header.number = BlockNumber::max_value();
check_fail(basic_test(&create_test_block(&header), engine.deref()),
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
header = good.clone();
header.gas_used = header.gas_limit + From::from(1);
check_fail(basic_test(&create_test_block(&header), engine.deref()),
TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used }));
header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(basic_test(&create_test_block(&header), engine.deref()),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
header = good.clone();
header.extra_data.resize(engine.maximum_extra_data_size() + 1, 0u8);
check_fail(basic_test(&create_test_block(&header), engine.deref()),
ExtraDataOutOfBounds(OutOfBounds { max: Some(engine.maximum_extra_data_size()), min: None, found: header.extra_data.len() }));
header = good.clone();
header.uncles_hash = good_uncles_hash.clone();
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
InvalidTransactionsRoot(Mismatch { expected: good_transactions_root.clone(), found: header.transactions_root }));
header = good.clone();
header.transactions_root = good_transactions_root.clone();
check_fail(basic_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref()),
InvalidUnclesHash(Mismatch { expected: good_uncles_hash.clone(), found: header.uncles_hash }));
check_ok(family_test(&create_test_block(&good), engine.deref(), &bc));
check_ok(family_test(&create_test_block_with_data(&good, &good_transactions, &good_uncles), engine.deref(), &bc));
header = good.clone();
header.parent_hash = H256::random();
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
UnknownParent(header.parent_hash));
header = good.clone();
header.timestamp = 10;
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp }));
header = good.clone();
header.number = 9;
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
header = good.clone();
let mut bad_uncles = good_uncles.clone();
bad_uncles.push(good_uncle1.clone());
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine.deref(), &bc),
TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() }));
// TODO: some additional uncle checks
}
}

250
ethcore/src/views.rs Normal file
View File

@@ -0,0 +1,250 @@
//! Block oriented views onto rlp.
use util::*;
use header::*;
use transaction::*;
/// View onto transaction rlp.
pub struct TransactionView<'a> {
rlp: Rlp<'a>
}
impl<'a> TransactionView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> TransactionView<'a> {
TransactionView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> TransactionView<'a> {
TransactionView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Get the nonce field of the transaction.
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
/// Get the gas_price field of the transaction.
pub fn gas_price(&self) -> U256 { self.rlp.val_at(1) }
/// Get the gas field of the transaction.
pub fn gas(&self) -> U256 { self.rlp.val_at(2) }
/// Get the value field of the transaction.
pub fn value(&self) -> U256 { self.rlp.val_at(4) }
/// Get the data field of the transaction.
pub fn data(&self) -> Bytes { self.rlp.val_at(5) }
/// Get the v field of the transaction.
pub fn v(&self) -> u8 { let r: u16 = self.rlp.val_at(6); r as u8 }
/// Get the r field of the transaction.
pub fn r(&self) -> U256 { self.rlp.val_at(7) }
/// Get the s field of the transaction.
pub fn s(&self) -> U256 { self.rlp.val_at(8) }
// TODO: something like pub fn action(&self) -> Action { self.rlp.val_at(3) }
}
impl<'a> Hashable for TransactionView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}
/// View onto transaction rlp.
pub struct AccountView<'a> {
rlp: Rlp<'a>
}
impl<'a> AccountView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> AccountView<'a> {
AccountView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> AccountView<'a> {
AccountView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Get the nonce field of the transaction.
pub fn nonce(&self) -> U256 { self.rlp.val_at(0) }
/// Get the gas_price field of the transaction.
pub fn balance(&self) -> U256 { self.rlp.val_at(1) }
/// Get the gas field of the transaction.
pub fn storage_root(&self) -> H256 { self.rlp.val_at(2) }
/// Get the value field of the transaction.
pub fn code_hash(&self) -> H256 { self.rlp.val_at(3) }
}
/// View onto block rlp.
pub struct BlockView<'a> {
rlp: Rlp<'a>
}
impl<'a> BlockView<'a> {
/// Creates new view onto block from raw bytes.
pub fn new(bytes: &'a [u8]) -> BlockView<'a> {
BlockView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto block from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> BlockView<'a> {
BlockView {
rlp: rlp
}
}
/// Return reference to underlaying rlp.
pub fn rlp(&self) -> &Rlp<'a> {
&self.rlp
}
/// Create new Header object from header rlp.
pub fn header(&self) -> Header {
self.rlp.val_at(0)
}
/// Create new header view obto block head rlp.
pub fn header_view(&self) -> HeaderView<'a> {
HeaderView::new_from_rlp(self.rlp.at(0))
}
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<Transaction> {
self.rlp.val_at(1)
}
/// Return List of transactions in given block.
pub fn transaction_views(&self) -> Vec<TransactionView> {
self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect()
}
/// Return transaction hashes.
pub fn transaction_hashes(&self) -> Vec<H256> {
self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
/// Return list of uncles of given block.
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(2)
}
/// Return List of transactions in given block.
pub fn uncle_views(&self) -> Vec<HeaderView> {
self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect()
}
/// Return list of uncle hashes of given block.
pub fn uncle_hashes(&self) -> Vec<H256> {
self.rlp.at(2).iter().map(|rlp| rlp.as_raw().sha3()).collect()
}
}
impl<'a> Hashable for BlockView<'a> {
fn sha3(&self) -> H256 {
self.header_view().sha3()
}
}
/// View onto block header rlp.
pub struct HeaderView<'a> {
rlp: Rlp<'a>
}
impl<'a> HeaderView<'a> {
/// Creates new view onto header from raw bytes.
pub fn new(bytes: &'a [u8]) -> HeaderView<'a> {
HeaderView {
rlp: Rlp::new(bytes)
}
}
/// Creates new view onto header from rlp.
pub fn new_from_rlp(rlp: Rlp<'a>) -> HeaderView<'a> {
HeaderView {
rlp: rlp
}
}
/// Returns raw rlp.
pub fn rlp(&self) -> &Rlp<'a> { &self.rlp }
/// Returns parent hash.
pub fn parent_hash(&self) -> H256 { self.rlp.val_at(0) }
/// Returns uncles hash.
pub fn uncles_hash(&self) -> H256 { self.rlp.val_at(1) }
/// Returns author.
pub fn author(&self) -> Address { self.rlp.val_at(2) }
/// Returns state root.
pub fn state_root(&self) -> H256 { self.rlp.val_at(3) }
/// Returns transactions root.
pub fn transactions_root(&self) -> H256 { self.rlp.val_at(4) }
/// Returns block receipts root.
pub fn receipts_root(&self) -> H256 { self.rlp.val_at(5) }
/// Returns block log bloom.
pub fn log_bloom(&self) -> H2048 { self.rlp.val_at(6) }
/// Returns block difficulty.
pub fn difficulty(&self) -> U256 { self.rlp.val_at(7) }
/// Returns block number.
pub fn number(&self) -> BlockNumber { self.rlp.val_at(8) }
/// Returns block gas limit.
pub fn gas_limit(&self) -> U256 { self.rlp.val_at(9) }
/// Returns block gas used.
pub fn gas_used(&self) -> U256 { self.rlp.val_at(10) }
/// Returns timestamp.
pub fn timestamp(&self) -> u64 { self.rlp.val_at(11) }
/// Returns block extra data.
pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) }
/// Returns block seal.
pub fn seal(&self) -> Vec<Bytes> {
let mut seal = vec![];
for i in 13..self.rlp.item_count() {
seal.push(self.rlp.val_at(i));
}
seal
}
}
impl<'a> Hashable for HeaderView<'a> {
fn sha3(&self) -> H256 {
self.rlp.as_raw().sha3()
}
}