Merge branch 'master' into auth-round-no-mocknet
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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::*;
|
||||
@@ -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)]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¶ms.code_address, data);
|
||||
if cost <= params.gas {
|
||||
self.engine.execute_builtin(¶ms.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]
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Block header.
|
||||
|
||||
use util::*;
|
||||
use basic_types::*;
|
||||
use basic_types::{LogBloom, Seal, ZERO_LOGBLOOM};
|
||||
use time::get_time;
|
||||
use rlp::*;
|
||||
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use super::test_common::*;
|
||||
use tests::helpers::*;
|
||||
use pod_state::{self, PodState};
|
||||
use log_entry::LogEntry;
|
||||
use ethereum;
|
||||
use ethjson;
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -140,7 +140,6 @@ pub mod db;
|
||||
|
||||
mod cache_manager;
|
||||
mod blooms;
|
||||
mod common;
|
||||
mod basic_types;
|
||||
mod env_info;
|
||||
mod pod_account;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(..) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user