Merge branch 'master' into auth-round-no-mocknet

This commit is contained in:
keorn
2016-10-26 17:50:04 +01:00
261 changed files with 3793 additions and 1842 deletions

View File

@@ -96,9 +96,9 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!()
}
fn get(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
}
self.db.get(&combine_key(&self.address_hash, key))
}
@@ -114,7 +114,7 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!()
}
fn emplace(&mut self, _key: H256, _value: Bytes) {
fn emplace(&mut self, _key: H256, _value: DBValue) {
unimplemented!()
}
@@ -122,7 +122,7 @@ impl<'db> HashDB for AccountDB<'db>{
unimplemented!()
}
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
self.db.get_aux(hash)
}
}
@@ -158,9 +158,9 @@ impl<'db> HashDB for AccountDBMut<'db>{
unimplemented!()
}
fn get(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
}
self.db.get(&combine_key(&self.address_hash, key))
}
@@ -178,16 +178,16 @@ impl<'db> HashDB for AccountDBMut<'db>{
}
let k = value.sha3();
let ak = combine_key(&self.address_hash, &k);
self.db.emplace(ak, value.to_vec());
self.db.emplace(ak, DBValue::from_slice(value));
k
}
fn emplace(&mut self, key: H256, value: Bytes) {
fn emplace(&mut self, key: H256, value: DBValue) {
if key == SHA3_NULL_RLP {
return;
}
let key = combine_key(&self.address_hash, &key);
self.db.emplace(key, value.to_vec())
self.db.emplace(key, value)
}
fn remove(&mut self, key: &H256) {
@@ -202,7 +202,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
self.db.insert_aux(hash, value);
}
fn get_aux(&self, hash: &[u8]) -> Option<Vec<u8>> {
fn get_aux(&self, hash: &[u8]) -> Option<DBValue> {
self.db.get_aux(hash)
}
@@ -218,9 +218,9 @@ impl<'db> HashDB for Wrapping<'db> {
unimplemented!()
}
fn get(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
}
self.0.get(key)
}
@@ -236,7 +236,7 @@ impl<'db> HashDB for Wrapping<'db> {
unimplemented!()
}
fn emplace(&mut self, _key: H256, _value: Bytes) {
fn emplace(&mut self, _key: H256, _value: DBValue) {
unimplemented!()
}
@@ -252,9 +252,9 @@ impl<'db> HashDB for WrappingMut<'db>{
unimplemented!()
}
fn get(&self, key: &H256) -> Option<&[u8]> {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
return Some(DBValue::from_slice(&NULL_RLP_STATIC));
}
self.0.get(key)
}
@@ -273,7 +273,7 @@ impl<'db> HashDB for WrappingMut<'db>{
self.0.insert(value)
}
fn emplace(&mut self, key: H256, value: Bytes) {
fn emplace(&mut self, key: H256, value: DBValue) {
if key == SHA3_NULL_RLP {
return;
}
@@ -286,4 +286,4 @@ impl<'db> HashDB for WrappingMut<'db>{
}
self.0.remove(key)
}
}
}

View File

