Merge branch 'master' into evm
Conflicts: src/evm/schedule.rs
This commit is contained in:
commit
2f8f0ac4cf
@ -18,6 +18,7 @@ rust-crypto = "0.2.34"
|
|||||||
time = "0.1"
|
time = "0.1"
|
||||||
#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" }
|
#interpolate_idents = { git = "https://github.com/SkylerLipthay/interpolate_idents" }
|
||||||
evmjit = { path = "rust-evmjit", optional = true }
|
evmjit = { path = "rust-evmjit", optional = true }
|
||||||
|
itertools = "0.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["jit"]
|
default = ["jit"]
|
||||||
|
@ -127,6 +127,12 @@ impl ContextHandle {
|
|||||||
unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) }
|
unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns address to which funds should be transfered after suicide.
|
||||||
|
pub fn suicide_refund_address(&self) -> JitI256 {
|
||||||
|
// evmjit reuses data_handle address field to store suicide address
|
||||||
|
self.data_handle.address
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns gas left.
|
/// Returns gas left.
|
||||||
pub fn gas_left(&self) -> u64 {
|
pub fn gas_left(&self) -> u64 {
|
||||||
self.data_handle.gas as u64
|
self.data_handle.gas as u64
|
||||||
|
321
src/account.rs
321
src/account.rs
@ -1,9 +1,285 @@
|
|||||||
use util::*;
|
use util::*;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] );
|
pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] );
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,PartialEq,Eq)]
|
||||||
|
pub enum Diff<T> where T: Eq {
|
||||||
|
Same,
|
||||||
|
Born(T),
|
||||||
|
Changed(T, T),
|
||||||
|
Died(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,PartialEq,Eq)]
|
||||||
|
pub enum Existance {
|
||||||
|
Born,
|
||||||
|
Alive,
|
||||||
|
Died,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Diff<T> where T: Eq {
|
||||||
|
pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } }
|
||||||
|
pub fn pre(&self) -> Option<&T> { match self { &Diff::Died(ref x) | &Diff::Changed(ref x, _) => Some(x), _ => None } }
|
||||||
|
pub fn post(&self) -> Option<&T> { match self { &Diff::Born(ref x) | &Diff::Changed(_, ref x) => Some(x), _ => None } }
|
||||||
|
pub fn is_same(&self) -> bool { match self { &Diff::Same => true, _ => false }}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,PartialEq,Eq)]
|
||||||
|
/// Genesis account data. Does not have a DB overlay cache.
|
||||||
|
pub struct PodAccount {
|
||||||
|
// Balance of the account.
|
||||||
|
pub balance: U256,
|
||||||
|
// Nonce of the account.
|
||||||
|
pub nonce: U256,
|
||||||
|
pub code: Bytes,
|
||||||
|
pub storage: BTreeMap<H256, H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug,Clone,PartialEq,Eq)]
|
||||||
|
pub struct AccountDiff {
|
||||||
|
pub balance: Diff<U256>, // Allowed to be Same
|
||||||
|
pub nonce: Diff<U256>, // Allowed to be Same
|
||||||
|
pub code: Diff<Bytes>, // Allowed to be Same
|
||||||
|
pub storage: BTreeMap<H256, Diff<H256>>,// Not allowed to be Same
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountDiff {
|
||||||
|
pub fn existance(&self) -> Existance {
|
||||||
|
match self.balance {
|
||||||
|
Diff::Born(_) => Existance::Born,
|
||||||
|
Diff::Died(_) => Existance::Died,
|
||||||
|
_ => Existance::Alive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*impl Debug for AccountDiff {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", PodAccount::from_account(self))
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
pub type StateDiff = BTreeMap<Address, AccountDiff>;
|
||||||
|
|
||||||
|
pub fn pod_diff(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(H256::new()),
|
||||||
|
post.storage.get(&k).cloned().unwrap_or(H256::new())
|
||||||
|
))).collect(),
|
||||||
|
};
|
||||||
|
if r.balance.is_same() && r.nonce.is_same() && r.code.is_same() && r.storage.len() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pod_map_diff(pre: &BTreeMap<Address, PodAccount>, post: &BTreeMap<Address, PodAccount>) -> StateDiff {
|
||||||
|
pre.keys().merge(post.keys()).filter_map(|acc| pod_diff(pre.get(acc), post.get(acc)).map(|d|(acc.clone(), d))).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_diff_create_delete() {
|
||||||
|
let a = map![
|
||||||
|
x!(1) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
assert_eq!(pod_map_diff(&a, &map![]), map![
|
||||||
|
x!(1) => AccountDiff{
|
||||||
|
balance: Diff::Died(x!(69)),
|
||||||
|
nonce: Diff::Died(x!(0)),
|
||||||
|
code: Diff::Died(vec![]),
|
||||||
|
storage: map![],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
assert_eq!(pod_map_diff(&map![], &a), map![
|
||||||
|
x!(1) => AccountDiff{
|
||||||
|
balance: Diff::Born(x!(69)),
|
||||||
|
nonce: Diff::Born(x!(0)),
|
||||||
|
code: Diff::Born(vec![]),
|
||||||
|
storage: map![],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_diff_cretae_delete_with_unchanged() {
|
||||||
|
let a = map![
|
||||||
|
x!(1) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let b = map![
|
||||||
|
x!(1) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
},
|
||||||
|
x!(2) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
assert_eq!(pod_map_diff(&a, &b), map![
|
||||||
|
x!(2) => AccountDiff{
|
||||||
|
balance: Diff::Born(x!(69)),
|
||||||
|
nonce: Diff::Born(x!(0)),
|
||||||
|
code: Diff::Born(vec![]),
|
||||||
|
storage: map![],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
assert_eq!(pod_map_diff(&b, &a), map![
|
||||||
|
x!(2) => AccountDiff{
|
||||||
|
balance: Diff::Died(x!(69)),
|
||||||
|
nonce: Diff::Died(x!(0)),
|
||||||
|
code: Diff::Died(vec![]),
|
||||||
|
storage: map![],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_diff_change_with_unchanged() {
|
||||||
|
let a = map![
|
||||||
|
x!(1) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
},
|
||||||
|
x!(2) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let b = map![
|
||||||
|
x!(1) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(1),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
},
|
||||||
|
x!(2) => PodAccount{
|
||||||
|
balance: x!(69),
|
||||||
|
nonce: x!(0),
|
||||||
|
code: vec![],
|
||||||
|
storage: map![]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
assert_eq!(pod_map_diff(&a, &b), map![
|
||||||
|
x!(1) => AccountDiff{
|
||||||
|
balance: Diff::Same,
|
||||||
|
nonce: Diff::Changed(x!(0), x!(1)),
|
||||||
|
code: Diff::Same,
|
||||||
|
storage: map![],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn account_diff_existence() {
|
||||||
|
let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]};
|
||||||
|
assert_eq!(pod_diff(Some(&a), Some(&a)), None);
|
||||||
|
assert_eq!(pod_diff(None, Some(&a)), Some(AccountDiff{
|
||||||
|
balance: Diff::Born(x!(69)),
|
||||||
|
nonce: Diff::Born(x!(0)),
|
||||||
|
code: Diff::Born(vec![]),
|
||||||
|
storage: map![],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn account_diff_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!(pod_diff(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 account_diff_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!(pod_diff(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 account_diff_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!(pod_diff(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))
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/// Single account in the system.
|
/// Single account in the system.
|
||||||
#[derive(Debug)]
|
#[derive(Clone)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
// Balance of the account.
|
// Balance of the account.
|
||||||
balance: U256,
|
balance: U256,
|
||||||
@ -19,6 +295,29 @@ pub struct Account {
|
|||||||
code_cache: Bytes,
|
code_cache: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PodAccount {
|
||||||
|
/// 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.borrow().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}),
|
||||||
|
code: acc.code_cache.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rlp(&self) -> Bytes {
|
||||||
|
let mut stream = RlpStream::new_list(4);
|
||||||
|
stream.append(&self.nonce);
|
||||||
|
stream.append(&self.balance);
|
||||||
|
// TODO.
|
||||||
|
stream.append(&SHA3_NULL_RLP);
|
||||||
|
stream.append(&self.code.sha3());
|
||||||
|
stream.out()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
/// General constructor.
|
/// General constructor.
|
||||||
pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account {
|
pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account {
|
||||||
@ -32,6 +331,18 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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().fold(HashMap::new(), |mut m, (k, v)| {m.insert(k, v); m})),
|
||||||
|
code_hash: Some(pod.code.sha3()),
|
||||||
|
code_cache: pod.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new account with the given balance.
|
/// Create a new account with the given balance.
|
||||||
pub fn new_basic(balance: U256, nonce: U256) -> Account {
|
pub fn new_basic(balance: U256, nonce: U256) -> Account {
|
||||||
Account {
|
Account {
|
||||||
@ -183,7 +494,7 @@ impl Account {
|
|||||||
|
|
||||||
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
|
/// 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) {
|
pub fn commit_code(&mut self, db: &mut HashDB) {
|
||||||
println!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
|
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()) {
|
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
|
||||||
(true, true) => self.code_hash = Some(SHA3_EMPTY),
|
(true, true) => self.code_hash = Some(SHA3_EMPTY),
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
@ -205,6 +516,12 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Account {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", PodAccount::from_account(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use header::BlockNumber;
|
|||||||
pub type LastHashes = Vec<H256>;
|
pub type LastHashes = Vec<H256>;
|
||||||
|
|
||||||
/// Information concerning the execution environment for a message-call/contract-creation.
|
/// Information concerning the execution environment for a message-call/contract-creation.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EnvInfo {
|
pub struct EnvInfo {
|
||||||
/// The block number.
|
/// The block number.
|
||||||
pub number: BlockNumber,
|
pub number: BlockNumber,
|
||||||
@ -35,6 +36,18 @@ impl EnvInfo {
|
|||||||
gas_used: U256::zero()
|
gas_used: U256::zero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_json(json: &Json) -> EnvInfo {
|
||||||
|
EnvInfo {
|
||||||
|
number: u64_from_json(&json["currentNumber"]),
|
||||||
|
author: address_from_json(&json["currentCoinbase"]),
|
||||||
|
difficulty: u256_from_json(&json["currentDifficulty"]),
|
||||||
|
gas_limit: u256_from_json(&json["currentGasLimit"]),
|
||||||
|
timestamp: u64_from_json(&json["currentTimestamp"]),
|
||||||
|
last_hashes: vec![h256_from_json(&json["previousHash"])],
|
||||||
|
gas_used: U256::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: it should be the other way around.
|
/// TODO: it should be the other way around.
|
||||||
|
@ -5,7 +5,6 @@ use util::uint::*;
|
|||||||
use evm::{Schedule, Error};
|
use evm::{Schedule, Error};
|
||||||
use env_info::*;
|
use env_info::*;
|
||||||
|
|
||||||
// TODO: replace all u64 with u256
|
|
||||||
pub trait Ext {
|
pub trait Ext {
|
||||||
/// Returns a value for given key.
|
/// Returns a value for given key.
|
||||||
fn sload(&self, key: &H256) -> H256;
|
fn sload(&self, key: &H256) -> H256;
|
||||||
@ -24,20 +23,20 @@ pub trait Ext {
|
|||||||
/// If contract creation is successfull, return gas_left and contract address,
|
/// If contract creation is successfull, return gas_left and contract address,
|
||||||
/// If depth is too big or transfer value exceeds balance, return None
|
/// If depth is too big or transfer value exceeds balance, return None
|
||||||
/// Otherwise return appropriate `Error`.
|
/// Otherwise return appropriate `Error`.
|
||||||
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), Error>;
|
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), Error>;
|
||||||
|
|
||||||
/// Message call.
|
/// Message call.
|
||||||
///
|
///
|
||||||
/// If call is successfull, returns gas left.
|
/// If call is successfull, returns gas left.
|
||||||
/// otherwise `Error`.
|
/// otherwise `Error`.
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
gas: u64,
|
gas: &U256,
|
||||||
call_gas: u64,
|
call_gas: &U256,
|
||||||
receive_address: &Address,
|
receive_address: &Address,
|
||||||
value: &U256,
|
value: &U256,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
code_address: &Address,
|
code_address: &Address,
|
||||||
output: &mut [u8]) -> Result<u64, Error>;
|
output: &mut [u8]) -> Result<U256, Error>;
|
||||||
|
|
||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> Vec<u8>;
|
fn extcode(&self, address: &Address) -> Vec<u8>;
|
||||||
@ -47,10 +46,11 @@ pub trait Ext {
|
|||||||
|
|
||||||
/// Should be called when transaction calls `RETURN` opcode.
|
/// Should be called when transaction calls `RETURN` opcode.
|
||||||
/// Returns gas_left if cost of returning the data is not too high.
|
/// Returns gas_left if cost of returning the data is not too high.
|
||||||
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, Error>;
|
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, Error>;
|
||||||
|
|
||||||
/// Should be called when contract commits suicide.
|
/// Should be called when contract commits suicide.
|
||||||
fn suicide(&mut self);
|
/// Address to which funds should be refunded.
|
||||||
|
fn suicide(&mut self, refund_address: &Address);
|
||||||
|
|
||||||
/// Returns schedule.
|
/// Returns schedule.
|
||||||
fn schedule(&self) -> &Schedule;
|
fn schedule(&self) -> &Schedule;
|
||||||
|
@ -209,19 +209,20 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
init_size: u64,
|
init_size: u64,
|
||||||
address: *mut evmjit::H256) {
|
address: *mut evmjit::H256) {
|
||||||
unsafe {
|
unsafe {
|
||||||
match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
|
match self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
|
||||||
Ok((gas_left, opt)) => {
|
Ok((gas_left, opt)) => {
|
||||||
*io_gas = gas_left;
|
*io_gas = gas_left.low_u64();
|
||||||
if let Some(addr) = opt {
|
*address = match opt {
|
||||||
*address = addr.into_jit();
|
Some(addr) => addr.into_jit(),
|
||||||
}
|
_ => Address::new().into_jit()
|
||||||
|
};
|
||||||
},
|
},
|
||||||
Err(err @ evm::Error::OutOfGas) => {
|
Err(err @ evm::Error::OutOfGas) => {
|
||||||
*self.err = Some(err);
|
*self.err = Some(err);
|
||||||
// hack to propagate `OutOfGas` to evmjit and stop
|
// hack to propagate `OutOfGas` to evmjit and stop
|
||||||
// the execution immediately.
|
// the execution immediately.
|
||||||
// Works, cause evmjit uses i64, not u64
|
// Works, cause evmjit uses i64, not u64
|
||||||
*io_gas = -1i64 as u64
|
*io_gas = -1i64 as u64;
|
||||||
},
|
},
|
||||||
Err(err) => *self.err = Some(err)
|
Err(err) => *self.err = Some(err)
|
||||||
}
|
}
|
||||||
@ -239,8 +240,8 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
out_size: u64,
|
out_size: u64,
|
||||||
code_address: *const evmjit::H256) -> bool {
|
code_address: *const evmjit::H256) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let res = self.ext.call(*io_gas,
|
let res = self.ext.call(&U256::from(*io_gas),
|
||||||
call_gas,
|
&U256::from(call_gas),
|
||||||
&Address::from_jit(&*receive_address),
|
&Address::from_jit(&*receive_address),
|
||||||
&U256::from_jit(&*value),
|
&U256::from_jit(&*value),
|
||||||
slice::from_raw_parts(in_beg, in_size as usize),
|
slice::from_raw_parts(in_beg, in_size as usize),
|
||||||
@ -249,7 +250,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(gas_left) => {
|
Ok(gas_left) => {
|
||||||
*io_gas = gas_left;
|
*io_gas = gas_left.low_u64();
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
Err(err @ evm::Error::OutOfGas) => {
|
Err(err @ evm::Error::OutOfGas) => {
|
||||||
@ -344,10 +345,9 @@ impl evm::Evm for JitEvm {
|
|||||||
|
|
||||||
match res {
|
match res {
|
||||||
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
|
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
|
||||||
evmjit::ReturnCode::Return => ext.ret(context.gas_left(), context.output_data()).map(|gas_left| U256::from(gas_left)),
|
evmjit::ReturnCode::Return => ext.ret(&U256::from(context.gas_left()), context.output_data()),
|
||||||
evmjit::ReturnCode::Suicide => {
|
evmjit::ReturnCode::Suicide => {
|
||||||
// what if there is a suicide and we run out of gas just after?
|
ext.suicide(&Address::from_jit(&context.suicide_refund_address()));
|
||||||
ext.suicide();
|
|
||||||
Ok(U256::from(context.gas_left()))
|
Ok(U256::from(context.gas_left()))
|
||||||
},
|
},
|
||||||
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
|
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
|
||||||
|
@ -51,18 +51,18 @@ impl Ext for FakeExt {
|
|||||||
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
|
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&mut self, _gas: u64, _value: &U256, _code: &[u8]) -> result::Result<(u64, Option<Address>), evm::Error> {
|
fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> result::Result<(U256, Option<Address>), evm::Error> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self,
|
fn call(&mut self,
|
||||||
_gas: u64,
|
_gas: &U256,
|
||||||
_call_gas: u64,
|
_call_gas: &U256,
|
||||||
_receive_address: &Address,
|
_receive_address: &Address,
|
||||||
_value: &U256,
|
_value: &U256,
|
||||||
_data: &[u8],
|
_data: &[u8],
|
||||||
_code_address: &Address,
|
_code_address: &Address,
|
||||||
_output: &mut [u8]) -> result::Result<u64, evm::Error> {
|
_output: &mut [u8]) -> result::Result<U256, evm::Error> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +77,11 @@ impl Ext for FakeExt {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(&mut self, _gas: u64, _data: &[u8]) -> result::Result<u64, evm::Error> {
|
fn ret(&mut self, _gas: &U256, _data: &[u8]) -> result::Result<U256, evm::Error> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suicide(&mut self) {
|
fn suicide(&mut self, _refund_address: &Address) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
158
src/executive.rs
158
src/executive.rs
@ -14,7 +14,7 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
|||||||
|
|
||||||
/// State changes which should be applied in finalize,
|
/// State changes which should be applied in finalize,
|
||||||
/// after transaction is fully executed.
|
/// after transaction is fully executed.
|
||||||
struct Substate {
|
pub struct Substate {
|
||||||
/// Any accounts that have suicided.
|
/// Any accounts that have suicided.
|
||||||
suicides: HashSet<Address>,
|
suicides: HashSet<Address>,
|
||||||
/// Any logs.
|
/// Any logs.
|
||||||
@ -27,7 +27,7 @@ struct Substate {
|
|||||||
|
|
||||||
impl Substate {
|
impl Substate {
|
||||||
/// Creates new substate.
|
/// Creates new substate.
|
||||||
fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Substate {
|
Substate {
|
||||||
suicides: HashSet::new(),
|
suicides: HashSet::new(),
|
||||||
logs: vec![],
|
logs: vec![],
|
||||||
@ -160,7 +160,9 @@ impl<'a> Executive<'a> {
|
|||||||
code: self.state.code(address).unwrap_or(vec![]),
|
code: self.state.code(address).unwrap_or(vec![]),
|
||||||
data: t.data.clone(),
|
data: t.data.clone(),
|
||||||
};
|
};
|
||||||
self.call(¶ms, &mut substate, &mut [])
|
// TODO: move output upstream
|
||||||
|
let mut out = vec![];
|
||||||
|
self.call(¶ms, &mut substate, BytesRef::Flexible(&mut out))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,7 +174,7 @@ impl<'a> Executive<'a> {
|
|||||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||||
/// Modifies the substate and the output.
|
/// Modifies the substate and the output.
|
||||||
/// Returns either gas_left or `evm::Error`.
|
/// Returns either gas_left or `evm::Error`.
|
||||||
fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result {
|
pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result {
|
||||||
// at first, transfer value to destination
|
// at first, transfer value to destination
|
||||||
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||||
|
|
||||||
@ -181,7 +183,7 @@ impl<'a> Executive<'a> {
|
|||||||
let cost = self.engine.cost_of_builtin(¶ms.address, ¶ms.data);
|
let cost = self.engine.cost_of_builtin(¶ms.address, ¶ms.data);
|
||||||
match cost <= params.gas {
|
match cost <= params.gas {
|
||||||
true => {
|
true => {
|
||||||
self.engine.execute_builtin(¶ms.address, ¶ms.data, output);
|
self.engine.execute_builtin(¶ms.address, ¶ms.data, &mut output);
|
||||||
Ok(params.gas - cost)
|
Ok(params.gas - cost)
|
||||||
},
|
},
|
||||||
false => Err(evm::Error::OutOfGas)
|
false => Err(evm::Error::OutOfGas)
|
||||||
@ -272,20 +274,26 @@ impl<'a> Executive<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Policy for handling output data on `RETURN` opcode.
|
/// Policy for handling output data on `RETURN` opcode.
|
||||||
enum OutputPolicy<'a> {
|
pub enum OutputPolicy<'a> {
|
||||||
/// Return reference to fixed sized output.
|
/// Return reference to fixed sized output.
|
||||||
/// Used for message calls.
|
/// Used for message calls.
|
||||||
Return(&'a mut [u8]),
|
Return(BytesRef<'a>),
|
||||||
/// Init new contract as soon as `RETURN` is called.
|
/// Init new contract as soon as `RETURN` is called.
|
||||||
InitContract
|
InitContract
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of evm Externalities.
|
/// Implementation of evm Externalities.
|
||||||
struct Externalities<'a> {
|
pub struct Externalities<'a> {
|
||||||
|
#[cfg(test)]
|
||||||
|
pub state: &'a mut State,
|
||||||
|
#[cfg(not(test))]
|
||||||
state: &'a mut State,
|
state: &'a mut State,
|
||||||
info: &'a EnvInfo,
|
info: &'a EnvInfo,
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
#[cfg(test)]
|
||||||
|
pub params: &'a ActionParams,
|
||||||
|
#[cfg(not(test))]
|
||||||
params: &'a ActionParams,
|
params: &'a ActionParams,
|
||||||
substate: &'a mut Substate,
|
substate: &'a mut Substate,
|
||||||
schedule: Schedule,
|
schedule: Schedule,
|
||||||
@ -294,7 +302,7 @@ struct Externalities<'a> {
|
|||||||
|
|
||||||
impl<'a> Externalities<'a> {
|
impl<'a> Externalities<'a> {
|
||||||
/// Basic `Externalities` constructor.
|
/// Basic `Externalities` constructor.
|
||||||
fn new(state: &'a mut State,
|
pub fn new(state: &'a mut State,
|
||||||
info: &'a EnvInfo,
|
info: &'a EnvInfo,
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
@ -337,19 +345,19 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn blockhash(&self, number: &U256) -> H256 {
|
fn blockhash(&self, number: &U256) -> H256 {
|
||||||
match *number < U256::from(self.info.number) {
|
match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 {
|
||||||
false => H256::from(&U256::zero()),
|
|
||||||
true => {
|
true => {
|
||||||
let index = U256::from(self.info.number) - *number - U256::one();
|
let index = self.info.number - number.low_u64() - 1;
|
||||||
self.info.last_hashes[index.low_u32() as usize].clone()
|
self.info.last_hashes[index as usize].clone()
|
||||||
}
|
},
|
||||||
|
false => H256::from(&U256::zero()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), evm::Error> {
|
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> {
|
||||||
// if balance is insufficient or we are to deep, return
|
// if balance is insufficient or we are to deep, return
|
||||||
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
|
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
|
||||||
return Ok((gas, None));
|
return Ok((*gas, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new contract address
|
// create new contract address
|
||||||
@ -360,7 +368,7 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
address: address.clone(),
|
address: address.clone(),
|
||||||
sender: self.params.address.clone(),
|
sender: self.params.address.clone(),
|
||||||
origin: self.params.origin.clone(),
|
origin: self.params.origin.clone(),
|
||||||
gas: U256::from(gas),
|
gas: *gas,
|
||||||
gas_price: self.params.gas_price.clone(),
|
gas_price: self.params.gas_price.clone(),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
code: code.to_vec(),
|
code: code.to_vec(),
|
||||||
@ -369,29 +377,29 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
|
|
||||||
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
||||||
ex.state.inc_nonce(&self.params.address);
|
ex.state.inc_nonce(&self.params.address);
|
||||||
ex.create(¶ms, self.substate).map(|gas_left| (gas_left.low_u64(), Some(address)))
|
ex.create(¶ms, self.substate).map(|gas_left| (gas_left, Some(address)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<u64, evm::Error> {
|
fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<U256, evm::Error> {
|
||||||
let mut gas_cost = call_gas;
|
let mut gas_cost = *call_gas;
|
||||||
let mut call_gas = call_gas;
|
let mut call_gas = *call_gas;
|
||||||
|
|
||||||
let is_call = receive_address == code_address;
|
let is_call = receive_address == code_address;
|
||||||
if is_call && !self.state.exists(&code_address) {
|
if is_call && !self.state.exists(&code_address) {
|
||||||
gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
|
gas_cost = gas_cost + U256::from(self.schedule.call_new_account_gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
if *value > U256::zero() {
|
if *value > U256::zero() {
|
||||||
assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible");
|
assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible");
|
||||||
gas_cost = gas_cost + self.schedule.call_value_transfer_gas as u64;
|
gas_cost = gas_cost + U256::from(self.schedule.call_value_transfer_gas);
|
||||||
call_gas = call_gas + self.schedule.call_stipend as u64;
|
call_gas = call_gas + U256::from(self.schedule.call_stipend);
|
||||||
}
|
}
|
||||||
|
|
||||||
if gas_cost > gas {
|
if gas_cost > *gas {
|
||||||
return Err(evm::Error::OutOfGas)
|
return Err(evm::Error::OutOfGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
let gas = gas - gas_cost;
|
let gas = *gas - gas_cost;
|
||||||
|
|
||||||
// if balance is insufficient or we are to deep, return
|
// if balance is insufficient or we are to deep, return
|
||||||
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
|
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
|
||||||
@ -402,7 +410,7 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
address: receive_address.clone(),
|
address: receive_address.clone(),
|
||||||
sender: self.params.address.clone(),
|
sender: self.params.address.clone(),
|
||||||
origin: self.params.origin.clone(),
|
origin: self.params.origin.clone(),
|
||||||
gas: U256::from(call_gas),
|
gas: call_gas,
|
||||||
gas_price: self.params.gas_price.clone(),
|
gas_price: self.params.gas_price.clone(),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
code: self.state.code(code_address).unwrap_or(vec![]),
|
code: self.state.code(code_address).unwrap_or(vec![]),
|
||||||
@ -410,29 +418,33 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
|
||||||
ex.call(¶ms, self.substate, output).map(|gas_left| {
|
ex.call(¶ms, self.substate, BytesRef::Fixed(output))
|
||||||
gas + gas_left.low_u64()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> Vec<u8> {
|
fn extcode(&self, address: &Address) -> Vec<u8> {
|
||||||
self.state.code(address).unwrap_or(vec![])
|
self.state.code(address).unwrap_or(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, evm::Error> {
|
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
|
||||||
println!("ret");
|
|
||||||
match &mut self.output {
|
match &mut self.output {
|
||||||
&mut OutputPolicy::Return(ref mut slice) => unsafe {
|
&mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe {
|
||||||
let len = cmp::min(slice.len(), data.len());
|
let len = cmp::min(slice.len(), data.len());
|
||||||
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
|
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
|
||||||
Ok(gas)
|
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 => {
|
&mut OutputPolicy::InitContract => {
|
||||||
let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64;
|
let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas);
|
||||||
if return_cost > gas {
|
if return_cost > *gas {
|
||||||
return match self.schedule.exceptional_failed_code_deposit {
|
return match self.schedule.exceptional_failed_code_deposit {
|
||||||
true => Err(evm::Error::OutOfGas),
|
true => Err(evm::Error::OutOfGas),
|
||||||
false => Ok(gas)
|
false => Ok(*gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut code = vec![];
|
let mut code = vec![];
|
||||||
@ -444,7 +456,7 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
let address = &self.params.address;
|
let address = &self.params.address;
|
||||||
self.state.init_code(address, code);
|
self.state.init_code(address, code);
|
||||||
self.substate.contracts_created.push(address.clone());
|
self.substate.contracts_created.push(address.clone());
|
||||||
Ok(gas - return_cost)
|
Ok(*gas - return_cost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,8 +466,10 @@ impl<'a> Ext for Externalities<'a> {
|
|||||||
self.substate.logs.push(LogEntry::new(address, topics, data.to_vec()));
|
self.substate.logs.push(LogEntry::new(address, topics, data.to_vec()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suicide(&mut self) {
|
fn suicide(&mut self, refund_address: &Address) {
|
||||||
let address = self.params.address.clone();
|
let address = self.params.address.clone();
|
||||||
|
let balance = self.balance(&address);
|
||||||
|
self.state.transfer_balance(&address, refund_address, &balance);
|
||||||
self.substate.suicides.insert(address);
|
self.substate.suicides.insert(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,7 +491,6 @@ mod tests {
|
|||||||
use engine::*;
|
use engine::*;
|
||||||
use spec::*;
|
use spec::*;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
use super::Substate;
|
|
||||||
|
|
||||||
struct TestEngine {
|
struct TestEngine {
|
||||||
spec: Spec,
|
spec: Spec,
|
||||||
@ -487,7 +500,7 @@ mod tests {
|
|||||||
impl TestEngine {
|
impl TestEngine {
|
||||||
fn new(stack_limit: usize) -> TestEngine {
|
fn new(stack_limit: usize) -> TestEngine {
|
||||||
TestEngine {
|
TestEngine {
|
||||||
spec: ethereum::new_frontier(),
|
spec: ethereum::new_frontier_test(),
|
||||||
stack_limit: stack_limit
|
stack_limit: stack_limit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -590,7 +603,60 @@ mod tests {
|
|||||||
ex.create(¶ms, &mut substate).unwrap()
|
ex.create(¶ms, &mut substate).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(47_976));
|
assert_eq!(gas_left, U256::from(62_976));
|
||||||
|
// ended with max depth
|
||||||
|
assert_eq!(substate.contracts_created.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_contract_value_too_high() {
|
||||||
|
// 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::new();
|
||||||
|
params.address = address.clone();
|
||||||
|
params.sender = sender.clone();
|
||||||
|
params.origin = sender.clone();
|
||||||
|
params.gas = U256::from(100_000);
|
||||||
|
params.code = code.clone();
|
||||||
|
params.value = U256::from(100);
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
state.add_balance(&sender, &U256::from(100));
|
||||||
|
let info = EnvInfo::new();
|
||||||
|
let engine = TestEngine::new(0);
|
||||||
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
|
ex.create(¶ms, &mut substate).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(gas_left, U256::from(62_976));
|
||||||
assert_eq!(substate.contracts_created.len(), 0);
|
assert_eq!(substate.contracts_created.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +762,7 @@ mod tests {
|
|||||||
|
|
||||||
let gas_left = {
|
let gas_left = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.call(¶ms, &mut substate, &mut []).unwrap()
|
ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(73_237));
|
assert_eq!(gas_left, U256::from(73_237));
|
||||||
@ -738,7 +804,7 @@ mod tests {
|
|||||||
|
|
||||||
let gas_left = {
|
let gas_left = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.call(¶ms, &mut substate, &mut []).unwrap()
|
ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(59_870));
|
assert_eq!(gas_left, U256::from(59_870));
|
||||||
@ -869,8 +935,8 @@ mod tests {
|
|||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::Execution(ExecutionError::NotEnoughCash { required , is }))
|
Err(Error::Execution(ExecutionError::NotEnoughCash { required , is }))
|
||||||
if required == U512::zero() && is == U512::one() => (),
|
if required == U512::from(100_018) && is == U512::from(100_017) => (),
|
||||||
_ => assert!(false, "Expected not enough cash error.")
|
_ => assert!(false, "Expected not enough cash error. {:?}", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -73,20 +73,18 @@
|
|||||||
//! sudo ldconfig
|
//! sudo ldconfig
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use] extern crate log;
|
||||||
extern crate log;
|
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
#[macro_use] extern crate itertools;
|
||||||
extern crate flate2;
|
extern crate flate2;
|
||||||
extern crate rocksdb;
|
extern crate rocksdb;
|
||||||
extern crate heapsize;
|
extern crate heapsize;
|
||||||
extern crate crypto;
|
extern crate crypto;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
#[cfg(feature = "jit" )]
|
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||||
extern crate evmjit;
|
|
||||||
|
|
||||||
extern crate ethcore_util as util;
|
#[macro_use] extern crate ethcore_util as util;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod basic_types;
|
pub mod basic_types;
|
||||||
|
37
src/state.rs
37
src/state.rs
@ -5,6 +5,7 @@ use executive::Executive;
|
|||||||
pub type ApplyResult = Result<Receipt, Error>;
|
pub type ApplyResult = Result<Receipt, Error>;
|
||||||
|
|
||||||
/// Representation of the entire state of all accounts in the system.
|
/// Representation of the entire state of all accounts in the system.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
db: OverlayDB,
|
db: OverlayDB,
|
||||||
root: H256,
|
root: H256,
|
||||||
@ -179,9 +180,33 @@ impl State {
|
|||||||
self.root = Self::commit_into(&mut self.db, r, self.cache.borrow_mut().deref_mut());
|
self.root = Self::commit_into(&mut self.db, r, self.cache.borrow_mut().deref_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Populate the state from `accounts`. Just uses `commit_into`.
|
/// Populate the state from `accounts`.
|
||||||
pub fn populate_from(&mut self, _accounts: &mut HashMap<Address, Option<Account>>) {
|
pub fn populate_from(&mut self, accounts: BTreeMap<Address, PodAccount>) {
|
||||||
unimplemented!();
|
for (add, acc) in accounts.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_map(&self) -> BTreeMap<Address, PodAccount> {
|
||||||
|
// TODO: handle database rather than just the cache.
|
||||||
|
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.
|
/// Pull account `a` in our cache from the trie DB and return it.
|
||||||
@ -224,9 +249,9 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for State {
|
impl fmt::Debug for State {
|
||||||
fn clone(&self) -> Self {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
State::from_existing(self.db.clone(), self.root.clone(), self.account_start_nonce.clone())
|
write!(f, "{:?}", self.cache.borrow())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
293
src/tests/executive.rs
Normal file
293
src/tests/executive.rs
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
use super::test_common::*;
|
||||||
|
use state::*;
|
||||||
|
use executive::*;
|
||||||
|
use spec::*;
|
||||||
|
use engine::*;
|
||||||
|
use evm;
|
||||||
|
use evm::{Schedule, Ext, Factory};
|
||||||
|
use ethereum;
|
||||||
|
|
||||||
|
struct TestEngine {
|
||||||
|
spec: Spec,
|
||||||
|
stack_limit: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestEngine {
|
||||||
|
fn new(stack_limit: usize) -> TestEngine {
|
||||||
|
TestEngine {
|
||||||
|
spec: ethereum::new_frontier_test(),
|
||||||
|
stack_limit: stack_limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine for TestEngine {
|
||||||
|
fn name(&self) -> &str { "TestEngine" }
|
||||||
|
fn spec(&self) -> &Spec { &self.spec }
|
||||||
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||||
|
let mut schedule = Schedule::new_frontier();
|
||||||
|
schedule.stack_limit = self.stack_limit;
|
||||||
|
schedule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CallCreate {
|
||||||
|
data: Bytes,
|
||||||
|
destination: Address,
|
||||||
|
_gas_limit: U256,
|
||||||
|
value: U256
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tiny wrapper around executive externalities.
|
||||||
|
/// Stores callcreates.
|
||||||
|
struct TestExt<'a> {
|
||||||
|
ext: Externalities<'a>,
|
||||||
|
callcreates: Vec<CallCreate>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TestExt<'a> {
|
||||||
|
fn new(ext: Externalities<'a>) -> TestExt {
|
||||||
|
TestExt {
|
||||||
|
ext: ext,
|
||||||
|
callcreates: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Ext for TestExt<'a> {
|
||||||
|
fn sload(&self, key: &H256) -> H256 {
|
||||||
|
self.ext.sload(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sstore(&mut self, key: H256, value: H256) {
|
||||||
|
self.ext.sstore(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
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]) -> Result<(U256, Option<Address>), evm::Error> {
|
||||||
|
// in call and create we need to check if we exited with insufficient balance or max limit reached.
|
||||||
|
// in case of reaching max depth, we should store callcreates. Otherwise, ignore.
|
||||||
|
let res = self.ext.create(gas, value, code);
|
||||||
|
let ext = &self.ext;
|
||||||
|
match res {
|
||||||
|
// just record call create
|
||||||
|
Ok((gas_left, Some(address))) => {
|
||||||
|
self.callcreates.push(CallCreate {
|
||||||
|
data: code.to_vec(),
|
||||||
|
destination: address.clone(),
|
||||||
|
_gas_limit: *gas,
|
||||||
|
value: *value
|
||||||
|
});
|
||||||
|
Ok((gas_left, Some(address)))
|
||||||
|
},
|
||||||
|
// creation failed only due to reaching stack_limit
|
||||||
|
Ok((gas_left, None)) if ext.state.balance(&ext.params.address) >= *value => {
|
||||||
|
let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address));
|
||||||
|
self.callcreates.push(CallCreate {
|
||||||
|
data: code.to_vec(),
|
||||||
|
// TODO: address is not stored here?
|
||||||
|
destination: Address::new(),
|
||||||
|
_gas_limit: *gas,
|
||||||
|
value: *value
|
||||||
|
});
|
||||||
|
Ok((gas_left, Some(address)))
|
||||||
|
},
|
||||||
|
other => other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self,
|
||||||
|
gas: &U256,
|
||||||
|
call_gas: &U256,
|
||||||
|
receive_address: &Address,
|
||||||
|
value: &U256,
|
||||||
|
data: &[u8],
|
||||||
|
code_address: &Address,
|
||||||
|
output: &mut [u8]) -> Result<U256, evm::Error> {
|
||||||
|
let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output);
|
||||||
|
let ext = &self.ext;
|
||||||
|
match res {
|
||||||
|
Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => {
|
||||||
|
self.callcreates.push(CallCreate {
|
||||||
|
data: data.to_vec(),
|
||||||
|
destination: receive_address.clone(),
|
||||||
|
_gas_limit: *call_gas,
|
||||||
|
value: *value
|
||||||
|
});
|
||||||
|
Ok(gas_left)
|
||||||
|
},
|
||||||
|
other => other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcode(&self, address: &Address) -> Vec<u8> {
|
||||||
|
self.ext.extcode(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
|
||||||
|
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 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();
|
||||||
|
for (name, test) in json.as_object().unwrap() {
|
||||||
|
// 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(name.to_string() + ": "+ s); fail = true };
|
||||||
|
|
||||||
|
// test env
|
||||||
|
let mut state = State::new_temp();
|
||||||
|
|
||||||
|
test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() {
|
||||||
|
let address = Address::from(addr.as_ref());
|
||||||
|
let balance = u256_from_json(&s["balance"]);
|
||||||
|
let code = bytes_from_json(&s["code"]);
|
||||||
|
let _nonce = u256_from_json(&s["nonce"]);
|
||||||
|
|
||||||
|
state.new_contract(&address);
|
||||||
|
state.add_balance(&address, &balance);
|
||||||
|
state.init_code(&address, code);
|
||||||
|
|
||||||
|
for (k, v) in s["storage"].as_object().unwrap() {
|
||||||
|
let key = H256::from(&u256_from_str(k));
|
||||||
|
let val = H256::from(&u256_from_json(v));
|
||||||
|
state.set_storage(&address, key, val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut info = EnvInfo::new();
|
||||||
|
|
||||||
|
test.find("env").map(|env| {
|
||||||
|
info.author = address_from_json(&env["currentCoinbase"]);
|
||||||
|
info.difficulty = u256_from_json(&env["currentDifficulty"]);
|
||||||
|
info.gas_limit = u256_from_json(&env["currentGasLimit"]);
|
||||||
|
info.number = u256_from_json(&env["currentNumber"]).low_u64();
|
||||||
|
info.timestamp = u256_from_json(&env["currentTimestamp"]).low_u64();
|
||||||
|
});
|
||||||
|
|
||||||
|
let engine = TestEngine::new(0);
|
||||||
|
|
||||||
|
// params
|
||||||
|
let mut params = ActionParams::new();
|
||||||
|
test.find("exec").map(|exec| {
|
||||||
|
params.address = address_from_json(&exec["address"]);
|
||||||
|
params.sender = address_from_json(&exec["caller"]);
|
||||||
|
params.origin = address_from_json(&exec["origin"]);
|
||||||
|
params.code = bytes_from_json(&exec["code"]);
|
||||||
|
params.data = bytes_from_json(&exec["data"]);
|
||||||
|
params.gas = u256_from_json(&exec["gas"]);
|
||||||
|
params.gas_price = u256_from_json(&exec["gasPrice"]);
|
||||||
|
params.value = u256_from_json(&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 ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output)));
|
||||||
|
let mut test_ext = TestExt::new(ex);
|
||||||
|
let evm = Factory::create();
|
||||||
|
let res = evm.exec(¶ms, &mut test_ext);
|
||||||
|
(res, test_ext.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 : {:?}, expected: {:?}", name, gas_left, u256_from_json(&test["gas"]));
|
||||||
|
fail_unless(!out_of_gas, "expected to run out of gas.");
|
||||||
|
fail_unless(gas_left == u256_from_json(&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());
|
||||||
|
//let balance = u256_from_json(&s["balance"]);
|
||||||
|
|
||||||
|
fail_unless(state.code(&address).unwrap_or(vec![]) == bytes_from_json(&s["code"]), "code is incorrect");
|
||||||
|
fail_unless(state.balance(&address) == u256_from_json(&s["balance"]), "balance is incorrect");
|
||||||
|
fail_unless(state.nonce(&address) == u256_from_json(&s["nonce"]), "nonce is incorrect");
|
||||||
|
|
||||||
|
for (k, v) in s["storage"].as_object().unwrap() {
|
||||||
|
let key = H256::from(&u256_from_str(k));
|
||||||
|
let val = H256::from(&u256_from_json(v));
|
||||||
|
|
||||||
|
fail_unless(state.storage_at(&address, &key) == val, "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 is = &callcreates[i];
|
||||||
|
let expected = &cc[i];
|
||||||
|
fail_unless(is.data == bytes_from_json(&expected["data"]), "callcreates data is incorrect");
|
||||||
|
fail_unless(is.destination == address_from_json(&expected["destination"]), "callcreates destination is incorrect");
|
||||||
|
fail_unless(is.value == u256_from_json(&expected["value"]), "callcreates value is incorrect");
|
||||||
|
|
||||||
|
// TODO: call_gas is calculated in externalities and is not exposed to TestExt.
|
||||||
|
// maybe move it to it's own function to simplify calculation?
|
||||||
|
//println!("name: {:?}, is {:?}, expected: {:?}", name, is.gas_limit, u256_from_json(&expected["gasLimit"]));
|
||||||
|
//fail_unless(is.gas_limit == u256_from_json(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for f in failed.iter() {
|
||||||
|
println!("FAILED: {:?}", f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//assert!(false);
|
||||||
|
failed
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
||||||
|
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
||||||
|
// this one crashes with some vm internal error. Separately they pass.
|
||||||
|
declare_test_ignore!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
||||||
|
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
||||||
|
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
|
||||||
|
// this one take way too long.
|
||||||
|
declare_test_ignore!{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"}
|
@ -1,4 +1,6 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod test_common;
|
mod test_common;
|
||||||
|
|
||||||
mod transaction;
|
mod transaction;
|
||||||
|
mod executive;
|
||||||
|
mod state;
|
||||||
|
70
src/tests/state.rs
Normal file
70
src/tests/state.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use super::test_common::*;
|
||||||
|
use state::*;
|
||||||
|
use ethereum;
|
||||||
|
|
||||||
|
pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap<H256, H256> {
|
||||||
|
json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| {
|
||||||
|
m.insert(H256::from(&u256_from_str(key)), H256::from(&u256_from_json(value)));
|
||||||
|
m
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translate the JSON object into a hash map of account information ready for insertion into State.
|
||||||
|
pub fn pod_map_from_json(json: &Json) -> BTreeMap<Address, PodAccount> {
|
||||||
|
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(&map_h256_h256_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(U256::zero()),
|
||||||
|
nonce: nonce.unwrap_or(U256::zero()),
|
||||||
|
storage: storage.unwrap_or(BTreeMap::new()),
|
||||||
|
code: code.unwrap_or(Vec::new())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 engine = ethereum::new_frontier_test().to_engine().unwrap();
|
||||||
|
|
||||||
|
for (name, test) in json.as_object().unwrap() {
|
||||||
|
let mut fail = false;
|
||||||
|
let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true; true } else {false};
|
||||||
|
|
||||||
|
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 = h256_from_json(&test["postStateRoot"]);
|
||||||
|
let pre = pod_map_from_json(&test["pre"]);
|
||||||
|
let post = pod_map_from_json(&test["post"]);
|
||||||
|
// TODO: read test["logs"]
|
||||||
|
|
||||||
|
println!("Transaction: {:?}", t);
|
||||||
|
println!("Env: {:?}", env);
|
||||||
|
|
||||||
|
let mut s = State::new_temp();
|
||||||
|
s.populate_from(pre);
|
||||||
|
|
||||||
|
s.apply(&env, engine.deref(), &t).unwrap();
|
||||||
|
let our_post = s.to_pod_map();
|
||||||
|
|
||||||
|
if fail_unless(s.root() == &post_state_root) {
|
||||||
|
println!("DIFF:\n{:?}", pod_map_diff(&post, &our_post));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Compare logs.
|
||||||
|
}
|
||||||
|
for f in failed.iter() {
|
||||||
|
println!("FAILED: {:?}", f);
|
||||||
|
}
|
||||||
|
failed
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_test!{StateTests_stExample, "StateTests/stExample"}
|
@ -1,43 +1,5 @@
|
|||||||
pub use common::*;
|
pub use common::*;
|
||||||
|
|
||||||
pub fn clean(s: &str) -> &str {
|
|
||||||
if s.len() >= 2 && &s[0..2] == "0x" {
|
|
||||||
&s[2..]
|
|
||||||
} else {
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytes_from_json(json: &Json) -> Bytes {
|
|
||||||
let s = json.as_string().unwrap();
|
|
||||||
if s.len() % 2 == 1 {
|
|
||||||
FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![])
|
|
||||||
} else {
|
|
||||||
FromHex::from_hex(clean(s)).unwrap_or(vec![])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn address_from_json(json: &Json) -> Address {
|
|
||||||
let s = json.as_string().unwrap();
|
|
||||||
if s.len() % 2 == 1 {
|
|
||||||
address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..])
|
|
||||||
} else {
|
|
||||||
address_from_hex(clean(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn u256_from_json(json: &Json) -> U256 {
|
|
||||||
let s = json.as_string().unwrap();
|
|
||||||
if s.len() >= 2 && &s[0..2] == "0x" {
|
|
||||||
// hex
|
|
||||||
U256::from_str(&s[2..]).unwrap()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// dec
|
|
||||||
U256::from_dec_str(s).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! declare_test {
|
macro_rules! declare_test {
|
||||||
($id: ident, $name: expr) => {
|
($id: ident, $name: expr) => {
|
||||||
|
@ -6,15 +6,16 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
let old_schedule = evm::Schedule::new_frontier();
|
let old_schedule = evm::Schedule::new_frontier();
|
||||||
let new_schedule = evm::Schedule::new_homestead();
|
let new_schedule = evm::Schedule::new_homestead();
|
||||||
|
let ot = RefCell::new(Transaction::new());
|
||||||
for (name, test) in json.as_object().unwrap() {
|
for (name, test) in json.as_object().unwrap() {
|
||||||
let mut fail = false;
|
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| if !cond && !fail { failed.push(name.to_string()); println!("Transaction: {:?}", ot.borrow()); fail = true };
|
||||||
let schedule = match test.find("blocknumber")
|
let schedule = match test.find("blocknumber")
|
||||||
.and_then(|j| j.as_string())
|
.and_then(|j| j.as_string())
|
||||||
.and_then(|s| BlockNumber::from_str(s).ok())
|
.and_then(|s| BlockNumber::from_str(s).ok())
|
||||||
.unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule };
|
.unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule };
|
||||||
let rlp = bytes_from_json(&test["rlp"]);
|
let rlp = bytes_from_json(&test["rlp"]);
|
||||||
let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(schedule));
|
let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(schedule, schedule.have_delegate_call));
|
||||||
fail_unless(test.find("transaction").is_none() == res.is_err());
|
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")) {
|
if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) {
|
||||||
let t = res.unwrap();
|
let t = res.unwrap();
|
||||||
@ -25,8 +26,10 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
fail_unless(t.nonce == u256_from_json(&tx["nonce"]));
|
fail_unless(t.nonce == u256_from_json(&tx["nonce"]));
|
||||||
fail_unless(t.value == u256_from_json(&tx["value"]));
|
fail_unless(t.value == u256_from_json(&tx["value"]));
|
||||||
if let Action::Call(ref to) = t.action {
|
if let Action::Call(ref to) = t.action {
|
||||||
|
*ot.borrow_mut() = t.clone();
|
||||||
fail_unless(to == &address_from_json(&tx["to"]));
|
fail_unless(to == &address_from_json(&tx["to"]));
|
||||||
} else {
|
} else {
|
||||||
|
*ot.borrow_mut() = t.clone();
|
||||||
fail_unless(bytes_from_json(&tx["to"]).len() == 0);
|
fail_unless(bytes_from_json(&tx["to"]).len() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use basic_types::*;
|
|||||||
use error::*;
|
use error::*;
|
||||||
use evm::Schedule;
|
use evm::Schedule;
|
||||||
|
|
||||||
|
#[derive(Debug,Clone)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
Create,
|
Create,
|
||||||
Call(Address),
|
Call(Address),
|
||||||
@ -10,6 +11,7 @@ pub enum Action {
|
|||||||
|
|
||||||
/// A set of information describing an externally-originating message call
|
/// A set of information describing an externally-originating message call
|
||||||
/// or contract creation operation.
|
/// or contract creation operation.
|
||||||
|
#[derive(Debug,Clone)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
pub nonce: U256,
|
pub nonce: U256,
|
||||||
pub gas_price: U256,
|
pub gas_price: U256,
|
||||||
@ -28,6 +30,21 @@ pub struct Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Transaction {
|
||||||
|
nonce: U256::zero(),
|
||||||
|
gas_price: U256::zero(),
|
||||||
|
gas: U256::zero(),
|
||||||
|
action: Action::Create,
|
||||||
|
value: U256::zero(),
|
||||||
|
data: vec![],
|
||||||
|
v: 0,
|
||||||
|
r: U256::zero(),
|
||||||
|
s: U256::zero(),
|
||||||
|
hash: RefCell::new(None),
|
||||||
|
sender: RefCell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Create a new message-call transaction.
|
/// Create a new message-call transaction.
|
||||||
pub fn new_call(to: Address, value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
|
pub fn new_call(to: Address, value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
@ -62,6 +79,32 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_json(json: &Json) -> Transaction {
|
||||||
|
let mut r = Transaction {
|
||||||
|
nonce: u256_from_json(&json["nonce"]),
|
||||||
|
gas_price: u256_from_json(&json["gasPrice"]),
|
||||||
|
gas: u256_from_json(&json["gasLimit"]),
|
||||||
|
action: match bytes_from_json(&json["to"]) {
|
||||||
|
ref x if x.len() == 0 => Action::Create,
|
||||||
|
ref x => Action::Call(Address::from_slice(x)),
|
||||||
|
},
|
||||||
|
value: u256_from_json(&json["value"]),
|
||||||
|
data: bytes_from_json(&json["data"]),
|
||||||
|
v: match json.find("v") { Some(ref j) => u8_from_json(j), None => 0 },
|
||||||
|
r: match json.find("r") { Some(ref j) => u256_from_json(j), None => U256::zero() },
|
||||||
|
s: match json.find("s") { Some(ref j) => u256_from_json(j), None => U256::zero() },
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the nonce of the transaction.
|
/// Get the nonce of the transaction.
|
||||||
pub fn nonce(&self) -> &U256 { &self.nonce }
|
pub fn nonce(&self) -> &U256 { &self.nonce }
|
||||||
/// Get the gas price of the transaction.
|
/// Get the gas price of the transaction.
|
||||||
@ -146,6 +189,7 @@ impl Transaction {
|
|||||||
|
|
||||||
/// Signs the transaction as coming from `sender`.
|
/// Signs the transaction as coming from `sender`.
|
||||||
pub fn sign(&mut self, secret: &Secret) {
|
pub fn sign(&mut self, secret: &Secret) {
|
||||||
|
// TODO: make always low.
|
||||||
let sig = ec::sign(secret, &self.message_hash());
|
let sig = ec::sign(secret, &self.message_hash());
|
||||||
let (r, s, v) = sig.unwrap().to_rsv();
|
let (r, s, v) = sig.unwrap().to_rsv();
|
||||||
self.r = r;
|
self.r = r;
|
||||||
@ -170,7 +214,10 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Do basic validation, checking for valid signature and minimum gas,
|
/// Do basic validation, checking for valid signature and minimum gas,
|
||||||
pub fn validate(self, schedule: &Schedule) -> Result<Transaction, Error> {
|
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());
|
try!(self.sender());
|
||||||
if self.gas < U256::from(self.gas_required(&schedule)) {
|
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})))
|
Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas})))
|
||||||
|
Loading…
Reference in New Issue
Block a user