Merge branch 'master' into coverage-effort
Conflicts: ethcore/res/ethereum/tests
This commit is contained in:
340
ethcore/src/account.rs
Normal file
340
ethcore/src/account.rs
Normal 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
130
ethcore/src/account_diff.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
||||
55
ethcore/src/action_params.rs
Normal file
55
ethcore/src/action_params.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
15
ethcore/src/basic_types.rs
Normal file
15
ethcore/src/basic_types.rs
Normal 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
397
ethcore/src/block.rs
Normal 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
359
ethcore/src/block_queue.rs
Normal 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
807
ethcore/src/blockchain.rs
Normal 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
272
ethcore/src/builtin.rs
Normal 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
389
ethcore/src/client.rs
Normal 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
12
ethcore/src/common.rs
Normal 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
78
ethcore/src/engine.rs
Normal 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
94
ethcore/src/env_info.rs
Normal 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
221
ethcore/src/error.rs
Normal 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);*/
|
||||
22
ethcore/src/ethereum/denominations.rs
Normal file
22
ethcore/src/ethereum/denominations.rs
Normal 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) }
|
||||
|
||||
261
ethcore/src/ethereum/ethash.rs
Normal file
261
ethcore/src/ethereum/ethash.rs
Normal 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
|
||||
79
ethcore/src/ethereum/mod.rs
Normal file
79
ethcore/src/ethereum/mod.rs
Normal 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
59
ethcore/src/evm/evm.rs
Normal 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
93
ethcore/src/evm/ext.rs
Normal 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
127
ethcore/src/evm/factory.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
);
|
||||
534
ethcore/src/evm/instructions.rs
Normal file
534
ethcore/src/evm/instructions.rs
Normal 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;
|
||||
|
||||
1193
ethcore/src/evm/interpreter.rs
Normal file
1193
ethcore/src/evm/interpreter.rs
Normal file
File diff suppressed because it is too large
Load Diff
368
ethcore/src/evm/jit.rs
Normal file
368
ethcore/src/evm/jit.rs
Normal 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
21
ethcore/src/evm/mod.rs
Normal 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
115
ethcore/src/evm/schedule.rs
Normal 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
475
ethcore/src/evm/tests.rs
Normal 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
854
ethcore/src/executive.rs
Normal 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(¶ms), 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(¶ms), 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(¶ms.sender, ¶ms.address, &val);
|
||||
}
|
||||
trace!("Executive::call(params={:?}) self.env_info={:?}", params, self.info);
|
||||
|
||||
if self.engine.is_builtin(¶ms.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(¶ms.code_address, data);
|
||||
match cost <= params.gas {
|
||||
true => {
|
||||
self.engine.execute_builtin(¶ms.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(¶ms.address);
|
||||
if let ActionValue::Transfer(val) = params.value {
|
||||
self.state.sub_balance(¶ms.sender, &val);
|
||||
self.state.new_contract(¶ms.address, val + prev_bal);
|
||||
} else {
|
||||
self.state.new_contract(¶ms.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
243
ethcore/src/externalities.rs
Normal file
243
ethcore/src/externalities.rs
Normal 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
276
ethcore/src/extras.rs
Normal 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
231
ethcore/src/header.rs
Normal 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
158
ethcore/src/lib.rs
Normal 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
79
ethcore/src/log_entry.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
31
ethcore/src/null_engine.rs
Normal file
31
ethcore/src/null_engine.rs
Normal 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
132
ethcore/src/pod_account.rs
Normal 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
83
ethcore/src/pod_state.rs
Normal 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
54
ethcore/src/receipt.rs
Normal 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
112
ethcore/src/service.rs
Normal 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
319
ethcore/src/spec.rs
Normal 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
448
ethcore/src/state.rs
Normal 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
99
ethcore/src/state_diff.rs
Normal 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
51
ethcore/src/substate.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
81
ethcore/src/tests/chain.rs
Normal file
81
ethcore/src/tests/chain.rs
Normal 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"}
|
||||
78
ethcore/src/tests/client.rs
Normal file
78
ethcore/src/tests/client.rs
Normal 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);
|
||||
}
|
||||
285
ethcore/src/tests/executive.rs
Normal file
285
ethcore/src/tests/executive.rs
Normal 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(¶ms),
|
||||
&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"}
|
||||
261
ethcore/src/tests/helpers.rs
Normal file
261
ethcore/src/tests/helpers.rs
Normal 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)
|
||||
}
|
||||
21
ethcore/src/tests/homestead_chain.rs
Normal file
21
ethcore/src/tests/homestead_chain.rs
Normal 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"}
|
||||
23
ethcore/src/tests/homestead_state.rs
Normal file
23
ethcore/src/tests/homestead_state.rs
Normal 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
11
ethcore/src/tests/mod.rs
Normal 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
747
ethcore/src/tests/state.rs
Normal 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"}
|
||||
34
ethcore/src/tests/test_common.rs
Normal file
34
ethcore/src/tests/test_common.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
ethcore/src/tests/transaction.rs
Normal file
78
ethcore/src/tests/transaction.rs
Normal 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
309
ethcore/src/transaction.rs
Normal 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
435
ethcore/src/verification.rs
Normal 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
250
ethcore/src/views.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user