@@ -265,6 +265,20 @@ impl AccountProvider {
Ok(())
}
/// Returns `true` if the password for `account` is `password`. `false` if not.
pub fn test_password(&self, account: &Address, password: String) -> Result<bool, Error> {
match self.sstore.sign(&account, &password, &Default::default()) {
Ok(_) => Ok(true),
Err(SSError::InvalidPassword) => Ok(false),
Err(e) => Err(Error::SStore(e)),
}
}
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
self.sstore.change_password(&account, &password, &new_password).map_err(Error::SStore)
}
/// Helper method used for unlocking accounts.
fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> {
// verify password by signing dump message

View File

@@ -15,10 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params.
use common::*;
use util::{Address, Bytes, Uint, U256};
use util::hash::{H256, FixedHash};
use util::sha3::{Hashable, SHA3_EMPTY};
use ethjson;
use types::executed::CallType;
use std::sync::Arc;
/// Transaction value
#[derive(Clone, Debug)]
pub enum ActionValue {

View File

@@ -16,7 +16,7 @@
//! Ethcore basic typenames.
use util::*;
use util::hash::H2048;
/// Type for a 2048-bit log-bloom, as used by our blocks.
pub type LogBloom = H2048;

View File

@@ -587,10 +587,17 @@ pub fn enact_verified(
mod tests {
use tests::helpers::*;
use super::*;
use common::*;
use engines::Engine;
use env_info::LastHashes;
use error::Error;
use header::Header;
use factory::Factories;
use state_db::StateDB;
use views::BlockView;
use util::Address;
use util::hash::FixedHash;
use std::sync::Arc;
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
#[cfg_attr(feature="dev", allow(too_many_arguments))]

View File

@@ -394,6 +394,8 @@ impl BlockProvider for BlockChain {
}
}
/// An iterator which walks the blockchain towards the genesis.
#[derive(Clone)]
pub struct AncestryIter<'a> {
current: H256,
chain: &'a BlockChain,
@@ -403,11 +405,10 @@ impl<'a> Iterator for AncestryIter<'a> {
type Item = H256;
fn next(&mut self) -> Option<H256> {
if self.current.is_zero() {
Option::None
None
} else {
let mut n = self.chain.block_details(&self.current).unwrap().parent;
mem::swap(&mut self.current, &mut n);
Some(n)
self.chain.block_details(&self.current)
.map(|details| mem::replace(&mut self.current, details.parent))
}
}
}
@@ -999,17 +1000,29 @@ impl BlockChain {
if !self.is_known(parent) { return None; }
let mut excluded = HashSet::new();
for a in self.ancestry_iter(parent.clone()).unwrap().take(uncle_generations) {
excluded.extend(self.uncle_hashes(&a).unwrap().into_iter());
excluded.insert(a);
let ancestry = match self.ancestry_iter(parent.clone()) {
Some(iter) => iter,
None => return None,
};
for a in ancestry.clone().take(uncle_generations) {
if let Some(uncles) = self.uncle_hashes(&a) {
excluded.extend(uncles);
excluded.insert(a);
} else {
break
}
}
let mut ret = Vec::new();
for a in self.ancestry_iter(parent.clone()).unwrap().skip(1).take(uncle_generations) {
ret.extend(self.block_details(&a).unwrap().children.iter()
.filter(|h| !excluded.contains(h))
);
for a in ancestry.skip(1).take(uncle_generations) {
if let Some(details) = self.block_details(&a) {
ret.extend(details.children.iter().filter(|h| !excluded.contains(h)))
} else {
break
}
}
Some(ret)
}

View File

@@ -204,7 +204,7 @@ impl Client {
let engine = spec.engine.clone();
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone());
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal());
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);

View File

@@ -117,6 +117,8 @@ pub struct ClientConfig {
pub jump_table_size: usize,
/// State pruning history size.
pub history: u64,
/// Check seal valididity on block import
pub check_seal: bool,
}
#[cfg(test)]

View File

@@ -37,6 +37,7 @@ pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
pub use verification::VerifierType;
/// IPC interfaces
#[cfg(feature="ipc")]

View File

@@ -194,15 +194,20 @@ pub trait BlockChainClient : Sync + Send {
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
let mut h = self.chain_info().best_block_hash;
let mut corpus = Vec::new();
for _ in 0..sample_size {
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let block = BlockView::new(&block_bytes);
let header = block.header_view();
if header.number() == 0 {
break;
while corpus.is_empty() {
for _ in 0..sample_size {
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
let block = BlockView::new(&block_bytes);
let header = block.header_view();
if header.number() == 0 {
if corpus.is_empty() {
corpus.push(20_000_000_000u64.into()); // we have literally no information - it' as good a number as any.
}
break;
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
}
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
h = header.parent_hash().clone();
}
corpus.sort();
let n = corpus.len();

View File

@@ -1,27 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub use util::*;
pub use basic_types::*;
pub use error::*;
pub use env_info::*;
pub use views::*;
pub use builtin::*;
pub use header::*;
pub use transaction::*;
pub use log_entry::*;
pub use receipt::*;
pub use action_params::*;

View File

@@ -19,17 +19,22 @@
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration};
use common::*;
use util::*;
use ethkey::verify_address;
use rlp::{UntrustedRlp, View, encode, decode};
use account_provider::AccountProvider;
use block::*;
use spec::CommonParams;
use engines::Engine;
use header::Header;
use error::{Error, BlockError};
use evm::Schedule;
use ethjson;
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
use service::ClientIoMessage;
use transaction::SignedTransaction;
use env_info::EnvInfo;
use builtin::Builtin;
/// `AuthorityRound` params.
#[derive(Debug, PartialEq)]

View File

@@ -16,14 +16,20 @@
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
use common::*;
use ethkey::{recover, public_to_address};
use account_provider::AccountProvider;
use block::*;
use builtin::Builtin;
use spec::CommonParams;
use engines::Engine;
use env_info::EnvInfo;
use error::{BlockError, Error};
use evm::Schedule;
use ethjson;
use header::Header;
use transaction::SignedTransaction;
use util::*;
/// `BasicAuthority` params.
#[derive(Debug, PartialEq)]
@@ -177,10 +183,13 @@ impl Engine for BasicAuthority {
#[cfg(test)]
mod tests {
use common::*;
use util::*;
use block::*;
use env_info::EnvInfo;
use error::{BlockError, Error};
use tests::helpers::*;
use account_provider::AccountProvider;
use header::Header;
use spec::Spec;
/// Create a new test chain spec with `BasicAuthority` consensus engine.

View File

@@ -18,11 +18,11 @@ use std::collections::BTreeMap;
use util::Address;
use builtin::Builtin;
use engines::Engine;
use env_info::EnvInfo;
use spec::CommonParams;
use evm::Schedule;
use env_info::EnvInfo;
use block::ExecutedBlock;
use common::Bytes;
use util::Bytes;
use account_provider::AccountProvider;
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
@@ -67,10 +67,11 @@ impl Engine for InstantSeal {
#[cfg(test)]
mod tests {
use common::*;
use util::*;
use tests::helpers::*;
use account_provider::AccountProvider;
use spec::Spec;
use header::Header;
use block::*;
#[test]

View File

@@ -26,13 +26,18 @@ pub use self::instant_seal::InstantSeal;
pub use self::basic_authority::BasicAuthority;
pub use self::authority_round::AuthorityRound;
use common::*;
use util::*;
use account_provider::AccountProvider;
use block::ExecutedBlock;
use builtin::Builtin;
use env_info::EnvInfo;
use error::Error;
use spec::CommonParams;
use evm::Schedule;
use io::IoChannel;
use service::ClientIoMessage;
use header::Header;
use transaction::SignedTransaction;
/// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based.
/// Provides hooks into each of the major parts of block import.

View File

@@ -15,9 +15,14 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256};
use common::*;
use util::*;
use block::*;
use builtin::Builtin;
use env_info::EnvInfo;
use error::{BlockError, Error};
use header::Header;
use spec::CommonParams;
use transaction::SignedTransaction;
use engines::Engine;
use evm::Schedule;
use ethjson;
@@ -187,8 +192,8 @@ impl Engine for Ethash {
// Commit state so that we can actually figure out the state root.
if let Err(e) = fields.state.commit() {
warn!("Encountered error on state commit: {}", e);
}
warn!("Encountered error on state commit: {}", e);
}
}
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
@@ -371,9 +376,12 @@ impl Header {
#[cfg(test)]
mod tests {
use common::*;
use util::*;
use block::*;
use tests::helpers::*;
use env_info::EnvInfo;
use error::{BlockError, Error};
use header::Header;
use super::super::new_morden;
use super::Ethash;
use rlp;

View File

@@ -65,10 +65,11 @@ pub fn new_morden() -> Spec { load(include_bytes!("../../res/ethereum/morden.jso
#[cfg(test)]
mod tests {
use common::*;
use util::*;
use state::*;
use super::*;
use tests::helpers::*;
use views::BlockView;
#[test]
fn ensure_db_good() {

View File

@@ -24,7 +24,8 @@ extern crate test;
use self::test::{Bencher, black_box};
use common::*;
use util::*;
use action_params::ActionParams;
use evm::{self, Factory, VMType};
use evm::tests::FakeExt;

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*;
use util::*;
use super::u256_to_address;
use evm::{self, CostType};
use evm::instructions::{self, Instruction, InstructionInfo};
@@ -64,14 +64,14 @@ impl<Gas: CostType> Gasometer<Gas> {
Some(cap_divisor) if self.current_gas >= needed => {
let gas_remaining = self.current_gas - needed;
let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor);
if let Some(Ok(r)) = requested {
if let Some(Ok(r)) = requested {
Ok(min(r, max_gas_provided))
} else {
Ok(max_gas_provided)
}
},
_ => {
if let Some(r) = requested {
if let Some(r) = requested {
r
} else if self.current_gas >= needed {
Ok(self.current_gas - needed)
@@ -84,10 +84,10 @@ impl<Gas: CostType> Gasometer<Gas> {
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
/// Determine how much gas is used by the given instruction, given the machine's state.
///
///
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
/// it will be the amount of gas that the current context provides to the child context.
/// it will be the amount of gas that the current context provides to the child context.
pub fn get_gas_cost_mem(
&mut self,
ext: &evm::Ext,
@@ -183,7 +183,7 @@ impl<Gas: CostType> Gasometer<Gas> {
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
};
// TODO: refactor to avoid duplicate calculation here and later on.
// TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let requested = Gas::from_u256(*stack.peek(0));
@@ -199,7 +199,7 @@ impl<Gas: CostType> Gasometer<Gas> {
try!(mem_needed(stack.peek(2), stack.peek(3)))
);
// TODO: refactor to avoid duplicate calculation here and later on.
// TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let requested = Gas::from_u256(*stack.peek(0));
@@ -212,9 +212,9 @@ impl<Gas: CostType> Gasometer<Gas> {
let mut gas = Gas::from(schedule.create_gas);
let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
// TODO: refactor to avoid duplicate calculation here and later on.
// TODO: refactor to avoid duplicate calculation here and later on.
let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into()));
let provided = try!(self.gas_provided(schedule, cost_so_far, None));
gas = overflowing!(gas.overflow_add(provided));

View File

@@ -29,12 +29,14 @@ use self::memory::Memory;
pub use self::shared_cache::SharedCache;
use std::marker::PhantomData;
use common::*;
use action_params::{ActionParams, ActionValue};
use types::executed::CallType;
use super::instructions::{self, Instruction, InstructionInfo};
use evm::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
use bit_set::BitSet;
use util::*;
type CodePosition = usize;
type ProgramCounter = usize;

View File

@@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Just in time compiler execution environment.
use common::*;
use util::*;
use evmjit;
use evm::{self, GasLeft};
use types::executed::CallType;

View File

@@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use common::*;
use util::*;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult};
use std::fmt::Debug;

View File

@@ -15,13 +15,17 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Transaction Execution environment.
use common::*;
use util::*;
use action_params::{ActionParams, ActionValue};
use state::{State, Substate};
use engines::Engine;
use types::executed::CallType;
use env_info::EnvInfo;
use error::ExecutionError;
use evm::{self, Ext, Factory, Finalize};
use externalities::*;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use transaction::{Action, SignedTransaction};
use crossbeam;
pub use types::executed::{Executed, ExecutionResult};
@@ -250,7 +254,7 @@ impl<'a> Executive<'a> {
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas
self.state.snapshot();
self.state.checkpoint();
// at first, transfer value to destination
if let ActionValue::Transfer(val) = params.value {
@@ -269,7 +273,7 @@ impl<'a> Executive<'a> {
let cost = self.engine.cost_of_builtin(&params.code_address, data);
if cost <= params.gas {
self.engine.execute_builtin(&params.code_address, data, &mut output);
self.state.discard_snapshot();
self.state.discard_checkpoint();
// trace only top level calls to builtins to avoid DDoS attacks
if self.depth == 0 {
@@ -289,7 +293,7 @@ impl<'a> Executive<'a> {
Ok(params.gas - cost)
} else {
// just drain the whole gas
self.state.revert_to_snapshot();
self.state.revert_to_checkpoint();
tracer.trace_failed_call(trace_info, vec![], evm::Error::OutOfGas.into());
@@ -335,7 +339,7 @@ impl<'a> Executive<'a> {
res
} else {
// otherwise it's just a basic transaction, only do tracing, if necessary.
self.state.discard_snapshot();
self.state.discard_checkpoint();
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
Ok(params.gas)
@@ -354,7 +358,7 @@ impl<'a> Executive<'a> {
vm_tracer: &mut V
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
// backup used in case of running out of gas
self.state.snapshot();
self.state.checkpoint();
// part of substate that may be reverted
let mut unconfirmed_substate = Substate::new();
@@ -485,10 +489,10 @@ impl<'a> Executive<'a> {
| Err(evm::Error::BadInstruction {.. })
| Err(evm::Error::StackUnderflow {..})
| Err(evm::Error::OutOfStack {..}) => {
self.state.revert_to_snapshot();
self.state.revert_to_checkpoint();
},
Ok(_) | Err(evm::Error::Internal) => {
self.state.discard_snapshot();
self.state.discard_checkpoint();
substate.accrue(un_substate);
}
}
@@ -500,13 +504,18 @@ impl<'a> Executive<'a> {
mod tests {
use ethkey::{Generator, Random};
use super::*;
use common::*;
use util::*;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
use evm::{Factory, VMType};
use error::ExecutionError;
use state::Substate;
use tests::helpers::*;
use trace::trace;
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer};
use trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer};
use transaction::{Action, Transaction};
use types::executed::CallType;
#[test]

View File

@@ -15,9 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Transaction Execution environment.
use common::*;
use util::*;
use action_params::{ActionParams, ActionValue};
use state::{State, Substate};
use engines::Engine;
use env_info::EnvInfo;
use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use types::executed::CallType;
@@ -253,6 +255,8 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) {
use log_entry::LogEntry;
let address = self.origin_info.address.clone();
self.substate.logs.push(LogEntry {
address: address,
@@ -303,8 +307,9 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
#[cfg(test)]
mod tests {
use common::*;
use util::*;
use engines::Engine;
use env_info::EnvInfo;
use evm::Ext;
use state::{State, Substate};
use tests::helpers::*;

View File

@@ -17,7 +17,7 @@
//! Block header.
use util::*;
use basic_types::*;
use basic_types::{LogBloom, Seal, ZERO_LOGBLOOM};
use time::get_time;
use rlp::*;

View File

@@ -15,9 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use super::test_common::*;
use action_params::ActionParams;
use state::{State, Substate};
use executive::*;
use engines::Engine;
use env_info::EnvInfo;
use evm;
use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult};
use externalities::*;

View File

@@ -37,6 +37,5 @@ declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/
declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"}
declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"}
declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"}
// TODO [ToDr] uncomment as soon as eip150 tests are merged to develop branch of ethereum/tests
// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"}
declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"}

View File

@@ -17,6 +17,7 @@
use super::test_common::*;
use tests::helpers::*;
use pod_state::{self, PodState};
use log_entry::LogEntry;
use ethereum;
use ethjson;

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub use common::*;
pub use util::*;
macro_rules! test {
($name: expr) => {

View File

@@ -18,6 +18,7 @@ use super::test_common::*;
use evm;
use ethjson;
use rlp::{UntrustedRlp, View};
use transaction::{Action, SignedTransaction};
fn do_json_test(json_data: &[u8]) -> Vec<String> {
let tests = ethjson::transaction::Test::load(json_data).unwrap();

View File

@@ -140,7 +140,6 @@ pub mod db;
mod cache_manager;
mod blooms;
mod common;
mod basic_types;
mod env_info;
mod pod_account;

View File

@@ -154,7 +154,7 @@ impl OverlayRecentV7 {
// and commit the altered entries.
fn migrate_journal(&self, source: Arc<Database>, mut batch: Batch, dest: &mut Database) -> Result<(), Error> {
if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) {
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest));
try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest));
let mut era = decode::<u64>(&val);
loop {

View File

@@ -163,7 +163,7 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
#[cfg(test)]
mod test {
use common::*;
use util::*;
use types::account_diff::*;
use super::{PodAccount, diff_pod};

View File

@@ -77,7 +77,7 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff {
#[cfg(test)]
mod test {
use common::*;
use util::*;
use types::state_diff::*;
use types::account_diff::*;
use pod_account::PodAccount;

View File

@@ -19,7 +19,7 @@
use account_db::{AccountDB, AccountDBMut};
use snapshot::Error;
use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
use util::{U256, FixedHash, H256, Bytes, HashDB, DBValue, SHA3_EMPTY, SHA3_NULL_RLP};
use util::trie::{TrieDB, Trie};
use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
@@ -112,7 +112,7 @@ impl Account {
let mut stream = RlpStream::new_list(pairs.len());
for (k, v) in pairs {
stream.begin_list(2).append(&k).append(&v);
stream.begin_list(2).append(&k).append(&&*v);
}
let pairs_rlp = stream.out();
@@ -130,7 +130,7 @@ impl Account {
match acct_db.get(&self.code_hash) {
Some(c) => {
used_code.insert(self.code_hash.clone());
account_stream.append(&CodeState::Inline.raw()).append(&c);
account_stream.append(&CodeState::Inline.raw()).append(&&*c);
}
None => {
warn!("code lookup failed during snapshot");
@@ -178,7 +178,7 @@ impl Account {
CodeState::Hash => {
let code_hash = try!(rlp.val_at(3));
if let Some(code) = code_map.get(&code_hash) {
acct_db.emplace(code_hash.clone(), code.clone());
acct_db.emplace(code_hash.clone(), DBValue::from_slice(&code));
}
(code_hash, None)
@@ -226,7 +226,7 @@ mod tests {
use snapshot::tests::helpers::fill_storage;
use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP};
use util::{Address, FixedHash, H256, HashDB};
use util::{Address, FixedHash, H256, HashDB, DBValue};
use rlp::{UntrustedRlp, View};
use std::collections::{HashSet, HashMap};
@@ -292,7 +292,7 @@ mod tests {
{
let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr2);
acct_db.emplace(code_hash.clone(), b"this is definitely code".to_vec());
acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code"));
}
let account1 = Account {

View File

@@ -29,8 +29,7 @@ use engines::Engine;
use ids::BlockID;
use views::BlockView;
use util::{Bytes, Hashable, HashDB, snappy, U256, Uint};
use util::memorydb::MemoryDB;
use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint};
use util::Mutex;
use util::hash::{FixedHash, H256};
use util::journaldb::{self, Algorithm, JournalDB};
@@ -38,6 +37,7 @@ use util::kvdb::Database;
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
use util::sha3::SHA3_NULL_RLP;
use rlp::{RlpStream, Stream, UntrustedRlp, View};
use bloom_journal::Bloom;
use self::account::Account;
use self::block::AbridgedBlock;
@@ -46,7 +46,7 @@ use self::io::SnapshotWriter;
use super::state_db::StateDB;
use super::state::Account as StateAccount;
use crossbeam::{scope, ScopedJoinHandle};
use crossbeam::scope;
use rand::{Rng, OsRng};
pub use self::error::Error;
@@ -136,7 +136,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
let writer = Mutex::new(writer);
let (state_hashes, block_hashes) = try!(scope(|scope| {
let block_guard = scope.spawn(|| chunk_blocks(chain, (number, block_at), &writer, p));
let block_guard = scope.spawn(|| chunk_blocks(chain, block_at, &writer, p));
let state_res = chunk_state(state_db, state_root, &writer, p);
state_res.and_then(|state_hashes| {
@@ -176,10 +176,15 @@ struct BlockChunker<'a> {
impl<'a> BlockChunker<'a> {
// Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash.
// Loops until we reach the first desired block, and writes out the remainder.
fn chunk_all(&mut self, first_hash: H256) -> Result<(), Error> {
fn chunk_all(&mut self) -> Result<(), Error> {
let mut loaded_size = 0;
let mut last = self.current_hash;
let genesis_hash = self.chain.genesis_hash();
for _ in 0..SNAPSHOT_BLOCKS {
if self.current_hash == genesis_hash { break }
while self.current_hash != first_hash {
let (block, receipts) = try!(self.chain.block(&self.current_hash)
.and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r)))
.ok_or(Error::BlockNotFound(self.current_hash)));
@@ -197,21 +202,21 @@ impl<'a> BlockChunker<'a> {
// cut off the chunk if too large.
if new_loaded_size > PREFERRED_CHUNK_SIZE {
try!(self.write_chunk());
if new_loaded_size > PREFERRED_CHUNK_SIZE && self.rlps.len() > 0 {
try!(self.write_chunk(last));
loaded_size = pair.len();
} else {
loaded_size = new_loaded_size;
}
self.rlps.push_front(pair);
last = self.current_hash;
self.current_hash = view.header_view().parent_hash();
}
if loaded_size != 0 {
// we don't store the first block, so once we get to this point,
// the "first" block will be first_number + 1.
try!(self.write_chunk());
try!(self.write_chunk(last));
}
Ok(())
@@ -219,23 +224,24 @@ impl<'a> BlockChunker<'a> {
// write out the data in the buffers to a chunk on disk
//
// we preface each chunk with the parent of the first block's details.
fn write_chunk(&mut self) -> Result<(), Error> {
// since the block we're inspecting now doesn't go into the
// chunk if it's too large, the current hash is the parent hash
// for the first block in that chunk.
let parent_hash = self.current_hash;
// we preface each chunk with the parent of the first block's details,
// obtained from the details of the last block written.
fn write_chunk(&mut self, last: H256) -> Result<(), Error> {
trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len());
let (parent_number, parent_details) = try!(self.chain.block_number(&parent_hash)
.and_then(|n| self.chain.block_details(&parent_hash).map(|d| (n, d)))
.ok_or(Error::BlockNotFound(parent_hash)));
let parent_total_difficulty = parent_details.total_difficulty;
let (last_header, last_details) = try!(self.chain.block_header(&last)
.and_then(|n| self.chain.block_details(&last).map(|d| (n, d)))
.ok_or(Error::BlockNotFound(last)));
let parent_number = last_header.number() - 1;
let parent_hash = last_header.parent_hash();
let parent_total_difficulty = last_details.total_difficulty - *last_header.difficulty();
trace!(target: "snapshot", "parent last written block: {}", parent_hash);
let num_entries = self.rlps.len();
let mut rlp_stream = RlpStream::new_list(3 + num_entries);
rlp_stream.append(&parent_number).append(&parent_hash).append(&parent_total_difficulty);
rlp_stream.append(&parent_number).append(parent_hash).append(&parent_total_difficulty);
for pair in self.rlps.drain(..) {
rlp_stream.append_raw(&pair, 1);
@@ -264,17 +270,7 @@ impl<'a> BlockChunker<'a> {
/// The path parameter is the directory to store the block chunks in.
/// This function assumes the directory exists already.
/// Returns a list of chunk hashes, with the first having the blocks furthest from the genesis.
pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
let (start_number, start_hash) = start_block_info;
let first_hash = if start_number < SNAPSHOT_BLOCKS {
// use the genesis hash.
chain.genesis_hash()
} else {
let first_num = start_number - SNAPSHOT_BLOCKS;
try!(chain.block_hash(first_num).ok_or(Error::IncompleteChain))
};
pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_hash: H256, writer: &Mutex<SnapshotWriter + 'a>, progress: &'a Progress) -> Result<Vec<H256>, Error> {
let mut chunker = BlockChunker {
chain: chain,
rlps: VecDeque::new(),
@@ -285,7 +281,7 @@ pub fn chunk_blocks<'a>(chain: &'a BlockChain, start_block_info: (u64, H256), wr
progress: progress,
};
try!(chunker.chunk_all(first_hash));
try!(chunker.chunk_all());
Ok(chunker.hashes)
}
@@ -372,7 +368,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
// account_key here is the address' hash.
for item in try!(account_trie.iter()) {
let (account_key, account_data) = try!(item);
let account = Account::from_thin_rlp(account_data);
let account = Account::from_thin_rlp(&*account_data);
let account_key_hash = H256::from_slice(&account_key);
let account_db = AccountDB::from_hash(db, account_key_hash);
@@ -394,6 +390,7 @@ pub struct StateRebuilder {
state_root: H256,
code_map: HashMap<H256, Bytes>, // maps code hashes to code itself.
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
bloom: Bloom,
}
impl StateRebuilder {
@@ -404,6 +401,7 @@ impl StateRebuilder {
state_root: SHA3_NULL_RLP,
code_map: HashMap::new(),
missing_code: HashMap::new(),
bloom: StateDB::load_bloom(&*db),
}
}
@@ -422,43 +420,19 @@ impl StateRebuilder {
// new code contained within this chunk.
let mut chunk_code = HashMap::new();
// build account tries in parallel.
// Todo [rob] keep a thread pool around so we don't do this per-chunk.
try!(scope(|scope| {
let mut handles = Vec::new();
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
let code_map = &self.code_map;
let handle: ScopedJoinHandle<Result<_, ::error::Error>> = scope.spawn(move || {
let mut db = MemoryDB::new();
let status = try!(rebuild_accounts(&mut db, account_chunk, out_pairs_chunk, code_map));
trace!(target: "snapshot", "thread rebuilt {} account tries", account_chunk.len());
Ok((db, status))
});
handles.push(handle);
for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) {
let code_map = &self.code_map;
let status = try!(rebuild_accounts(self.db.as_hashdb_mut(), account_chunk, out_pairs_chunk, code_map));
chunk_code.extend(status.new_code);
for (addr_hash, code_hash) in status.missing_code {
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
}
// consolidate all edits into the main overlay.
for handle in handles {
let (thread_db, status): (MemoryDB, _) = try!(handle.join());
self.db.consolidate(thread_db);
chunk_code.extend(status.new_code);
for (addr_hash, code_hash) in status.missing_code {
self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash);
}
}
Ok::<_, ::error::Error>(())
}));
}
// patch up all missing code. must be done after collecting all new missing code entries.
for (code_hash, code) in chunk_code {
for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) {
let mut db = AccountDBMut::from_hash(self.db.as_hashdb_mut(), addr_hash);
db.emplace(code_hash, code.clone());
db.emplace(code_hash, DBValue::from_slice(&code));
}
self.code_map.insert(code_hash, code);
@@ -466,9 +440,6 @@ impl StateRebuilder {
let backing = self.db.backing().clone();
// bloom has to be updated
let mut bloom = StateDB::load_bloom(&backing);
// batch trie writes
{
let mut account_trie = if self.state_root != SHA3_NULL_RLP {
@@ -479,17 +450,17 @@ impl StateRebuilder {
for (hash, thin_rlp) in pairs {
if &thin_rlp[..] != &empty_rlp[..] {
bloom.set(&*hash);
self.bloom.set(&*hash);
}
try!(account_trie.insert(&hash, &thin_rlp));
}
}
let bloom_journal = bloom.drain_journal();
let bloom_journal = self.bloom.drain_journal();
let mut batch = backing.transaction();
try!(StateDB::commit_bloom(&mut batch, bloom_journal));
try!(self.db.inject(&mut batch));
try!(backing.write(batch).map_err(::util::UtilError::SimpleString));
backing.write_buffered(batch);
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
Ok(())
}
@@ -596,7 +567,7 @@ impl BlockRebuilder {
let rlp = UntrustedRlp::new(chunk);
let item_count = rlp.item_count();
trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 2);
trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3);
// todo: assert here that these values are consistent with chunks being in order.
let mut cur_number = try!(rlp.val_at::<u64>(0)) + 1;
@@ -632,7 +603,7 @@ impl BlockRebuilder {
} else {
self.chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, is_best, false);
}
self.db.write(batch).expect("Error writing to the DB");
self.db.write_buffered(batch);
self.chain.commit();
parent_hash = BlockView::new(&block_bytes).hash();

View File

@@ -74,6 +74,7 @@ struct Restoration {
snappy_buffer: Bytes,
final_state_root: H256,
guard: Guard,
db: Arc<Database>,
}
struct RestorationParams<'a> {
@@ -105,12 +106,13 @@ impl Restoration {
manifest: manifest,
state_chunks_left: state_chunks,
block_chunks_left: block_chunks,
state: StateRebuilder::new(raw_db, params.pruning),
state: StateRebuilder::new(raw_db.clone(), params.pruning),
blocks: blocks,
writer: params.writer,
snappy_buffer: Vec::new(),
final_state_root: root,
guard: params.guard,
db: raw_db,
})
}
@@ -467,39 +469,47 @@ impl Service {
/// Feed a chunk of either kind. no-op if no restoration or status is wrong.
fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) -> Result<(), Error> {
// TODO: be able to process block chunks and state chunks at same time?
let mut restoration = self.restoration.lock();
let (result, db) = {
let mut restoration = self.restoration.lock();
match self.status() {
RestorationStatus::Inactive | RestorationStatus::Failed => Ok(()),
RestorationStatus::Ongoing { .. } => {
let res = {
let rest = match *restoration {
Some(ref mut r) => r,
None => return Ok(()),
};
match is_state {
true => rest.feed_state(hash, chunk),
false => rest.feed_blocks(hash, chunk, &*self.engine),
}.map(|_| rest.is_done())
};
match res {
Ok(is_done) => {
match is_state {
true => self.state_chunks.fetch_add(1, Ordering::SeqCst),
false => self.block_chunks.fetch_add(1, Ordering::SeqCst),
match self.status() {
RestorationStatus::Inactive | RestorationStatus::Failed => return Ok(()),
RestorationStatus::Ongoing { .. } => {
let (res, db) = {
let rest = match *restoration {
Some(ref mut r) => r,
None => return Ok(()),
};
match is_done {
true => self.finalize_restoration(&mut *restoration),
false => Ok(())
(match is_state {
true => rest.feed_state(hash, chunk),
false => rest.feed_blocks(hash, chunk, &*self.engine),
}.map(|_| rest.is_done()), rest.db.clone())
};
let res = match res {
Ok(is_done) => {
match is_state {
true => self.state_chunks.fetch_add(1, Ordering::SeqCst),
false => self.block_chunks.fetch_add(1, Ordering::SeqCst),
};
match is_done {
true => {
try!(db.flush().map_err(::util::UtilError::SimpleString));
drop(db);
return self.finalize_restoration(&mut *restoration);
},
false => Ok(())
}
}
}
other => other.map(drop),
other => other.map(drop),
};
(res, db)
}
}
}
};
result.and_then(|_| db.flush().map_err(|e| ::util::UtilError::SimpleString(e).into()))
}
/// Feed a state chunk to be processed synchronously.
@@ -549,8 +559,9 @@ impl SnapshotService for Service {
}
fn begin_restore(&self, manifest: ManifestData) {
self.io_channel.send(ClientIoMessage::BeginRestoration(manifest))
.expect("snapshot service and io service are kept alive by client service; qed");
if let Err(e) = self.io_channel.send(ClientIoMessage::BeginRestoration(manifest)) {
trace!("Error sending snapshot service message: {:?}", e);
}
}
fn abort_restore(&self) {
@@ -559,13 +570,15 @@ impl SnapshotService for Service {
}
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk))
.expect("snapshot service and io service are kept alive by client service; qed");
if let Err(e) = self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk)) {
trace!("Error sending snapshot service message: {:?}", e);
}
}
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk))
.expect("snapshot service and io service are kept alive by client service; qed");
if let Err(e) = self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk)) {
trace!("Error sending snapshot service message: {:?}", e);
}
}
}

View File

@@ -57,7 +57,7 @@ fn chunk_and_restore(amount: u64) {
// snapshot it.
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
let block_hashes = chunk_blocks(&bc, (amount, best_hash), &writer, &Progress::default()).unwrap();
let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
state_hashes: Vec::new(),
block_hashes: block_hashes,

View File

@@ -21,6 +21,7 @@ use account_db::AccountDBMut;
use rand::Rng;
use snapshot::account::Account;
use util::DBValue;
use util::hash::{FixedHash, H256};
use util::hashdb::HashDB;
use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode};
@@ -66,7 +67,7 @@ impl StateProducer {
let mut account = Account::from_thin_rlp(&*account_data);
let acct_db = AccountDBMut::from_hash(db, *address_hash);
fill_storage(acct_db, account.storage_root_mut(), &mut self.storage_seed);
*account_data = account.to_thin_rlp();
*account_data = DBValue::from_vec(account.to_thin_rlp());
}
// sweep again to alter account trie.

View File

@@ -16,10 +16,12 @@
//! Parameters for a block chain.
use common::*;
use util::*;
use builtin::Builtin;
use engines::{Engine, NullEngine, InstantSeal, BasicAuthority, AuthorityRound};
use pod_state::*;
use account_db::*;
use header::{BlockNumber, Header};
use state_db::StateDB;
use super::genesis::Genesis;
use super::seal::Generic as GenericSeal;

View File

@@ -172,7 +172,7 @@ impl Account {
using it will not fail.");
let item: U256 = match db.get(key){
Ok(x) => x.map_or_else(U256::zero, decode),
Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)),
Err(e) => panic!("Encountered potential DB corruption: {}", e),
};
let value: H256 = item.into();
@@ -253,8 +253,8 @@ impl Account {
self.is_cached() ||
match db.get(&self.code_hash) {
Some(x) => {
self.code_cache = Arc::new(x.to_vec());
self.code_size = Some(x.len());
self.code_cache = Arc::new(x.to_vec());
true
},
_ => {
@@ -351,7 +351,7 @@ impl Account {
self.code_filth = Filth::Clean;
},
(true, false) => {
db.emplace(self.code_hash.clone(), (*self.code_cache).clone());
db.emplace(self.code_hash.clone(), DBValue::from_slice(&*self.code_cache));
self.code_size = Some(self.code_cache.len());
self.code_filth = Filth::Clean;
},

View File

@@ -16,14 +16,18 @@
use std::cell::{RefCell, RefMut};
use std::collections::hash_map::Entry;
use common::*;
use util::*;
use receipt::Receipt;
use engines::Engine;
use env_info::EnvInfo;
use error::Error;
use executive::{Executive, TransactOptions};
use factory::Factories;
use trace::FlatTrace;
use pod_account::*;
use pod_state::{self, PodState};
use types::state_diff::StateDiff;
use transaction::SignedTransaction;
use state_db::StateDB;
mod account;
@@ -167,24 +171,24 @@ impl AccountEntry {
/// Upon destruction all the local cache data propagated into the global cache.
/// Propagated items might be rejected if current state is non-canonical.
///
/// State snapshotting.
/// State checkpointing.
///
/// A new snapshot can be created with `snapshot()`. Snapshots can be
/// A new checkpoint can be created with `checkpoint()`. checkpoints can be
/// created in a hierarchy.
/// When a snapshot is active all changes are applied directly into
/// `cache` and the original value is copied into an active snapshot.
/// Reverting a snapshot with `revert_to_snapshot` involves copying
/// original values from the latest snapshot back into `cache`. The code
/// When a checkpoint is active all changes are applied directly into
/// `cache` and the original value is copied into an active checkpoint.
/// Reverting a checkpoint with `revert_to_checkpoint` involves copying
/// original values from the latest checkpoint back into `cache`. The code
/// takes care not to overwrite cached storage while doing that.
/// Snapshot can be discateded with `discard_snapshot`. All of the orignal
/// backed-up values are moved into a parent snapshot (if any).
/// checkpoint can be discateded with `discard_checkpoint`. All of the orignal
/// backed-up values are moved into a parent checkpoint (if any).
///
pub struct State {
db: StateDB,
root: H256,
cache: RefCell<HashMap<Address, AccountEntry>>,
// The original account is preserved in
snapshots: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
checkpoints: RefCell<Vec<HashMap<Address, Option<AccountEntry>>>>,
account_start_nonce: U256,
factories: Factories,
}
@@ -213,7 +217,7 @@ impl State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce,
factories: factories,
}
@@ -229,7 +233,7 @@ impl State {
db: db,
root: root,
cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce,
factories: factories
};
@@ -237,21 +241,21 @@ impl State {
Ok(state)
}
/// Create a recoverable snaphot of this state.
pub fn snapshot(&mut self) {
self.snapshots.get_mut().push(HashMap::new());
/// Create a recoverable checkpoint of this state.
pub fn checkpoint(&mut self) {
self.checkpoints.get_mut().push(HashMap::new());
}
/// Merge last snapshot with previous.
pub fn discard_snapshot(&mut self) {
// merge with previous snapshot
let last = self.snapshots.get_mut().pop();
if let Some(mut snapshot) = last {
if let Some(ref mut prev) = self.snapshots.get_mut().last_mut() {
/// Merge last checkpoint with previous.
pub fn discard_checkpoint(&mut self) {
// merge with previous checkpoint
let last = self.checkpoints.get_mut().pop();
if let Some(mut checkpoint) = last {
if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() {
if prev.is_empty() {
**prev = snapshot;
**prev = checkpoint;
} else {
for (k, v) in snapshot.drain() {
for (k, v) in checkpoint.drain() {
prev.entry(k).or_insert(v);
}
}
@@ -259,15 +263,15 @@ impl State {
}
}
/// Revert to the last snapshot and discard it.
pub fn revert_to_snapshot(&mut self) {
if let Some(mut snapshot) = self.snapshots.get_mut().pop() {
for (k, v) in snapshot.drain() {
/// Revert to the last checkpoint and discard it.
pub fn revert_to_checkpoint(&mut self) {
if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() {
for (k, v) in checkpoint.drain() {
match v {
Some(v) => {
match self.cache.get_mut().entry(k) {
Entry::Occupied(mut e) => {
// Merge snapshotted changes back into the main account
// Merge checkpointed changes back into the main account
// storage preserving the cache.
e.get_mut().overwrite_with(v);
},
@@ -293,14 +297,14 @@ impl State {
fn insert_cache(&self, address: &Address, account: AccountEntry) {
// Dirty account which is not in the cache means this is a new account.
// It goes directly into the snapshot as there's nothing to rever to.
// It goes directly into the checkpoint as there's nothing to rever to.
//
// In all other cases account is read as clean first, and after that made
// dirty in and added to the snapshot with `note_cache`.
// dirty in and added to the checkpoint with `note_cache`.
if account.is_dirty() {
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
if !snapshot.contains_key(address) {
snapshot.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
if !checkpoint.contains_key(address) {
checkpoint.insert(address.clone(), self.cache.borrow_mut().insert(address.clone(), account));
return;
}
}
@@ -309,9 +313,9 @@ impl State {
}
fn note_cache(&self, address: &Address) {
if let Some(ref mut snapshot) = self.snapshots.borrow_mut().last_mut() {
if !snapshot.contains_key(address) {
snapshot.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() {
if !checkpoint.contains_key(address) {
checkpoint.insert(address.clone(), self.cache.borrow().get(address).map(AccountEntry::clone_dirty));
}
}
}
@@ -404,7 +408,7 @@ impl State {
// account is not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(address) {
Ok(acc) => acc.map(Account::from_rlp),
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
let r = maybe_acc.as_ref().map_or(H256::new(), |a| {
@@ -548,7 +552,7 @@ impl State {
/// Commits our cached account changes into the trie.
pub fn commit(&mut self) -> Result<(), Error> {
assert!(self.snapshots.borrow().is_empty());
assert!(self.checkpoints.borrow().is_empty());
Self::commit_into(&self.factories, &mut self.db, &mut self.root, &mut *self.cache.borrow_mut())
}
@@ -561,7 +565,7 @@ impl State {
#[cfg(feature = "json-tests")]
/// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: PodState) {
assert!(self.snapshots.borrow().is_empty());
assert!(self.checkpoints.borrow().is_empty());
for (add, acc) in accounts.drain().into_iter() {
self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc))));
}
@@ -569,7 +573,7 @@ impl State {
/// Populate a PodAccount map from this state.
pub fn to_pod(&self) -> PodState {
assert!(self.snapshots.borrow().is_empty());
assert!(self.checkpoints.borrow().is_empty());
// TODO: handle database rather than just the cache.
// will need fat db.
PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
@@ -644,7 +648,7 @@ impl State {
// not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let mut maybe_acc = match db.get(a) {
Ok(acc) => acc.map(Account::from_rlp),
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
if let Some(ref mut account) = maybe_acc.as_mut() {
@@ -676,7 +680,7 @@ impl State {
let maybe_acc = if self.db.check_account_bloom(a) {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(a) {
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(acc))),
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
Ok(None) => AccountEntry::new_clean(None),
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
@@ -739,7 +743,7 @@ impl Clone for State {
db: self.db.boxed_clone(),
root: self.root.clone(),
cache: RefCell::new(cache),
snapshots: RefCell::new(Vec::new()),
checkpoints: RefCell::new(Vec::new()),
account_start_nonce: self.account_start_nonce.clone(),
factories: self.factories.clone(),
}
@@ -756,7 +760,7 @@ use super::*;
use util::{U256, H256, FixedHash, Address, Hashable};
use tests::helpers::*;
use devtools::*;
use env_info::*;
use env_info::EnvInfo;
use spec::*;
use transaction::*;
use util::log::init_log;
@@ -1777,34 +1781,34 @@ fn ensure_cached() {
}
#[test]
fn snapshot_basic() {
fn checkpoint_basic() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
state.snapshot();
state.checkpoint();
state.add_balance(&a, &U256::from(69u64));
assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_snapshot();
state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64));
state.snapshot();
state.checkpoint();
state.add_balance(&a, &U256::from(1u64));
assert_eq!(state.balance(&a), U256::from(70u64));
state.revert_to_snapshot();
state.revert_to_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64));
}
#[test]
fn snapshot_nested() {
fn checkpoint_nested() {
let mut state_result = get_temp_state();
let mut state = state_result.reference_mut();
let a = Address::zero();
state.snapshot();
state.snapshot();
state.checkpoint();
state.checkpoint();
state.add_balance(&a, &U256::from(69u64));
assert_eq!(state.balance(&a), U256::from(69u64));
state.discard_snapshot();
state.discard_checkpoint();
assert_eq!(state.balance(&a), U256::from(69u64));
state.revert_to_snapshot();
state.revert_to_checkpoint();
assert_eq!(state.balance(&a), U256::from(0));
}

View File

@@ -20,11 +20,12 @@ use ethereum;
use block::IsBlock;
use tests::helpers::*;
use types::filter::Filter;
use common::*;
use util::*;
use devtools::*;
use miner::Miner;
use rlp::{Rlp, View};
use spec::Spec;
use views::BlockView;
#[test]
fn imports_from_empty() {

View File

@@ -17,18 +17,22 @@
use ethkey::KeyPair;
use io::*;
use client::{BlockChainClient, Client, ClientConfig};
use common::*;
use util::*;
use spec::*;
use state_db::StateDB;
use block::{OpenBlock, Drain};
use blockchain::{BlockChain, Config as BlockChainConfig};
use builtin::Builtin;
use evm::Schedule;
use engines::Engine;
use env_info::EnvInfo;
use ethereum;
use devtools::*;
use miner::Miner;
use header::Header;
use transaction::{Action, SignedTransaction, Transaction};
use rlp::{self, RlpStream, Stream};
use db::COL_STATE;
use views::BlockView;
#[cfg(feature = "json-tests")]
pub enum ChainEra {
@@ -346,7 +350,7 @@ pub fn get_temp_state() -> GuardedTempResult<State> {
pub fn get_temp_state_db_in(path: &Path) -> StateDB {
let db = new_db(path.to_str().expect("Only valid utf8 paths for tests."));
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, COL_STATE);
let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, ::db::COL_STATE);
StateDB::new(journal_db, 5 * 1024 * 1024)
}

View File

@@ -31,6 +31,8 @@ pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, Queu
pub enum VerifierType {
/// Verifies block normally.
Canon,
/// Verifies block normallly, but skips seal verification.
CanonNoSeal,
/// Does not verify block at all.
/// Used in tests.
Noop,
@@ -44,7 +46,17 @@ impl Default for VerifierType {
pub fn new(v: VerifierType) -> Box<Verifier> {
match v {
VerifierType::Canon => Box::new(CanonVerifier),
VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier),
VerifierType::Noop => Box::new(NoopVerifier),
}
}
impl VerifierType {
/// Check if seal verification is enabled for this verifier type.
pub fn verifying_seal(&self) -> bool {
match *self {
VerifierType::Canon => true,
VerifierType::Noop | VerifierType::CanonNoSeal => false,
}
}
}

View File

@@ -57,7 +57,7 @@ pub trait Kind: 'static + Sized + Send + Sync {
fn create(input: Self::Input, engine: &Engine) -> Result<Self::Unverified, Error>;
/// Attempt to verify the `Unverified` item using the given engine.
fn verify(unverified: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error>;
fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error>;
}
/// The blocks verification module.
@@ -89,9 +89,9 @@ pub mod blocks {
}
}
fn verify(un: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error> {
fn verify(un: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {
let hash = un.hash();
match verify_block_unordered(un.header, un.bytes, engine) {
match verify_block_unordered(un.header, un.bytes, engine, check_seal) {
Ok(verified) => Ok(verified),
Err(e) => {
warn!(target: "client", "Stage 2 block verification failed for {}: {:?}", hash, e);
@@ -176,8 +176,11 @@ pub mod headers {
verify_header_params(&input, engine).map(|_| input)
}
fn verify(unverified: Self::Unverified, engine: &Engine) -> Result<Self::Verified, Error> {
engine.verify_block_unordered(&unverified, None).map(|_| unverified)
fn verify(unverified: Self::Unverified, engine: &Engine, check_seal: bool) -> Result<Self::Verified, Error> {
match check_seal {
true => engine.verify_block_unordered(&unverified, None).map(|_| unverified),
false => Ok(unverified),
}
}
}
}

View File

@@ -159,7 +159,7 @@ struct Verification<K: Kind> {
impl<K: Kind> VerificationQueue<K> {
/// Creates a new queue instance.
pub fn new(config: Config, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> Self {
pub fn new(config: Config, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>, check_seal: bool) -> Self {
let verification = Arc::new(Verification {
unverified: Mutex::new(VecDeque::new()),
verifying: Mutex::new(VecDeque::new()),
@@ -198,7 +198,7 @@ impl<K: Kind> VerificationQueue<K> {
.name(format!("Verifier #{}", i))
.spawn(move || {
panic_handler.catch_panic(move || {
VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty)
VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty, check_seal)
}).unwrap()
})
.expect("Error starting block verification thread")
@@ -219,7 +219,7 @@ impl<K: Kind> VerificationQueue<K> {
}
}
fn verify(verification: Arc<Verification<K>>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>) {
fn verify(verification: Arc<Verification<K>>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>, check_seal: bool) {
while !deleting.load(AtomicOrdering::Acquire) {
{
let mut more_to_verify = verification.more_to_verify.lock().unwrap();
@@ -253,7 +253,7 @@ impl<K: Kind> VerificationQueue<K> {
};
let hash = item.hash();
let is_ready = match K::verify(item, &*engine) {
let is_ready = match K::verify(item, &*engine, check_seal) {
Ok(verified) => {
let mut verifying = verification.verifying.lock();
let mut idx = None;
@@ -529,7 +529,7 @@ mod tests {
fn get_test_queue() -> BlockQueue {
let spec = get_test_spec();
let engine = spec.engine;
BlockQueue::new(Config::default(), engine, IoChannel::disconnected())
BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true)
}
#[test]
@@ -537,7 +537,7 @@ mod tests {
// TODO better test
let spec = Spec::new_test();
let engine = spec.engine;
let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected());
let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true);
}
#[test]
@@ -601,7 +601,7 @@ mod tests {
let engine = spec.engine;
let mut config = Config::default();
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
let queue = BlockQueue::new(config, engine, IoChannel::disconnected());
let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true);
assert!(!queue.queue_info().is_full());
let mut blocks = get_good_dummy_block_seq(50);
for b in blocks.drain(..) {

View File

@@ -21,10 +21,14 @@
/// 2. Signatures verification done in the queue.
/// 3. Final verification against the blockchain done before enactment.
use common::*;
use util::*;
use engines::Engine;
use error::{BlockError, Error};
use blockchain::*;
use header::{BlockNumber, Header};
use rlp::{UntrustedRlp, View};
use transaction::SignedTransaction;
use views::BlockView;
/// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreverifiedBlock {
@@ -66,10 +70,12 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block
/// Returns a `PreverifiedBlock` structure populated with transactions
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> {
try!(engine.verify_block_unordered(&header, Some(&bytes)));
for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&try!(u), None));
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, check_seal: bool) -> Result<PreverifiedBlock, Error> {
if check_seal {
try!(engine.verify_block_unordered(&header, Some(&bytes)));
for u in try!(UntrustedRlp::new(&bytes).at(2)).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&try!(u), None));
}
}
// Verify transactions.
let mut transactions = Vec::new();