Merge branch 'master' into close_gently
Conflicts: parity/main.rs
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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(¶ms.code_address) {
|
||||
// if destination is builtin, try to execute it
|
||||
|
||||
|
||||
let default = [];
|
||||
let data = if let Some(ref d) = params.data { d as &[u8] } else { &default as &[u8] };
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(¶ms),
|
||||
&mut substate,
|
||||
let mut ex = TestExt::new(&mut state,
|
||||
&info,
|
||||
&engine,
|
||||
0,
|
||||
OriginInfo::from(¶ms),
|
||||
&mut substate,
|
||||
OutputPolicy::Return(BytesRef::Flexible(&mut output)),
|
||||
params.address.clone());
|
||||
let evm = engine.vm_factory().create();
|
||||
|
||||
@@ -20,7 +20,6 @@ mod test_common;
|
||||
mod transaction;
|
||||
mod executive;
|
||||
mod state;
|
||||
mod client;
|
||||
mod chain;
|
||||
mod homestead_state;
|
||||
mod homestead_chain;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
@@ -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();
|
||||
|
||||
@@ -15,3 +15,4 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod helpers;
|
||||
mod client;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user