Merge branch 'master' into close_gently

Conflicts:
	parity/main.rs
This commit is contained in:
Tomusdrw
2016-02-10 13:13:04 +01:00
38 changed files with 1132 additions and 398 deletions

View File

@@ -183,11 +183,11 @@ impl Account {
#[cfg(test)]
/// 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() }
#[cfg(test)]
/// 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 overlay.
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
@@ -198,7 +198,11 @@ impl Account {
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; }
/// Panics if balance is less than `x`
pub fn sub_balance(&mut self, x: &U256) {
assert!(self.balance >= *x);
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 AccountDBMut) {

View File

@@ -85,6 +85,9 @@ pub trait BlockProvider {
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256>;
/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress>;
/// Get the partial-header of a block.
fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
@@ -107,6 +110,16 @@ pub trait BlockProvider {
self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number())
}
/// Get transaction with given transaction hash.
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> {
self.transaction_address(hash).and_then(|address| self.transaction_at(&address))
}
/// Get transaction at given address.
fn transaction_at(&self, address: &TransactionAddress) -> Option<SignedTransaction> {
self.block(&address.block_hash).map(|bytes| BlockView::new(&bytes).transactions()).and_then(|t| t.into_iter().nth(address.index))
}
/// Get a list of transactions for a given block.
/// Returns None if block deos not exist.
fn transactions(&self, hash: &H256) -> Option<Vec<SignedTransaction>> {
@@ -201,6 +214,11 @@ impl BlockProvider for BlockChain {
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.query_extras(&index, &self.block_hashes)
}
/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress> {
self.query_extras(hash, &self.transaction_addresses)
}
}
const COLLECTION_QUEUE_SIZE: usize = 8;
@@ -474,6 +492,14 @@ impl BlockChain {
parent_details.children.push(hash.clone());
batch.put_extras(&parent_hash, &parent_details);
// update transaction addresses
for (i, tx_hash) in block.transaction_hashes().iter().enumerate() {
batch.put_extras(tx_hash, &TransactionAddress {
block_hash: hash.clone(),
index: i
});
}
// if it's not new best block, just return
if !is_new_best {
return (batch, None, details);
@@ -824,4 +850,21 @@ mod tests {
let bc = bc_result.reference();
assert_eq!(bc.best_block_number(), 0);
}
#[test]
fn find_transaction_by_hash() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap();
let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
bc.insert_block(&b1);
let transactions = bc.transactions(&b1_hash).unwrap();
assert_eq!(transactions.len(), 7);
for t in transactions {
assert_eq!(bc.transaction(&t.hash()).unwrap(), t);
}
}
}

View File

@@ -86,6 +86,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block total difficulty.
fn block_total_difficulty(&self, hash: &H256) -> Option<U256>;
/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;
/// Get raw block header data by block number.
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;
@@ -163,7 +166,7 @@ pub struct Client {
}
const HISTORY: u64 = 1000;
const CLIENT_DB_VER_STR: &'static str = "1.0";
const CLIENT_DB_VER_STR: &'static str = "2.0";
impl Client {
/// Create a new client with given spec and DB path.
@@ -367,6 +370,10 @@ impl BlockChainClient for Client {
self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty)
}
fn code(&self, address: &Address) -> Option<Bytes> {
self.state().code(address)
}
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
}

View File

@@ -23,8 +23,6 @@ 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.
@@ -49,6 +47,17 @@ impl Ethash {
})
}
#[cfg(test)]
fn new_test(spec: Spec) -> Ethash {
Ethash {
spec: spec,
pow: EthashManager::new(),
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)))
@@ -123,6 +132,11 @@ impl Engine for Ethash {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the seal fields.
if header.seal.len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
)));
}
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
@@ -143,6 +157,11 @@ impl Engine for Ethash {
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
if header.seal.len() != self.seal_fields() {
return Err(From::from(BlockError::InvalidSealArity(
Mismatch { expected: self.seal_fields(), found: header.seal.len() }
)));
}
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));
@@ -156,6 +175,11 @@ impl Engine for Ethash {
}
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// we should not calculate difficulty for genesis blocks
if header.number() == 0 {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
}
// Check difficulty is correct given the two timestamps.
let expected_difficulty = self.calculate_difficuty(header, parent);
if header.difficulty != expected_difficulty {
@@ -242,38 +266,236 @@ impl Header {
}
}
#[test]
fn on_close_block() {
#[cfg(test)]
mod tests {
extern crate ethash;
use common::*;
use block::*;
use engine::*;
use tests::helpers::*;
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.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db, &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
use super::super::new_morden;
#[test]
fn on_close_block() {
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.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db, &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() {
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.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let mut b = OpenBlock::new(engine.deref(), db, &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());
}
#[test]
fn has_valid_metadata() {
let engine = Ethash::new_boxed(new_morden());
assert!(!engine.name().is_empty());
assert!(engine.version().major >= 1);
}
#[test]
fn can_return_params() {
let engine = Ethash::new_test(new_morden());
assert!(engine.u64_param("durationLimit") > 0);
assert!(engine.u256_param("minimumDifficulty") > U256::zero());
}
#[test]
fn can_return_factory() {
let engine = Ethash::new_test(new_morden());
engine.vm_factory();
}
#[test]
fn can_return_schedule() {
let engine = Ethash::new_test(new_morden());
let schedule = engine.schedule(&EnvInfo {
number: 10000000,
author: x!(0),
timestamp: 0,
difficulty: x!(0),
last_hashes: vec![],
gas_used: x!(0),
gas_limit: x!(0)
});
assert!(schedule.stack_limit > 0);
let schedule = engine.schedule(&EnvInfo {
number: 100,
author: x!(0),
timestamp: 0,
difficulty: x!(0),
last_hashes: vec![],
gas_used: x!(0),
gas_limit: x!(0)
});
assert!(!schedule.have_delegate_call);
}
#[test]
fn can_do_seal_verification_fail() {
let engine = Ethash::new_test(new_morden());
let header: Header = Header::default();
let verify_result = engine.verify_block_basic(&header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_difficulty_verification_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
let verify_result = engine.verify_block_basic(&header, None);
match verify_result {
Err(Error::Block(BlockError::DifficultyOutOfBounds(_))) => {},
Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_proof_of_work_verification_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
let verify_result = engine.verify_block_basic(&header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {},
Err(_) => { panic!("should be invalid proof of work error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_seal_unordered_verification_fail() {
let engine = Ethash::new_test(new_morden());
let header: Header = Header::default();
let verify_result = engine.verify_block_unordered(&header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidSealArity(_))) => {},
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_seal256_verification_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
let verify_result = engine.verify_block_unordered(&header, None);
match verify_result {
Err(Error::Block(BlockError::MismatchedH256SealElement(_))) => {},
Err(_) => { panic!("should be invalid 256-bit seal fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_proof_of_work_unordered_verification_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).to_vec(), rlp::encode(&H64::zero()).to_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
let verify_result = engine.verify_block_unordered(&header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {},
Err(_) => { panic!("should be invalid proof-of-work fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_verify_block_family_genesis_fail() {
let engine = Ethash::new_test(new_morden());
let header: Header = Header::default();
let parent_header: Header = Header::default();
let verify_result = engine.verify_block_family(&header, &parent_header, None);
match verify_result {
Err(Error::Block(BlockError::RidiculousNumber(_))) => {},
Err(_) => { panic!("should be invalid block number fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_verify_block_family_difficulty_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_number(2);
let mut parent_header: Header = Header::default();
parent_header.set_number(1);
let verify_result = engine.verify_block_family(&header, &parent_header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidDifficulty(_))) => {},
Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_verify_block_family_gas_fail() {
let engine = Ethash::new_test(new_morden());
let mut header: Header = Header::default();
header.set_number(2);
header.set_difficulty(U256::from_str("0000000000000000000000000000000000000000000000000000000000020000").unwrap());
let mut parent_header: Header = Header::default();
parent_header.set_number(1);
let verify_result = engine.verify_block_family(&header, &parent_header, None);
match verify_result {
Err(Error::Block(BlockError::InvalidGasLimit(_))) => {},
Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
// TODO: difficulty test
}
#[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.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, &last_hashes, Address::zero(), vec![]);
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
b.push_uncle(uncle).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("478eae0e571ba000").unwrap());
assert_eq!(b.state().balance(&uncle_author), U256::from_str("3cb71f51fc558000").unwrap());
}
// TODO: difficulty test

View File

@@ -159,11 +159,13 @@ macro_rules! evm_test_ignore(
#[test]
#[ignore]
#[cfg(feature = "jit")]
#[cfg(feature = "ignored-tests")]
fn $name_jit() {
$name_test(Factory::new(VMType::Jit));
}
#[test]
#[ignore]
#[cfg(feature = "ignored-tests")]
fn $name_int() {
$name_test(Factory::new(VMType::Interpreter));
}

View File

@@ -43,11 +43,11 @@ pub struct Executed {
pub gas: U256,
/// Gas used during execution of transaction.
pub gas_used: U256,
/// Gas refunded after the execution of transaction.
/// 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.
@@ -56,9 +56,9 @@ pub struct Executed {
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
///
///
/// 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>
}
@@ -119,13 +119,13 @@ impl<'a> Executive<'a> {
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
return Err(From::from(ExecutionError::BlockGasLimitReached {
gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used,
gas: t.gas
}));
}
@@ -220,7 +220,7 @@ impl<'a> Executive<'a> {
if self.engine.is_builtin(&params.code_address) {
// if destination is builtin, try to execute it
let default = [];
let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] };
@@ -239,7 +239,7 @@ impl<'a> Executive<'a> {
}
} 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();
@@ -258,7 +258,7 @@ impl<'a> Executive<'a> {
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.
@@ -317,7 +317,7 @@ impl<'a> Executive<'a> {
self.state.kill_account(address);
}
match result {
match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(_) => {
Ok(Executed {
@@ -345,8 +345,8 @@ impl<'a> Executive<'a> {
fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate) {
match *result {
Err(evm::Error::OutOfGas)
| Err(evm::Error::BadJumpDestination {..})
| Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::BadJumpDestination {..})
| Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::StackUnderflow {..})
| Err(evm::Error::OutOfStack {..}) => {
self.state.revert_snapshot();
@@ -360,45 +360,14 @@ impl<'a> Executive<'a> {
}
#[cfg(test)]
#[allow(dead_code)]
mod tests {
use super::*;
use common::*;
use ethereum;
use engine::*;
use spec::*;
use evm::{Schedule, Factory, VMType};
use evm::{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();
@@ -487,7 +456,7 @@ mod tests {
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);
@@ -541,7 +510,7 @@ mod tests {
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);
}
@@ -593,12 +562,13 @@ mod tests {
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
// TODO: fix (preferred) or remove
evm_test_ignore!{test_aba_calls: test_aba_calls_jit, test_aba_calls_int}
fn test_aba_calls(factory: Factory) {
// 60 00 - push 0
@@ -659,11 +629,12 @@ mod tests {
}
// test is incorrect, mk
// TODO: fix (preferred) or remove
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
// 54 - sload
// 01 - add
// 60 00 - push 0
// 55 - sstore
@@ -704,6 +675,7 @@ mod tests {
}
// test is incorrect, mk
// TODO: fix (preferred) or remove
evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int}
fn test_transact_simple(factory: Factory) {
let keypair = KeyPair::create().unwrap();
@@ -762,7 +734,7 @@ mod tests {
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.")
@@ -793,10 +765,10 @@ mod tests {
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() => (),
Err(Error::Execution(ExecutionError::InvalidNonce { expected, got }))
if expected == U256::zero() && got == U256::one() => (),
_ => assert!(false, "Expected invalid nonce error.")
}
}
@@ -828,8 +800,8 @@ mod tests {
};
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) => (),
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.")
}
}
@@ -859,10 +831,10 @@ mod tests {
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) => (),
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)
}
}
@@ -902,5 +874,4 @@ mod tests {
}
}
}
}

View File

@@ -68,12 +68,12 @@ pub struct Externalities<'a> {
impl<'a> Externalities<'a> {
/// Basic `Externalities` constructor.
pub fn new(state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
pub fn new(state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
substate: &'a mut Substate,
output: OutputPolicy<'a>) -> Self {
Externalities {
state: state,
@@ -106,16 +106,18 @@ impl<'a> Ext for Externalities<'a> {
}
fn blockhash(&self, number: &U256) -> H256 {
// TODO: comment out what this function expects from env_info, since it will produce panics if the latter is inconsistent
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;
assert!(index < self.env_info.last_hashes.len() as u64, format!("Inconsistent env_info, should contain at least {:?} last hashes", index+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())
H256::zero()
},
}
}
@@ -139,7 +141,7 @@ impl<'a> Ext for Externalities<'a> {
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) => {
@@ -150,18 +152,18 @@ impl<'a> Ext for Externalities<'a> {
}
}
fn call(&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
fn call(&mut self,
gas: &U256,
sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
code_address: &Address,
data: &[u8],
code_address: &Address,
output: &mut [u8]) -> MessageCallResult {
let mut params = ActionParams {
sender: sender_address.clone(),
address: receive_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(),
@@ -257,3 +259,144 @@ impl<'a> Ext for Externalities<'a> {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
}
}
#[cfg(test)]
mod tests {
use common::*;
use state::*;
use engine::*;
use evm::{Ext};
use substate::*;
use tests::helpers::*;
use super::*;
fn get_test_origin() -> OriginInfo {
OriginInfo {
address: Address::zero(),
origin: Address::zero(),
gas_price: U256::zero(),
value: U256::zero()
}
}
fn get_test_env_info() -> EnvInfo {
EnvInfo {
number: 100,
author: x!(0),
timestamp: 0,
difficulty: x!(0),
last_hashes: vec![],
gas_used: x!(0),
gas_limit: x!(0)
}
}
struct TestSetup {
state: GuardedTempResult<State>,
engine: Box<Engine>,
sub_state: Substate,
env_info: EnvInfo
}
impl TestSetup {
fn new() -> TestSetup {
TestSetup {
state: get_temp_state(),
engine: get_test_spec().to_engine().unwrap(),
sub_state: Substate::new(),
env_info: get_test_env_info()
}
}
}
#[test]
fn can_be_created() {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
assert_eq!(ext.env_info().number, 100);
}
#[test]
fn can_return_block_hash_no_env() {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
assert_eq!(hash, H256::zero());
}
#[test]
fn can_return_block_hash() {
let test_hash = H256::from("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd");
let test_env_number = 0x120001;
let mut setup = TestSetup::new();
{
let env_info = &mut setup.env_info;
env_info.number = test_env_number;
env_info.last_hashes.push(test_hash.clone());
}
let state = setup.state.reference_mut();
let ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
let hash = ext.blockhash(&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap());
assert_eq!(test_hash, hash);
}
#[test]
#[should_panic]
fn can_call_fail_empty() {
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
let mut output = vec![];
// this should panic because we have no balance on any account
ext.call(
&U256::from_str("0000000000000000000000000000000000000000000000000000000000120000").unwrap(),
&Address::new(),
&Address::new(),
Some(U256::from_str("0000000000000000000000000000000000000000000000000000000000150000").unwrap()),
&vec![],
&Address::new(),
&mut output);
}
#[test]
fn can_log() {
let log_data = vec![120u8, 110u8];
let log_topics = vec![H256::from("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd")];
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
{
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
ext.log(log_topics, &log_data);
}
assert_eq!(setup.sub_state.logs.len(), 1);
}
#[test]
fn can_suicide() {
let refund_account = &Address::new();
let mut setup = TestSetup::new();
let state = setup.state.reference_mut();
{
let mut ext = Externalities::new(state, &setup.env_info, &*setup.engine, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract);
ext.suicide(&refund_account);
}
assert_eq!(setup.sub_state.suicides.len(), 1);
}
}

View File

@@ -260,7 +260,7 @@ pub struct TransactionAddress {
/// Block hash
pub block_hash: H256,
/// Transaction index within the block
pub index: u64
pub index: usize
}
impl ExtrasIndexable for TransactionAddress {

View File

@@ -26,29 +26,29 @@ use externalities::*;
use substate::*;
use tests::helpers::*;
struct TestEngine {
struct TestEngineFrontier {
vm_factory: Factory,
spec: Spec,
max_depth: usize
}
impl TestEngine {
fn new(max_depth: usize, vm_type: VMType) -> TestEngine {
TestEngine {
impl TestEngineFrontier {
fn new(max_depth: usize, vm_type: VMType) -> TestEngineFrontier {
TestEngineFrontier {
vm_factory: Factory::new(vm_type),
spec: ethereum::new_frontier_test(),
max_depth: max_depth
max_depth: max_depth
}
}
}
impl Engine for TestEngine {
impl Engine for TestEngineFrontier {
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 {
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
let mut schedule = Schedule::new_frontier();
schedule.max_depth = self.max_depth;
schedule.max_depth = self.max_depth;
schedule
}
}
@@ -69,12 +69,12 @@ struct TestExt<'a> {
}
impl<'a> TestExt<'a> {
fn new(state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
fn new(state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
substate: &'a mut Substate,
output: OutputPolicy<'a>,
address: Address) -> Self {
TestExt {
@@ -116,13 +116,13 @@ impl<'a> Ext for TestExt<'a> {
ContractCreateResult::Created(self.contract_address.clone(), *gas)
}
fn call(&mut self,
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
fn call(&mut self,
gas: &U256,
_sender_address: &Address,
receive_address: &Address,
value: Option<U256>,
data: &[u8],
_code_address: &Address,
data: &[u8],
_code_address: &Address,
_output: &mut [u8]) -> MessageCallResult {
self.callcreates.push(CallCreate {
data: data.to_vec(),
@@ -136,7 +136,7 @@ impl<'a> Ext for TestExt<'a> {
fn extcode(&self, address: &Address) -> Bytes {
self.ext.extcode(address)
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
self.ext.log(topics, data)
}
@@ -185,11 +185,11 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
// ::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
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();
@@ -209,7 +209,7 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
EnvInfo::from_json(env)
}).unwrap_or_default();
let engine = TestEngine::new(1, vm.clone());
let engine = TestEngineFrontier::new(1, vm.clone());
// params
let mut params = ActionParams::default();
@@ -226,18 +226,18 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
let out_of_gas = test.find("callcreates").map(|_calls| {
}).is_none();
let mut substate = Substate::new();
let mut output = vec![];
// execute
let (res, callcreates) = {
let mut ex = TestExt::new(&mut state,
&info,
&engine,
0,
OriginInfo::from(&params),
&mut substate,
let mut ex = TestExt::new(&mut state,
&info,
&engine,
0,
OriginInfo::from(&params),
&mut substate,
OutputPolicy::Return(BytesRef::Flexible(&mut output)),
params.address.clone());
let evm = engine.vm_factory().create();

View File

@@ -20,7 +20,6 @@ mod test_common;
mod transaction;
mod executive;
mod state;
mod client;
mod chain;
mod homestead_state;
mod homestead_chain;

View File

@@ -56,6 +56,12 @@ mod tests {
use super::*;
use common::*;
#[test]
fn created() {
let sub_state = Substate::new();
assert_eq!(sub_state.suicides.len(), 0);
}
#[test]
fn accrue() {
let mut sub_state = Substate::new();

View File

@@ -15,8 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use client::{BlockChainClient,Client};
use super::test_common::*;
use tests::helpers::*;
use common::*;
#[test]
fn created() {

View File

@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#[cfg(feature = "json-tests")]
use client::{BlockChainClient, Client};
use std::env;
use common::*;
@@ -24,7 +23,9 @@ use std::fs::{remove_dir_all};
use blockchain::{BlockChain};
use state::*;
use rocksdb::*;
use evm::{Schedule, Factory};
use engine::*;
use ethereum;
#[cfg(feature = "json-tests")]
pub enum ChainEra {
@@ -82,6 +83,35 @@ impl<T> GuardedTempResult<T> {
}
}
pub struct TestEngine {
factory: Factory,
spec: Spec,
max_depth: usize
}
impl TestEngine {
pub 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
}
}
pub fn get_test_spec() -> Spec {
Spec::new_test()
}
@@ -134,7 +164,6 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
rlp.out()
}
#[cfg(feature = "json-tests")]
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
let dir = RandomTempPath::new();
@@ -174,7 +203,6 @@ pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>
}
}
#[cfg(feature = "json-tests")]
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();
@@ -271,7 +299,6 @@ pub fn get_good_dummy_block() -> Bytes {
create_test_block(&block_header)
}
#[cfg(feature = "json-tests")]
pub fn get_bad_state_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();

View File

@@ -15,3 +15,4 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub mod helpers;
mod client;

View File

@@ -20,7 +20,7 @@ use util::*;
use error::*;
use evm::Schedule;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
/// Transaction action type.
pub enum Action {
/// Create creates new contract.
@@ -45,7 +45,7 @@ impl Decodable for Action {
/// A set of information describing an externally-originating message call
/// or contract creation operation.
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Transaction {
/// Nonce.
pub nonce: U256,
@@ -158,7 +158,7 @@ impl Transaction {
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq)]
pub struct SignedTransaction {
/// Plain Transaction.
unsigned: Transaction,
@@ -174,6 +174,12 @@ pub struct SignedTransaction {
sender: RefCell<Option<Address>>
}
impl PartialEq for SignedTransaction {
fn eq(&self, other: &SignedTransaction) -> bool {
self.unsigned == other.unsigned && self.v == other.v && self.r == other.r && self.s == other.s
}
}
impl Deref for SignedTransaction {
type Target = Transaction;

View File

@@ -294,6 +294,10 @@ mod tests {
})
}
fn transaction_address(&self, _hash: &H256) -> Option<TransactionAddress> {
unimplemented!()
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option<H256> {
self.numbers.get(&index).cloned()

View File

@@ -155,6 +155,11 @@ impl<'a> BlockView<'a> {
self.rlp.val_at(1)
}
/// Return number of transactions in given block, without deserializing them.
pub fn transactions_count(&self) -> usize {
self.rlp.at(1).iter().count()
}
/// 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()
@@ -170,6 +175,11 @@ impl<'a> BlockView<'a> {
self.rlp.val_at(2)
}
/// Return number of uncles in given block, without deserializing them.
pub fn uncles_count(&self) -> usize {
self.rlp.at(2).iter().count()
}
/// 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()