v2.6.5 beta (#11240)

* [CI] check evmbin build (#11096)
* Correct EIP-712 encoding (#11092)
* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086)
* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053)
* Add cargo-remote dir to .gitignore (?)
* Update light client headers: ropsten 6631425 foundation 8798209 (#11201)
* Update list of bootnodes for xDai chain (#11236)
* ethcore/res: add mordor testnet configuration (#11200)
* [chain specs]: activate Istanbul on mainnet (#11228)
* [builtin]: support multiple prices and activations in chain spec (#11039)
* [receipt]: add sender & receiver to RichReceipts (#11179)
* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180)
* Made ecrecover implementation trait public (#11188)
* Fix docker centos build (#11226)
* Update MIX bootnodes. (#11203)
* Insert explicit warning into the panic hook (#11225)
* Use provided usd-per-eth value if an endpoint is specified (#11209)
* Cleanup stratum a bit (#11161)
* Add Constantinople EIPs to the dev (instant_seal) config (#10809) (already backported)
* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175)
* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172)
* Type annotation for next_key() matching of json filter options (#11192)
* Upgrade jsonrpc to latest (#11206)
* [dependencies]: jsonrpc 14.0.1 (#11183)
* Upgrade to jsonrpc v14 (#11151)
* Switching sccache from local to Redis (#10971)
* Snapshot restoration overhaul (#11219)
* Add new line after writing block to hex file. (#10984)
* Pause pruning while snapshotting (#11178)
* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127)
* Fix block detail updating (#11015)
* Make InstantSeal Instant again #11186
* Filter out some bad ropsten warp snapshots (#11247)
This commit is contained in:
Talha Cross
2019-11-11 23:55:19 +01:00
committed by s3krit
parent 9838c9f447
commit bc90ba2863
193 changed files with 24263 additions and 3349 deletions

View File

@@ -15,77 +15,99 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp;
use std::collections::{HashSet, BTreeMap, VecDeque};
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::convert::TryFrom;
use std::io::{BufRead, BufReader};
use std::str::from_utf8;
use std::sync::{Arc, Weak};
use std::time::{Instant, Duration};
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering as AtomicOrdering, Ordering, AtomicU64};
use std::time::{Duration, Instant};
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
use ansi_term::Colour;
use bytes::Bytes;
use call_contract::{CallContract, RegistryInfo};
use ethcore_miner::pool::VerifiedTransaction;
use ethereum_types::{H256, H264, Address, U256};
use evm::Schedule;
use bytes::ToPretty;
use ethereum_types::{Address, H256, H264, U256};
use hash::keccak;
use io::IoChannel;
use hash_db::EMPTY_PREFIX;
use itertools::Itertools;
use journaldb;
use kvdb::{DBValue, KeyValueDB, DBTransaction};
use kvdb::{DBTransaction, DBValue, KeyValueDB};
use parking_lot::{Mutex, RwLock};
use rand::rngs::OsRng;
use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action};
use trie::{TrieSpec, TrieFactory, Trie};
use types::ancestry_action::AncestryAction;
use types::encoded;
use types::filter::Filter;
use types::log_entry::LocalizedLogEntry;
use types::receipt::{Receipt, LocalizedReceipt};
use types::{BlockNumber, header::{Header, ExtendedHeader}};
use vm::{EnvInfo, LastHashes};
use hash_db::EMPTY_PREFIX;
use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
use client::ancient_import::AncientVerifier;
use rlp::PayloadInfo;
use rustc_hex::FromHex;
use trie::{Trie, TrieFactory, TrieSpec};
use block::{ClosedBlock, Drain, enact_verified, LockedBlock, OpenBlock, SealedBlock};
use blockchain::{
BlockChain,
BlockChainDB,
BlockNumberKey,
BlockProvider,
BlockReceipts,
ExtrasInsert,
ImportRoute,
TransactionAddress,
TreeRoute
};
use call_contract::{CallContract, RegistryInfo};
use client::bad_blocks;
use client::{
Nonce, Balance, ChainInfo, BlockInfo, TransactionInfo,
ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
ClientIoMessage, BlockChainReset
ClientIoMessage, BlockChainReset, ImportExportBlocks, ForceUpdateSealing
};
use client::{
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
TraceFilter, CallAnalytics, Mode,
ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
IoClient, BadBlocks,
IoClient, BadBlocks
};
use client::bad_blocks;
use client::ancient_import::AncientVerifier;
use db::{keys::BlockDetails, Readable, Writable};
use engines::{MAX_UNCLE_AGE, Engine, EpochTransition, ForkChoice, EngineError, SealingState};
use engines::epoch::PendingTransition;
use error::{
ImportError, ExecutionError, CallError, BlockError,
QueueError, Error as EthcoreError, EthcoreResult,
QueueError, Error as EthcoreError, EthcoreResult
};
use ethcore_miner::pool::VerifiedTransaction;
use evm::Schedule;
use executive::{Executive, Executed, TransactOptions, contract_address};
use factory::{Factories, VmFactory};
use io::IoChannel;
use journaldb;
use miner::{Miner, MinerService};
use snapshot::{self, io as snapshot_io, SnapshotClient};
use snapshot::{self, io as snapshot_io, SnapshotClient, Error as SnapshotError, Progress};
use snapshot::io::SnapshotWriter;
use spec::Spec;
use state::{self, State};
use state_db::StateDB;
use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace::{self, Database as TraceDatabase, ImportRequest as TraceImportRequest, LocalizedTrace, TraceDB};
use transaction_ext::Transaction;
use verification::queue::kind::BlockLike;
use verification::queue::kind::blocks::Unverified;
use verification::{PreverifiedBlock, Verifier, BlockQueue};
use types::{
ancestry_action::AncestryAction,
BlockNumber,
encoded,
data_format::DataFormat,
filter::Filter,
header::{Header, ExtendedHeader},
log_entry::LocalizedLogEntry,
receipt::{LocalizedReceipt, Receipt},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, UnverifiedTransaction},
};
use verification;
use ansi_term::Colour;
use verification::{BlockQueue, Verifier, PreverifiedBlock};
use verification::queue::kind::blocks::Unverified;
use verification::queue::kind::BlockLike;
use vm::{CreateContractAddress, EnvInfo, LastHashes};
// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;
pub use verification::QueueInfo as BlockQueueInfo;
use db::{Writable, Readable, keys::BlockDetails};
use_contract!(registry, "res/contracts/registrar.json");
@@ -194,6 +216,9 @@ pub struct Client {
/// Database pruning strategy to use for StateDB
pruning: journaldb::Algorithm,
/// Don't prune the state we're currently snapshotting
snapshotting_at: AtomicU64,
/// Client uses this to store blocks, traces, etc.
db: RwLock<Arc<dyn BlockChainDB>>,
@@ -773,6 +798,7 @@ impl Client {
tracedb,
engine,
pruning: config.pruning.clone(),
snapshotting_at: AtomicU64::new(0),
db: RwLock::new(db.clone()),
state_db: RwLock::new(state_db),
report: RwLock::new(Default::default()),
@@ -957,31 +983,47 @@ impl Client {
}
// prune ancient states until below the memory limit or only the minimum amount remain.
fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ::error::Error> {
let number = match state_db.journal_db().latest_era() {
fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), EthcoreError> {
if !state_db.journal_db().is_pruned() {
return Ok(())
}
let latest_era = match state_db.journal_db().latest_era() {
Some(n) => n,
None => return Ok(()),
};
// prune all ancient eras until we're below the memory target,
// but have at least the minimum number of states.
// Prune all ancient eras until we're below the memory target (default: 32Mb),
// but have at least the minimum number of states, i.e. `history`.
// If a snapshot is under way, no pruning happens and memory consumption is allowed to
// increase above the memory target until the snapshot has finished.
loop {
let needs_pruning = state_db.journal_db().is_pruned() &&
state_db.journal_db().journal_size() >= self.config.history_mem;
if !needs_pruning { break }
match state_db.journal_db().earliest_era() {
Some(era) if era + self.history <= number => {
trace!(target: "client", "Pruning state for ancient era {}", era);
match chain.block_hash(era) {
Some(earliest_era) if earliest_era + self.history <= latest_era => {
let freeze_at = self.snapshotting_at.load(Ordering::SeqCst);
if freeze_at > 0 && freeze_at == earliest_era {
// Note: journal_db().mem_used() can be used for a more accurate memory
// consumption measurement but it can be expensive so sticking with the
// faster `journal_size()` instead.
trace!(target: "pruning", "Pruning is paused at era {} (snapshot under way); earliest era={}, latest era={}, journal_size={} Not pruning.",
freeze_at, earliest_era, latest_era, state_db.journal_db().journal_size());
break;
}
trace!(target: "pruning", "Pruning state for ancient era #{}; latest era={}, journal_size={}",
earliest_era, latest_era, state_db.journal_db().journal_size());
match chain.block_hash(earliest_era) {
Some(ancient_hash) => {
let mut batch = DBTransaction::new();
state_db.mark_canonical(&mut batch, era, &ancient_hash)?;
state_db.mark_canonical(&mut batch, earliest_era, &ancient_hash)?;
self.db.read().key_value().write_buffered(batch);
state_db.journal_db().flush();
}
None =>
debug!(target: "client", "Missing expected hash for block {}", era),
debug!(target: "pruning", "Missing expected hash for block {}", earliest_era),
}
}
_ => break, // means that every era is kept, no pruning necessary.
@@ -1023,15 +1065,16 @@ impl Client {
}
/// Get a copy of the best block's state.
pub fn latest_state(&self) -> State<StateDB> {
pub fn latest_state_and_header(&self) -> (State<StateDB>, Header) {
let header = self.best_block_header();
State::from_existing(
let state = State::from_existing(
self.state_db.read().boxed_clone_canon(&header.hash()),
*header.state_root(),
self.engine.account_start_nonce(header.number()),
self.factories.clone()
)
.expect("State root of best block header always valid.")
.expect("State root of best block header always valid.");
(state, header)
}
/// Attempt to get a copy of a specific block's final state.
@@ -1041,9 +1084,9 @@ impl Client {
/// is unknown.
pub fn state_at(&self, id: BlockId) -> Option<State<StateDB>> {
// fast path for latest state.
match id.clone() {
BlockId::Latest => return Some(self.latest_state()),
_ => {},
if let BlockId::Latest = id {
let (state, _) = self.latest_state_and_header();
return Some(state)
}
let block_number = match self.block_number(id) {
@@ -1077,8 +1120,9 @@ impl Client {
}
/// Get a copy of the best block's state.
pub fn state(&self) -> Box<dyn StateInfo> {
Box::new(self.latest_state()) as Box<_>
pub fn state(&self) -> impl StateInfo {
let (state, _) = self.latest_state_and_header();
state
}
/// Get info on the cache.
@@ -1531,8 +1575,8 @@ impl ImportBlock for Client {
impl StateClient for Client {
type State = State<::state_db::StateDB>;
fn latest_state(&self) -> Self::State {
Client::latest_state(self)
fn latest_state_and_header(&self) -> (Self::State, Header) {
Client::latest_state_and_header(self)
}
fn state_at(&self, id: BlockId) -> Option<Self::State> {
@@ -1749,6 +1793,10 @@ impl BlockChainClient for Client {
self.config.spec_name.clone()
}
fn chain(&self) -> Arc<BlockProvider> {
self.chain.read().clone()
}
fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> {
trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name);
if !self.enabled.load(AtomicOrdering::Relaxed) {
@@ -2081,7 +2129,7 @@ impl BlockChainClient for Client {
blocks
};
Ok(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit))
Ok(chain.logs(blocks, |entry| filter.matches(entry), filter.limit))
}
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
@@ -2402,7 +2450,9 @@ impl ImportSealedBlock for Client {
let raw = block.rlp_bytes();
let header = block.header.clone();
let hash = header.hash();
self.notify(|n| n.block_pre_import(&raw, &hash, header.difficulty()));
self.notify(|n| {
n.block_pre_import(&raw, &hash, header.difficulty())
});
let route = {
// Do a super duper basic verification to detect potential bugs
@@ -2490,19 +2540,22 @@ impl ::miner::TransactionVerifierClient for Client {}
impl ::miner::BlockChainClient for Client {}
impl super::traits::EngineClient for Client {
fn update_sealing(&self) {
self.importer.miner.update_sealing(self)
fn update_sealing(&self, force: ForceUpdateSealing) {
self.importer.miner.update_sealing(self, force)
}
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
let import = self.importer.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block));
let import = self.importer.miner.submit_seal(block_hash, seal)
.and_then(|block| self.import_sealed_block(block));
if let Err(err) = import {
warn!(target: "poa", "Wrong internal seal submission! {:?}", err);
}
}
fn broadcast_consensus_message(&self, message: Bytes) {
self.notify(|notify| notify.broadcast(ChainMessageType::Consensus(message.clone())));
self.notify(|notify| {
notify.broadcast(ChainMessageType::Consensus(message.clone()))
});
}
fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> {
@@ -2559,6 +2612,141 @@ impl ProvingBlockChainClient for Client {
impl SnapshotClient for Client {}
impl ImportExportBlocks for Client {
fn export_blocks<'a>(
&self,
mut out: Box<dyn std::io::Write + 'a>,
from: BlockId,
to: BlockId,
format: Option<DataFormat>
) -> Result<(), String> {
let from = self.block_number(from).ok_or("Starting block could not be found")?;
let to = self.block_number(to).ok_or("End block could not be found")?;
let format = format.unwrap_or_default();
for i in from..=to {
if i % 10000 == 0 {
info!("#{}", i);
}
let b = self.block(BlockId::Number(i))
.ok_or("Error exporting incomplete chain")?
.into_inner();
match format {
DataFormat::Binary => {
out.write(&b)
.map_err(|e| {
format!("Couldn't write to stream. Cause: {}", e)
})?;
}
DataFormat::Hex => {
out.write_fmt(format_args!("{}\n", b.pretty()))
.map_err(|e| {
format!("Couldn't write to stream. Cause: {}", e)
})?;
}
}
}
Ok(())
}
fn import_blocks<'a>(
&self,
mut source: Box<dyn std::io::Read + 'a>,
format: Option<DataFormat>
) -> Result<(), String> {
const READAHEAD_BYTES: usize = 8;
let mut first_bytes: Vec<u8> = vec![0; READAHEAD_BYTES];
let mut first_read = 0;
let format = match format {
Some(format) => format,
None => {
first_read = source.read(&mut first_bytes)
.map_err(|_| {
"Error reading from the file/stream."
})?;
match first_bytes[0] {
0xf9 => DataFormat::Binary,
_ => DataFormat::Hex,
}
}
};
let do_import = |bytes: Vec<u8>| {
let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?;
let number = block.header.number();
while self.queue_info().is_full() {
std::thread::sleep(Duration::from_secs(1));
}
match self.import_block(block) {
Err(EthcoreError::Import(ImportError::AlreadyInChain)) => {
trace!("Skipping block #{}: already in chain.", number);
}
Err(e) => {
return Err(format!("Cannot import block #{}: {:?}", number, e));
},
Ok(_) => {},
}
Ok(())
};
match format {
DataFormat::Binary => {
loop {
let (mut bytes, n) = if first_read > 0 {
(first_bytes.clone(), first_read)
} else {
let mut bytes = vec![0; READAHEAD_BYTES];
let n = source.read(&mut bytes)
.map_err(|err| {
format!("Error reading from the file/stream: {:?}", err)
})?;
(bytes, n)
};
if n == 0 { break; }
first_read = 0;
let s = PayloadInfo::from(&bytes)
.map_err(|e| {
format!("Invalid RLP in the file/stream: {:?}", e)
})?.total();
bytes.resize(s, 0);
source.read_exact(&mut bytes[n..])
.map_err(|err| {
format!("Error reading from the file/stream: {:?}", err)
})?;
do_import(bytes)?;
}
}
DataFormat::Hex => {
for line in BufReader::new(source).lines() {
let s = line
.map_err(|err| {
format!("Error reading from the file/stream: {:?}", err)
})?;
let s = if first_read > 0 {
from_utf8(&first_bytes)
.map_err(|err| {
format!("Invalid UTF-8: {:?}", err)
})?
.to_owned() + &(s[..])
} else {
s
};
first_read = 0;
let bytes = s.from_hex()
.map_err(|err| {
format!("Invalid hex in file/stream: {:?}", err)
})?;
do_import(bytes)?;
}
}
};
self.flush_queue();
Ok(())
}
}
/// Returns `LocalizedReceipt` given `LocalizedTransaction`
/// and a vector of receipts from given block up to transaction index.
fn transaction_receipt(
@@ -2606,7 +2794,28 @@ fn transaction_receipt(
#[cfg(test)]
mod tests {
use ethereum_types::{H256, Address};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;
use ethereum_types::{Address, H256};
use hash::keccak;
use kvdb::DBTransaction;
use blockchain::{ExtrasInsert, BlockProvider};
use super::{BlockChainClient, ChainInfo};
use ethkey::KeyPair;
use types::{
encoded,
engines::ForkChoice,
ids::{BlockId, TransactionId},
log_entry::{LocalizedLogEntry, LogEntry},
receipt::{LocalizedReceipt, Receipt, TransactionOutcome},
transaction::{Action, LocalizedTransaction, Transaction},
};
use test_helpers::{generate_dummy_client, generate_dummy_client_with_data, generate_dummy_client_with_spec_and_data, get_good_dummy_block_hash};
use super::transaction_receipt;
#[test]
fn should_not_cache_details_before_commit() {
@@ -2760,42 +2969,64 @@ mod tests {
outcome: TransactionOutcome::StateRoot(state_root),
});
}
#[test]
fn should_mark_finalization_correctly_for_parent() {
let client = generate_dummy_client_with_spec_and_data(::spec::Spec::new_test_with_finality, 2, 0, &[]);
let chain = client.chain();
let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h));
assert!(block1_details.is_some());
let block1_details = block1_details.unwrap();
assert_eq!(block1_details.children.len(), 1);
assert!(block1_details.is_finalized);
let block2_details = chain.block_hash(2).and_then(|h| chain.block_details(&h));
assert!(block2_details.is_some());
let block2_details = block2_details.unwrap();
assert_eq!(block2_details.children.len(), 0);
assert!(!block2_details.is_finalized);
}
}
/// Queue some items to be processed by IO client.
struct IoChannelQueue {
currently_queued: Arc<AtomicUsize>,
limit: usize,
/// Using a *signed* integer for counting currently queued messages since the
/// order in which the counter is incremented and decremented is not defined.
/// Using an unsigned integer can (and will) result in integer underflow,
/// incorrectly rejecting messages and returning a FullQueue error.
currently_queued: Arc<AtomicI64>,
limit: i64,
}
impl IoChannelQueue {
pub fn new(limit: usize) -> Self {
let limit = i64::try_from(limit).unwrap_or(i64::max_value());
IoChannelQueue {
currently_queued: Default::default(),
limit,
}
}
pub fn queue<F>(&self, channel: &IoChannel<ClientIoMessage>, count: usize, fun: F) -> Result<(), QueueError> where
pub fn queue<F>(&self, channel: &IoChannel<ClientIoMessage>, count: usize, fun: F) -> EthcoreResult<()> where
F: Fn(&Client) + Send + Sync + 'static,
{
let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed);
if queue_size >= self.limit {
return Err(QueueError::Full(self.limit))
let err_limit = usize::try_from(self.limit).unwrap_or(usize::max_value());
return Err(EthcoreError::Queue(QueueError::Full(err_limit)))
};
let count = i64::try_from(count).unwrap_or(i64::max_value());
let currently_queued = self.currently_queued.clone();
let result = channel.send(ClientIoMessage::execute(move |client| {
let _ok = channel.send(ClientIoMessage::execute(move |client| {
currently_queued.fetch_sub(count, AtomicOrdering::SeqCst);
fun(client);
}));
}))?;
match result {
Ok(_) => {
self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst);
Ok(())
},
Err(e) => return Err(QueueError::Channel(e)),
}
self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst);
Ok(())
}
}

View File

@@ -117,7 +117,7 @@ pub struct ClientConfig {
pub history: u64,
/// Ideal memory usage for state pruning history.
pub history_mem: usize,
/// Check seal valididity on block import
/// Check seal validity on block import
pub check_seal: bool,
/// Maximal number of transactions queued for verification in a separate thread.
pub transaction_verification_queue_size: usize,

View File

@@ -33,12 +33,12 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain
pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess};
pub use self::io_message::ClientIoMessage;
#[cfg(any(test, feature = "test-helpers"))]
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use self::test_client::{TestBlockChainClient, TestState, EachBlockWith};
pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
pub use self::traits::{
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,
BlockChainReset
BlockChainReset, ImportExportBlocks, ForceUpdateSealing
};
pub use state::StateInfo;
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};

View File

@@ -20,6 +20,7 @@ use std::str::FromStr;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder};
use std::sync::Arc;
use std::collections::{HashMap, BTreeMap};
use blockchain::BlockProvider;
use std::mem;
use blockchain::{TreeRoute, BlockReceipts};
@@ -57,7 +58,7 @@ use client::{
TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics,
ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock,
Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient,
BadBlocks
BadBlocks, ForceUpdateSealing
};
use engines::Engine;
use error::{Error, EthcoreResult};
@@ -586,7 +587,7 @@ impl ImportBlock for TestBlockChainClient {
impl Call for TestBlockChainClient {
// State will not be used by test client anyway, since all methods that accept state are mocked
type State = ();
type State = TestState;
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result<Executed, CallError> {
self.execution_result.read().clone().unwrap()
@@ -605,26 +606,30 @@ impl Call for TestBlockChainClient {
}
}
impl StateInfo for () {
impl StateClient for TestBlockChainClient {
// State will not be used by test client anyway, since all methods that accept state are mocked
type State = TestState;
fn latest_state_and_header(&self) -> (Self::State, Header) {
(TestState, self.best_block_header())
}
fn state_at(&self, _id: BlockId) -> Option<Self::State> {
Some(TestState)
}
}
/// NewType wrapper around `()` to impersonate `State` in trait impls. State will not be used by
/// test client, since all methods that accept state are mocked.
pub struct TestState;
impl StateInfo for TestState {
fn nonce(&self, _address: &Address) -> ethtrie::Result<U256> { unimplemented!() }
fn balance(&self, _address: &Address) -> ethtrie::Result<U256> { unimplemented!() }
fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result<H256> { unimplemented!() }
fn code(&self, _address: &Address) -> ethtrie::Result<Option<Arc<Bytes>>> { unimplemented!() }
}
impl StateClient for TestBlockChainClient {
// State will not be used by test client anyway, since all methods that accept state are mocked
type State = ();
fn latest_state(&self) -> Self::State {
()
}
fn state_at(&self, _id: BlockId) -> Option<Self::State> {
Some(())
}
}
impl EngineInfo for TestBlockChainClient {
fn engine(&self) -> &dyn Engine {
unimplemented!()
@@ -699,6 +704,10 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn chain(&self) -> Arc<BlockProvider> {
unimplemented!()
}
fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> {
None
}
@@ -933,8 +942,8 @@ impl ProvingBlockChainClient for TestBlockChainClient {
}
impl super::traits::EngineClient for TestBlockChainClient {
fn update_sealing(&self) {
self.miner.update_sealing(self)
fn update_sealing(&self, force: ForceUpdateSealing) {
self.miner.update_sealing(self, force)
}
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {

View File

@@ -17,7 +17,7 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use blockchain::{BlockReceipts, TreeRoute};
use blockchain::{BlockReceipts, TreeRoute, BlockProvider};
use bytes::Bytes;
use call_contract::{CallContract, RegistryInfo};
use ethcore_miner::pool::VerifiedTransaction;
@@ -31,6 +31,7 @@ use types::basic_account::BasicAccount;
use types::block_status::BlockStatus;
use types::blockchain_info::BlockChainInfo;
use types::call_analytics::CallAnalytics;
use types::data_format::DataFormat;
use types::encoded;
use types::filter::Filter;
use types::header::Header;
@@ -145,7 +146,7 @@ pub trait StateClient {
type State: StateInfo;
/// Get a copy of the best block's state.
fn latest_state(&self) -> Self::State;
fn latest_state_and_header(&self) -> (Self::State, Header);
/// Attempt to get a copy of a specific block's final state.
///
@@ -237,6 +238,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
.expect("code will return Some if given BlockId::Latest; qed")
}
fn chain(&self) -> Arc<BlockProvider>;
/// Get block queue information.
fn queue_info(&self) -> BlockQueueInfo;
@@ -419,10 +422,19 @@ pub trait BroadcastProposalBlock {
/// Provides methods to import sealed block and broadcast a block proposal
pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {}
/// Do we want to force update sealing?
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ForceUpdateSealing {
/// Ideally you want to use `No` at all times as `Yes` skips `reseal_required` checks.
Yes,
/// Don't skip `reseal_required` checks
No
}
/// Client facilities used by internally sealing Engines.
pub trait EngineClient: Sync + Send + ChainInfo {
/// Make a new block and seal it.
fn update_sealing(&self);
fn update_sealing(&self, force: ForceUpdateSealing);
/// Submit a seal for a block in the mining queue.
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>);
@@ -474,3 +486,29 @@ pub trait BlockChainReset {
/// reset to best_block - n
fn reset(&self, num: u32) -> Result<(), String>;
}
/// Provides a method for importing/exporting blocks
pub trait ImportExportBlocks {
/// Export blocks to destination, with the given from, to and format argument.
/// destination could be a file or stdout.
/// If the format is hex, each block is written on a new line.
/// For binary exports, all block data is written to the same line.
fn export_blocks<'a>(
&self,
destination: Box<dyn std::io::Write + 'a>,
from: BlockId,
to: BlockId,
format: Option<DataFormat>
) -> Result<(), String>;
/// Import blocks from destination, with the given format argument
/// Source could be a file or stdout.
/// For hex format imports, it attempts to read the blocks on a line by line basis.
/// For binary format imports, reads the 8 byte RLP header in order to decode the block
/// length to be read.
fn import_blocks<'a>(
&self,
source: Box<dyn std::io::Read + 'a>,
format: Option<DataFormat>
) -> Result<(), String>;
}

View File

@@ -25,7 +25,7 @@ use std::sync::{Weak, Arc};
use std::time::{UNIX_EPOCH, Duration};
use block::*;
use client::EngineClient;
use client::{EngineClient, ForceUpdateSealing};
use engines::{Engine, Seal, SealingState, EngineError, ConstructedVerifier};
use engines::block_reward;
use engines::block_reward::{BlockRewardContract, RewardKind};
@@ -929,7 +929,7 @@ impl IoHandler<()> for TransitionHandler {
self.step.can_propose.store(true, AtomicOrdering::SeqCst);
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.update_sealing();
c.update_sealing(ForceUpdateSealing::No);
}
}
}
@@ -957,7 +957,7 @@ impl Engine for AuthorityRound {
self.step.can_propose.store(true, AtomicOrdering::SeqCst);
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.update_sealing();
c.update_sealing(ForceUpdateSealing::No);
}
}
}

View File

@@ -67,7 +67,7 @@ use std::time;
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
use block::ExecutedBlock;
use client::{BlockId, EngineClient};
use client::{BlockId, EngineClient, ForceUpdateSealing};
use engines::clique::util::{extract_signers, recover_creator};
use engines::{Engine, EngineError, Seal, SealingState};
use error::{BlockError, Error};
@@ -750,7 +750,7 @@ impl Engine for Clique {
if self.signer.read().is_some() {
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.update_sealing();
c.update_sealing(ForceUpdateSealing::No);
}
}
}

View File

@@ -65,6 +65,13 @@ impl Engine for InstantSeal {
fn sealing_state(&self) -> SealingState { SealingState::Ready }
fn should_reseal_on_update(&self) -> bool {
// We would like for the miner to `update_sealing` if there are local_pending_transactions
// in the pool to prevent transactions sent in parallel from stalling in the transaction
// pool. (see #9660)
true
}
fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal {
if !block.transactions.is_empty() {
let block_number = block.header.number();

View File

@@ -321,6 +321,14 @@ pub trait Engine: Sync + Send {
/// Returns the engine's current sealing state.
fn sealing_state(&self) -> SealingState { SealingState::External }
/// Called in `miner.chain_new_blocks` if the engine wishes to `update_sealing`
/// after a block was recently sealed.
///
/// returns false by default
fn should_reseal_on_update(&self) -> bool {
false
}
/// Attempt to seal the block internally.
///
/// If `Some` is returned, then you get a valid seal.

View File

@@ -17,23 +17,29 @@
use engines::Engine;
use engines::block_reward::{self, RewardKind};
use ethereum_types::U256;
use machine::Machine;
use types::BlockNumber;
use types::header::{Header, ExtendedHeader};
use block::ExecutedBlock;
use error::Error;
use machine::Machine;
use types::BlockNumber;
use types::{
ancestry_action::AncestryAction,
header::{Header, ExtendedHeader},
};
/// Params for a null engine.
#[derive(Clone, Default)]
pub struct NullEngineParams {
/// base reward for a block.
pub block_reward: U256,
/// Immediate finalization.
pub immediate_finalization: bool
}
impl From<::ethjson::spec::NullEngineParams> for NullEngineParams {
fn from(p: ::ethjson::spec::NullEngineParams) -> Self {
NullEngineParams {
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
immediate_finalization: p.immediate_finalization.unwrap_or(false)
}
}
}
@@ -105,4 +111,13 @@ impl Engine for NullEngine {
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice {
super::total_difficulty_fork_choice(new, current)
}
fn ancestry_actions(&self, _header: &Header, ancestry: &mut dyn Iterator<Item=ExtendedHeader>) -> Vec<AncestryAction> {
if self.params.immediate_finalization {
// always mark parent finalized
ancestry.take(1).map(|e| AncestryAction::MarkFinalized(e.header.hash())).collect()
} else {
Vec::new()
}
}
}

View File

@@ -151,7 +151,7 @@ mod tests {
use std::collections::BTreeMap;
use hash::keccak;
use accounts::AccountProvider;
use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock};
use client::{BlockChainClient, ChainInfo, BlockInfo, EngineClient, ImportBlock, ForceUpdateSealing};
use engines::EpochChange;
use engines::validator_set::ValidatorSet;
use ethkey::Secret;
@@ -181,24 +181,24 @@ mod tests {
let signer = Box::new((tap.clone(), v1, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
client.transact_contract(Default::default(), Default::default()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 0);
// Right signer for the first block.
let signer = Box::new((tap.clone(), v0, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
// This time v0 is wrong.
client.transact_contract(Default::default(), Default::default()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
let signer = Box::new((tap.clone(), v1, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 2);
// v1 is still good.
client.transact_contract(Default::default(), Default::default()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 3);
// Check syncing.

View File

@@ -453,7 +453,7 @@ mod tests {
use spec::Spec;
use accounts::AccountProvider;
use types::transaction::{Transaction, Action};
use client::{ChainInfo, BlockInfo, ImportBlock};
use client::{ChainInfo, BlockInfo, EngineClient, ImportBlock, ForceUpdateSealing};
use ethkey::Secret;
use miner::{self, MinerService};
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
@@ -495,7 +495,7 @@ mod tests {
data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
}.sign(&s0, Some(chain_id));
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
// Add "1" validator back in.
let tx = Transaction {
@@ -507,14 +507,14 @@ mod tests {
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
}.sign(&s0, Some(chain_id));
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
// The transaction is not yet included so still unable to seal.
assert_eq!(client.chain_info().best_block_number, 1);
// Switch to the validator that is still there.
let signer = Box::new((tap.clone(), v0, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 2);
// Switch back to the added validator, since the state is updated.
let signer = Box::new((tap.clone(), v1, "".into()));
@@ -528,7 +528,7 @@ mod tests {
data: Vec::new(),
}.sign(&s0, Some(chain_id));
client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
::client::EngineClient::update_sealing(&*client);
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
// Able to seal again.
assert_eq!(client.chain_info().best_block_number, 3);

View File

@@ -134,6 +134,11 @@ pub fn new_sokol<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/poasokol.json"))
}
/// Create a new Mordor testnet chain spec.
pub fn new_mordor<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/mordor.json"))
}
// For tests
/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead.

View File

@@ -160,8 +160,8 @@ impl<'de> Deserialize<'de> for FilterOptions {
M: MapAccess<'de>,
{
let mut filter = FilterOptions::default();
while let Some(key) = map.next_key()? {
match key {
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"from" => {
filter.from = map.validate_from()?;
},
@@ -221,8 +221,8 @@ impl<'de> Deserialize<'de> for FilterOptions {
let mut counter = 0;
let mut f_op = Wrapper::O(FilterOperator::Any);
while let Some(key) = map.next_key()? {
match key {
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"eq" => f_op = W::O(FilterOperator::Eq(map.next_value()?)),
"gt" => f_op = W::O(FilterOperator::GreaterThan(map.next_value()?)),
"lt" => f_op = W::O(FilterOperator::LessThan(map.next_value()?)),

View File

@@ -51,9 +51,10 @@ use using_queue::{UsingQueue, GetAction};
use block::{ClosedBlock, SealedBlock};
use client::{
BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce, TransactionInfo, TransactionId
BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce,
TransactionInfo, TransactionId, ForceUpdateSealing
};
use client::{BlockId, ClientIoMessage};
use client::{Client, BlockId, ClientIoMessage};
use client::traits::EngineClient;
use engines::{Engine, Seal, SealingState, EngineSigner};
use error::Error;
@@ -291,6 +292,7 @@ impl Miner {
let tx_queue_strategy = options.tx_queue_strategy;
let nonce_cache_size = cmp::max(4096, limits.max_count / 4);
let refuse_service_transactions = options.refuse_service_transactions;
let engine = spec.engine.clone();
Miner {
sealing: Mutex::new(SealingWork {
@@ -309,7 +311,7 @@ impl Miner {
options,
transaction_queue: Arc::new(TransactionQueue::new(limits, verifier_options, tx_queue_strategy)),
accounts: Arc::new(accounts),
engine: spec.engine.clone(),
engine,
io_channel: RwLock::new(None),
service_transaction_checker: if refuse_service_transactions {
None
@@ -862,12 +864,12 @@ impl Miner {
match self.engine.sealing_state() {
SealingState::Ready => {
self.maybe_enable_sealing();
self.update_sealing(chain)
self.update_sealing(chain, ForceUpdateSealing::No);
}
SealingState::External => {
// this calls `maybe_enable_sealing()`
if self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared {
self.update_sealing(chain)
self.update_sealing(chain, ForceUpdateSealing::No);
}
}
SealingState::NotReady => { self.maybe_enable_sealing(); },
@@ -1216,6 +1218,11 @@ impl miner::MinerService for Miner {
let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used };
let receipt = &receipts[index];
RichReceipt {
from: tx.sender(),
to: match tx.action {
Action::Create => None,
Action::Call(ref address) => Some(*address),
},
transaction_hash: tx.hash(),
transaction_index: index,
cumulative_gas_used: receipt.gas_used,
@@ -1238,14 +1245,16 @@ impl miner::MinerService for Miner {
/// Update sealing if required.
/// Prepare the block and work if the Engine does not seal internally.
fn update_sealing<C>(&self, chain: &C) where
fn update_sealing<C>(&self, chain: &C, force: ForceUpdateSealing) where
C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync,
{
trace!(target: "miner", "update_sealing");
// Do nothing if reseal is not required,
// Do nothing if we don't want to force update_sealing and reseal is not required.
// but note that `requires_reseal` updates internal state.
if !self.requires_reseal(chain.chain_info().best_block_number) {
if force == ForceUpdateSealing::No &&
!self.requires_reseal(chain.chain_info().best_block_number)
{
return;
}
@@ -1280,13 +1289,14 @@ impl miner::MinerService for Miner {
if self.seal_and_import_block_internally(chain, block) {
trace!(target: "miner", "update_sealing: imported internally sealed block");
}
return
},
SealingState::NotReady => unreachable!("We returned right after sealing_state was computed. qed."),
SealingState::External => {
trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work");
self.prepare_work(block, original_work_hash)
self.prepare_work(block, original_work_hash);
},
}
};
}
fn is_currently_sealing(&self) -> bool {
@@ -1387,7 +1397,7 @@ impl miner::MinerService for Miner {
// | NOTE Code below requires sealing locks. |
// | Make sure to release the locks before calling that method. |
// --------------------------------------------------------------------------
self.update_sealing(chain);
self.update_sealing(chain, ForceUpdateSealing::No);
}
}
@@ -1405,8 +1415,7 @@ impl miner::MinerService for Miner {
let engine = self.engine.clone();
let accounts = self.accounts.clone();
let service_transaction_checker = self.service_transaction_checker.clone();
let cull = move |chain: &::client::Client| {
let cull = move |chain: &Client| {
let client = PoolClient::new(
chain,
&nonce_cache,
@@ -1415,8 +1424,9 @@ impl miner::MinerService for Miner {
service_transaction_checker.as_ref(),
);
queue.cull(client);
if is_internal_import {
chain.update_sealing();
if engine.should_reseal_on_update() {
// force update_sealing here to skip `reseal_required` checks
chain.update_sealing(ForceUpdateSealing::Yes);
}
};
@@ -1425,8 +1435,9 @@ impl miner::MinerService for Miner {
}
} else {
self.transaction_queue.cull(client);
if is_internal_import {
self.update_sealing(chain);
if self.engine.should_reseal_on_update() {
// force update_sealing here to skip `reseal_required` checks
self.update_sealing(chain, ForceUpdateSealing::Yes);
}
}
}
@@ -1751,7 +1762,7 @@ mod tests {
).pop().unwrap();
assert_eq!(import.unwrap(), ());
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
client.flush_queue();
assert!(miner.pending_block(0).is_none());
assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber);
@@ -1761,7 +1772,7 @@ mod tests {
PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)
).is_ok());
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
client.flush_queue();
assert!(miner.pending_block(0).is_none());
assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber);
@@ -1789,7 +1800,7 @@ mod tests {
let miner = Miner::new_for_tests(&spec, None);
let client = generate_dummy_client(2);
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
assert!(miner.is_currently_sealing());
}
@@ -1800,7 +1811,7 @@ mod tests {
let miner = Miner::new_for_tests(&spec, None);
let client = generate_dummy_client(2);
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
assert!(!miner.is_currently_sealing());
}
@@ -1811,7 +1822,7 @@ mod tests {
let miner = Miner::new_for_tests(&spec, None);
let client = generate_dummy_client(2);
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
assert!(!miner.is_currently_sealing());
}
@@ -1830,7 +1841,7 @@ mod tests {
miner.add_work_listener(Box::new(DummyNotifyWork));
let client = generate_dummy_client(2);
miner.update_sealing(&*client);
miner.update_sealing(&*client, ForceUpdateSealing::No);
assert!(miner.is_currently_sealing());
}
@@ -1868,6 +1879,7 @@ mod tests {
},
fetch,
p,
"fake_endpoint".to_owned()
)
)
}

View File

@@ -47,7 +47,7 @@ use call_contract::{CallContract, RegistryInfo};
use client::{
ScheduleInfo,
BlockChain, BlockProducer, SealedBlockImporter, ChainInfo,
AccountData, Nonce,
AccountData, Nonce, ForceUpdateSealing
};
use error::Error;
use state::StateInfo;
@@ -84,7 +84,7 @@ pub trait MinerService : Send + Sync {
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
/// Update current pending block
fn update_sealing<C>(&self, chain: &C)
fn update_sealing<C>(&self, chain: &C, force: ForceUpdateSealing)
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
// Notifications

View File

@@ -225,8 +225,6 @@ impl NotifyWork for Stratum {
self.service.push_work_all(
self.dispatcher.payload(pow_hash, difficulty, number)
).unwrap_or_else(
|e| warn!(target: "stratum", "Error while pushing work: {:?}", e)
);
}
}
@@ -239,16 +237,13 @@ impl Stratum {
let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client));
let stratum_svc = StratumService::start(
let service = StratumService::start(
&SocketAddr::new(options.listen_addr.parse::<IpAddr>()?, options.port),
dispatcher.clone(),
options.secret.clone(),
)?;
Ok(Stratum {
dispatcher: dispatcher,
service: stratum_svc,
})
Ok(Stratum { dispatcher, service })
}
/// Start STRATUM job dispatcher and register it in the miner

View File

@@ -250,7 +250,7 @@ mod tests {
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);
let p = Progress::default();
let p = Progress::new();
let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap();
let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap();
assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account);
@@ -276,7 +276,7 @@ mod tests {
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);
let p = Progress::default();
let p = Progress::new();
let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap();
let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap();
@@ -303,7 +303,7 @@ mod tests {
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);
let p = Progress::default();
let p = Progress::new();
let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), 500, 1000, &p).unwrap();
let mut root = KECCAK_NULL_RLP;
let mut restored_account = None;
@@ -347,8 +347,8 @@ mod tests {
};
let mut used_code = HashSet::new();
let p1 = Progress::default();
let p2 = Progress::default();
let p1 = Progress::new();
let p2 = Progress::new();
let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::from_hash(db.as_hash_db(), keccak(addr1)), &mut used_code, usize::max_value(), usize::max_value(), &p1).unwrap();
let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::from_hash(db.as_hash_db(), keccak(addr2)), &mut used_code, usize::max_value(), usize::max_value(), &p2).unwrap();
assert_eq!(used_code.len(), 1);

View File

@@ -166,7 +166,7 @@ impl<'a> PowWorker<'a> {
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);
trace!(target: "snapshot", "parent last written block: #{}/{}", parent_number, parent_hash);
let num_entries = self.rlps.len();
let mut rlp_stream = RlpStream::new_list(3 + num_entries);

View File

@@ -23,8 +23,9 @@ use std::collections::{HashMap, HashSet};
use std::cmp;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY};
use std::time::Instant;
use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY};
use account_db::{AccountDB, AccountDBMut};
use blockchain::{BlockChain, BlockProvider};
use engines::Engine;
@@ -36,7 +37,7 @@ use hash_db::HashDB;
use keccak_hasher::KeccakHasher;
use snappy;
use bytes::Bytes;
use parking_lot::Mutex;
use parking_lot::{Mutex, RwLock};
use journaldb::{self, Algorithm, JournalDB};
use kvdb::{KeyValueDB, DBValue};
use trie::{Trie, TrieMut};
@@ -113,21 +114,38 @@ impl Default for SnapshotConfiguration {
}
/// A progress indicator for snapshots.
#[derive(Debug, Default)]
#[derive(Debug)]
pub struct Progress {
accounts: AtomicUsize,
prev_accounts: AtomicUsize,
blocks: AtomicUsize,
size: AtomicU64,
bytes: AtomicUsize,
prev_bytes: AtomicUsize,
done: AtomicBool,
abort: AtomicBool,
last_tick: RwLock<Instant>,
}
impl Progress {
/// Create a new progress tracker.
pub fn new() -> Progress {
Progress {
accounts: AtomicUsize::new(0),
prev_accounts: AtomicUsize::new(0),
blocks: AtomicUsize::new(0),
bytes: AtomicUsize::new(0),
prev_bytes: AtomicUsize::new(0),
abort: AtomicBool::new(false),
done: AtomicBool::new(false),
last_tick: RwLock::new(Instant::now()),
}
}
/// Reset the progress.
pub fn reset(&self) {
self.accounts.store(0, Ordering::Release);
self.blocks.store(0, Ordering::Release);
self.size.store(0, Ordering::Release);
self.bytes.store(0, Ordering::Release);
self.abort.store(false, Ordering::Release);
// atomic fence here to ensure the others are written first?
@@ -142,12 +160,39 @@ impl Progress {
pub fn blocks(&self) -> usize { self.blocks.load(Ordering::Acquire) }
/// Get the written size of the snapshot in bytes.
pub fn size(&self) -> u64 { self.size.load(Ordering::Acquire) }
pub fn bytes(&self) -> usize { self.bytes.load(Ordering::Acquire) }
/// Whether the snapshot is complete.
pub fn done(&self) -> bool { self.done.load(Ordering::Acquire) }
/// Return the progress rate over the last tick (i.e. since last update).
pub fn rate(&self) -> (f64, f64) {
let last_tick = *self.last_tick.read();
let dt = last_tick.elapsed().as_secs_f64();
if dt < 1.0 {
return (0f64, 0f64);
}
let delta_acc = self.accounts.load(Ordering::Relaxed)
.saturating_sub(self.prev_accounts.load(Ordering::Relaxed));
let delta_bytes = self.bytes.load(Ordering::Relaxed)
.saturating_sub(self.prev_bytes.load(Ordering::Relaxed));
(delta_acc as f64 / dt, delta_bytes as f64 / dt)
}
/// Update state progress counters and set the last tick.
pub fn update(&self, accounts_delta: usize, bytes_delta: usize) {
*self.last_tick.write() = Instant::now();
self.prev_accounts.store(
self.accounts.fetch_add(accounts_delta, Ordering::SeqCst),
Ordering::SeqCst
);
self.prev_bytes.store(
self.bytes.fetch_add(bytes_delta, Ordering::SeqCst),
Ordering::SeqCst
);
}
}
/// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer.
pub fn take_snapshot<W: SnapshotWriter + Send>(
chunker: Box<dyn SnapshotComponents>,
@@ -163,35 +208,34 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
let state_root = start_header.state_root();
let block_number = start_header.number();
info!("Taking snapshot starting at block {}", block_number);
info!("Taking snapshot starting at block #{}/{:?}", block_number, block_hash);
let version = chunker.current_version();
let writer = Mutex::new(writer);
let (state_hashes, block_hashes) = thread::scope(|scope| -> Result<(Vec<H256>, Vec<H256>), Error> {
let writer = &writer;
let block_guard = scope.spawn(move |_| {
let tb = scope.builder().name("Snapshot Worker - Blocks".to_string());
let block_guard = tb.spawn(move |_| {
chunk_secondary(chunker, chain, block_hash, writer, p)
});
})?;
// The number of threads must be between 1 and SNAPSHOT_SUBPARTS
assert!(processing_threads >= 1, "Cannot use less than 1 threads for creating snapshots");
let num_threads: usize = cmp::min(processing_threads, SNAPSHOT_SUBPARTS);
let num_threads = cmp::min(processing_threads, SNAPSHOT_SUBPARTS);
info!(target: "snapshot", "Using {} threads for Snapshot creation.", num_threads);
let mut state_guards = Vec::with_capacity(num_threads as usize);
let mut state_guards = Vec::with_capacity(num_threads);
for thread_idx in 0..num_threads {
let state_guard = scope.spawn(move |_| -> Result<Vec<H256>, Error> {
let tb = scope.builder().name(format!("Snapshot Worker #{} - State", thread_idx).to_string());
let state_guard = tb.spawn(move |_| -> Result<Vec<H256>, Error> {
let mut chunk_hashes = Vec::new();
for part in (thread_idx..SNAPSHOT_SUBPARTS).step_by(num_threads) {
debug!(target: "snapshot", "Chunking part {} in thread {}", part, thread_idx);
debug!(target: "snapshot", "Chunking part {} of the state at {} in thread {}", part, block_number, thread_idx);
let mut hashes = chunk_state(state_db, &state_root, writer, p, Some(part), thread_idx)?;
chunk_hashes.append(&mut hashes);
}
Ok(chunk_hashes)
});
})?;
state_guards.push(state_guard);
}
@@ -203,7 +247,8 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
state_hashes.extend(part_state_hashes);
}
debug!(target: "snapshot", "Took a snapshot of {} accounts", p.accounts.load(Ordering::SeqCst));
info!("Took a snapshot at #{} of {} accounts", block_number, p.accounts());
Ok((state_hashes, block_hashes))
}).expect("Sub-thread never panics; qed")?;
@@ -252,7 +297,7 @@ pub fn chunk_secondary<'a>(
trace!(target: "snapshot", "wrote secondary chunk. hash: {:x}, size: {}, uncompressed size: {}",
hash, size, raw_data.len());
progress.size.fetch_add(size as u64, Ordering::SeqCst);
progress.update(0, size);
chunk_hashes.push(hash);
Ok(())
};
@@ -309,8 +354,7 @@ impl<'a> StateChunker<'a> {
self.writer.lock().write_state_chunk(hash, compressed)?;
trace!(target: "snapshot", "Thread {} wrote state chunk. size: {}, uncompressed size: {}", self.thread_idx, compressed_size, raw_data.len());
self.progress.accounts.fetch_add(num_entries, Ordering::SeqCst);
self.progress.size.fetch_add(compressed_size as u64, Ordering::SeqCst);
self.progress.update(num_entries, compressed_size);
self.hashes.push(hash);
self.cur_size = 0;
@@ -361,7 +405,7 @@ pub fn chunk_state<'a>(
if let Some(part) = part {
assert!(part < 16, "Wrong chunk state part number (must be <16) in snapshot creation.");
let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS;
let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS; // 16
let mut seek_from = vec![0; 32];
seek_from[0] = (part * part_offset) as u8;
account_iter.seek(&seek_from)?;
@@ -383,7 +427,15 @@ pub fn chunk_state<'a>(
let account = ::rlp::decode(&*account_data)?;
let account_db = AccountDB::from_hash(db, account_key_hash);
let fat_rlps = account::to_fat_rlps(&account_key_hash, &account, &account_db, &mut used_code, PREFERRED_CHUNK_SIZE - chunker.chunk_size(), PREFERRED_CHUNK_SIZE, progress)?;
let fat_rlps = account::to_fat_rlps(
&account_key_hash,
&account,
&account_db,
&mut used_code,
PREFERRED_CHUNK_SIZE - chunker.chunk_size(),
PREFERRED_CHUNK_SIZE,
progress
)?;
for (i, fat_rlp) in fat_rlps.into_iter().enumerate() {
if i > 0 {
chunker.write_chunk()?;
@@ -479,7 +531,6 @@ impl StateRebuilder {
StateDB::commit_bloom(&mut batch, bloom_journal)?;
self.db.inject(&mut batch)?;
backing.write_buffered(batch);
trace!(target: "snapshot", "current state root: {:?}", self.state_root);
Ok(())
}

View File

@@ -24,7 +24,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::cmp;
use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, SnapshotService, MAX_CHUNK_SIZE};
use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, SnapshotService, Progress, MAX_CHUNK_SIZE};
use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter};
use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler};
@@ -38,10 +38,10 @@ use types::ids::BlockId;
use io::IoChannel;
use ethereum_types::H256;
use parking_lot::{Mutex, RwLock, RwLockReadGuard};
use bytes::Bytes;
use journaldb::Algorithm;
use kvdb::DBTransaction;
use parking_lot::{Mutex, RwLock, RwLockReadGuard};
use snappy;
use snapshot::error::Error::UnlinkedAncientBlockChain;
@@ -141,6 +141,7 @@ impl Restoration {
if let Some(ref mut writer) = self.writer.as_mut() {
writer.write_state_chunk(hash, chunk)?;
trace!(target: "snapshot", "Wrote {}/{} bytes of state to db/disk. Current state root: {:?}", len, chunk.len(), self.state.state_root());
}
self.state_chunks_left.remove(&hash);
@@ -194,7 +195,7 @@ impl Restoration {
}
self.guard.disarm();
trace!(target: "snapshot", "restoration finalised correctly");
trace!(target: "snapshot", "Restoration finalised correctly");
Ok(())
}
@@ -265,7 +266,7 @@ impl Service {
state_chunks: AtomicUsize::new(0),
block_chunks: AtomicUsize::new(0),
client: params.client,
progress: Default::default(),
progress: Progress::new(),
taking_snapshot: AtomicBool::new(false),
restoring_snapshot: AtomicBool::new(false),
};
@@ -398,7 +399,10 @@ impl Service {
Some(x) => x,
None => return Ok(0),
};
info!(target: "snapshot", "Migrating blocks from old db to new. Start: #{}/{:?}, Target: #{}/{:?}",
self.client.block_number(BlockId::Hash(start_hash)).unwrap_or_default(), start_hash,
self.client.block_number(BlockId::Hash(target_hash)).unwrap_or_default(), target_hash,
);
let mut batch = DBTransaction::new();
let mut parent_hash = start_hash;
while parent_hash != target_hash {
@@ -440,10 +444,10 @@ impl Service {
next_chain.commit();
next_db.key_value().flush().expect("DB flush failed.");
batch = DBTransaction::new();
}
if block_number % 10_000 == 0 {
info!(target: "snapshot", "Block restoration at #{}", block_number);
if block_number % 10_000 == 0 {
info!(target: "snapshot", "Block restoration at #{}", block_number);
}
}
}
@@ -468,11 +472,13 @@ impl Service {
if self.progress.done() || !self.taking_snapshot.load(Ordering::SeqCst) { return }
let p = &self.progress;
info!("Snapshot: {} accounts {} blocks {} bytes", p.accounts(), p.blocks(), p.size());
info!("Snapshot: {} accounts, {} blocks, {} bytes", p.accounts(), p.blocks(), p.bytes());
let rate = p.rate();
debug!(target: "snapshot", "Current progress rate: {:.0} acc/s, {:.0} bytes/s (compressed)", rate.0, rate.1);
}
/// Take a snapshot at the block with the given number.
/// calling this while a restoration is in progress or vice versa
/// Calling this while a restoration is in progress or vice versa
/// will lead to a race condition where the first one to finish will
/// have their produced snapshot overwritten.
pub fn take_snapshot(&self, client: &Client, num: u64) -> Result<(), Error> {
@@ -482,45 +488,40 @@ impl Service {
}
info!("Taking snapshot at #{}", num);
self.progress.reset();
{
scopeguard::defer! {{
self.taking_snapshot.store(false, Ordering::SeqCst);
}}
let start_time = std::time::Instant::now();
self.progress.reset();
let temp_dir = self.temp_snapshot_dir();
let snapshot_dir = self.snapshot_dir();
let temp_dir = self.temp_snapshot_dir();
let snapshot_dir = self.snapshot_dir();
let _ = fs::remove_dir_all(&temp_dir);
let _ = fs::remove_dir_all(&temp_dir); // expected to fail
let writer = LooseWriter::new(temp_dir.clone())?;
let writer = LooseWriter::new(temp_dir.clone())?;
let guard = Guard::new(temp_dir.clone());
let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress);
self.taking_snapshot.store(false, Ordering::SeqCst);
if let Err(e) = res {
if client.chain_info().best_block_number >= num + client.pruning_history() {
// The state we were snapshotting was pruned before we could finish.
info!("Periodic snapshot failed: block state pruned. Run with a longer `--pruning-history` or with `--no-periodic-snapshot`");
return Err(e);
} else {
return Err(e);
let guard = Guard::new(temp_dir.clone());
let _ = client.take_snapshot(writer, BlockId::Number(num), &self.progress)?;
info!("Finished taking snapshot at #{}, in {:.0?}", num, start_time.elapsed());
// destroy the old snapshot reader.
let mut reader = self.reader.write();
*reader = None;
if snapshot_dir.exists() {
trace!(target: "snapshot", "Removing previous snapshot at {:?}", &snapshot_dir);
fs::remove_dir_all(&snapshot_dir)?;
}
fs::rename(temp_dir, &snapshot_dir)?;
trace!(target: "snapshot", "Moved new snapshot into place at {:?}", &snapshot_dir);
*reader = Some(LooseReader::new(snapshot_dir)?);
guard.disarm();
Ok(())
}
info!("Finished taking snapshot at #{}", num);
let mut reader = self.reader.write();
// destroy the old snapshot reader.
*reader = None;
if snapshot_dir.exists() {
fs::remove_dir_all(&snapshot_dir)?;
}
fs::rename(temp_dir, &snapshot_dir)?;
*reader = Some(LooseReader::new(snapshot_dir)?);
guard.disarm();
Ok(())
}
/// Initialize the restoration synchronously.
@@ -639,13 +640,19 @@ impl Service {
Ok(())
}
/// Import a previous chunk at the given path. Returns whether the block was imported or not
fn import_prev_chunk(&self, restoration: &mut Option<Restoration>, manifest: &ManifestData, file: io::Result<fs::DirEntry>) -> Result<bool, Error> {
/// Import a previous chunk at the given path. Returns whether the chunk was imported or not
fn import_prev_chunk(
&self,
restoration: &mut Option<Restoration>,
manifest: &ManifestData,
file: io::Result<fs::DirEntry>
) -> Result<bool, Error> {
let file = file?;
let path = file.path();
let mut file = File::open(path.clone())?;
let mut buffer = Vec::new();
let filesize = file.metadata()?.len();
let mut buffer = Vec::with_capacity(filesize as usize + 1); // +1 for EOF
file.read_to_end(&mut buffer)?;
let hash = keccak(&buffer);
@@ -655,6 +662,7 @@ impl Service {
} else if manifest.state_hashes.contains(&hash) {
true
} else {
warn!(target: "snapshot", "Hash of the content of {:?} not present in the manifest block/state hashes.", path);
return Ok(false);
};
@@ -665,11 +673,10 @@ impl Service {
Ok(true)
}
// finalize the restoration. this accepts an already-locked
// restoration as an argument -- so acquiring it again _will_
// lead to deadlock.
// Finalize the restoration. This accepts an already-locked restoration as an argument -- so
// acquiring it again _will_ lead to deadlock.
fn finalize_restoration(&self, rest: &mut Option<Restoration>) -> Result<(), Error> {
trace!(target: "snapshot", "finalizing restoration");
trace!(target: "snapshot", "Finalizing restoration");
*self.status.lock() = RestorationStatus::Finalizing;
let recover = rest.as_ref().map_or(false, |rest| rest.writer.is_some());
@@ -680,7 +687,7 @@ impl Service {
.unwrap_or(Ok(()))?;
let migrated_blocks = self.migrate_blocks()?;
info!(target: "snapshot", "Migrated {} ancient blocks", migrated_blocks);
info!(target: "snapshot", "Migrated {} ancient blocks from the old DB", migrated_blocks);
// replace the Client's database with the new one (restart the Client).
self.client.restore_db(&*self.restoration_db().to_string_lossy())?;
@@ -692,11 +699,11 @@ impl Service {
let snapshot_dir = self.snapshot_dir();
if snapshot_dir.exists() {
trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy());
trace!(target: "snapshot", "Removing old snapshot dir at {}", snapshot_dir.to_string_lossy());
fs::remove_dir_all(&snapshot_dir)?;
}
trace!(target: "snapshot", "copying restored snapshot files over");
trace!(target: "snapshot", "Copying restored snapshot files over");
fs::rename(self.temp_recovery_dir(), &snapshot_dir)?;
*reader = Some(LooseReader::new(snapshot_dir)?);
@@ -767,7 +774,7 @@ impl Service {
false => Ok(())
}
}
other => other.map(drop),
Err(e) => Err(e)
};
(res, db)
}

View File

@@ -27,7 +27,7 @@ use types::basic_account::BasicAccount;
use blockchain::{BlockChain, BlockChainDB};
use client::{Client, ChainInfo};
use engines::Engine;
use snapshot::{StateRebuilder};
use snapshot::{StateRebuilder, Progress};
use snapshot::io::{SnapshotReader, PackedWriter, PackedReader};
use tempdir::TempDir;
@@ -138,7 +138,7 @@ pub fn snap(client: &Client) -> (Box<dyn SnapshotReader>, TempDir) {
let tempdir = TempDir::new("").unwrap();
let path = tempdir.path().join("file");
let writer = PackedWriter::new(&path).unwrap();
let progress = Default::default();
let progress = Progress::new();
let hash = client.chain_info().best_block_hash;
client.take_snapshot(writer, BlockId::Hash(hash), &progress).unwrap();

View File

@@ -66,7 +66,7 @@ fn chunk_and_restore(amount: u64) {
&bc,
best_hash,
&writer,
&Progress::default()
&Progress::new()
).unwrap();
let manifest = ::snapshot::ManifestData {

View File

@@ -178,7 +178,7 @@ fn keep_ancient_blocks() {
&bc,
best_hash,
&writer,
&Progress::default()
&Progress::new()
).unwrap();
let state_db = client.state_db().journal_db().boxed_clone();
let start_header = bc.block_header_data(&best_hash).unwrap();
@@ -187,7 +187,7 @@ fn keep_ancient_blocks() {
state_db.as_hash_db(),
&state_root,
&writer,
&Progress::default(),
&Progress::new(),
None,
0
).unwrap();

View File

@@ -21,11 +21,10 @@ use std::sync::atomic::AtomicBool;
use hash::{KECCAK_NULL_RLP, keccak};
use types::basic_account::BasicAccount;
use snapshot::account;
use snapshot::account::to_fat_rlps;
use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS};
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
use super::helpers::StateProducer;
use error::Error;
use rand::SeedableRng;
@@ -58,7 +57,7 @@ fn snap_and_restore() {
let mut state_hashes = Vec::new();
for part in 0..SNAPSHOT_SUBPARTS {
let mut hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), Some(part), 0).unwrap();
let mut hashes = chunk_state(&old_db, &state_root, &writer, &Progress::new(), Some(part), 0).unwrap();
state_hashes.append(&mut hashes);
}
@@ -129,8 +128,8 @@ fn get_code_from_prev_chunk() {
let mut make_chunk = |acc, hash| {
let mut db = journaldb::new_memory_db();
AccountDBMut::from_hash(&mut db, hash).insert(EMPTY_PREFIX, &code[..]);
let p = Progress::default();
let fat_rlp = account::to_fat_rlps(&hash, &acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value(), usize::max_value(), &p).unwrap();
let p = Progress::new();
let fat_rlp = to_fat_rlps(&hash, &acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value(), usize::max_value(), &p).unwrap();
let mut stream = RlpStream::new_list(1);
stream.append_raw(&fat_rlp[0], 1);
stream.out()
@@ -174,7 +173,7 @@ fn checks_flag() {
let state_root = producer.state_root();
let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap());
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), None, 0).unwrap();
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::new(), None, 0).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
version: 2,

View File

@@ -17,6 +17,7 @@
//! Parameters for a block chain.
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
@@ -534,19 +535,28 @@ impl From<SpecHardcodedSync> for ethjson::spec::HardcodedSync {
}
fn load_machine_from(s: ethjson::spec::Spec) -> Machine {
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), Builtin::try_from(p.1).expect("chain spec is invalid"))).collect();
let params = CommonParams::from(s.params);
Spec::machine(&s.engine, params, builtins)
}
fn convert_json_to_spec(
(address, builtin): (ethjson::hash::Address, ethjson::spec::builtin::Builtin),
) -> Result<(Address, Builtin), Error> {
let builtin = Builtin::try_from(builtin)?;
Ok((address.into(), builtin))
}
/// Load from JSON object.
fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result<Spec, Error> {
let builtins = s.accounts
let builtins: Result<BTreeMap<Address, Builtin>, _> = s
.accounts
.builtins()
.into_iter()
.map(|p| (p.0.into(), From::from(p.1)))
.map(convert_json_to_spec)
.collect();
let builtins = builtins?;
let g = Genesis::from(s.genesis);
let GenericSeal(seal_rlp) = g.seal.into();
let params = CommonParams::from(s.params);
@@ -970,6 +980,10 @@ impl Spec {
#[cfg(any(test, feature = "test-helpers"))]
pub fn new_test_with_reward() -> Spec { load_bundled!("null_morden_with_reward") }
/// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus with finality
#[cfg(any(test, feature = "test-helpers"))]
pub fn new_test_with_finality() -> Spec { load_bundled!("null_morden_with_finality") }
/// Create a new Spec which is a NullEngine consensus with a premine of address whose
/// secret is keccak('').
#[cfg(any(test, feature = "test-helpers"))]

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
use std::str::{FromStr, from_utf8};
use std::sync::Arc;
use ethereum_types::{U256, Address};
@@ -22,23 +22,29 @@ use ethkey::KeyPair;
use hash::keccak;
use io::IoChannel;
use tempdir::TempDir;
use types::transaction::{PendingTransaction, Transaction, Action, Condition};
use types::filter::Filter;
use types::view;
use types::views::BlockView;
use client::{BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock};
use client::{
BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, BlockInfo,
PrepareOpenBlock, ImportSealedBlock, ImportBlock, ImportExportBlocks
};
use ethereum;
use executive::{Executive, TransactOptions};
use types::{
data_format::DataFormat,
transaction::{PendingTransaction, Transaction, Action, Condition},
filter::Filter,
view,
views::BlockView,
};
use miner::{Miner, PendingOrdering, MinerService};
use spec::Spec;
use state::{self, State, CleanupMode};
use state::{self, State, StateInfo, CleanupMode};
use test_helpers::{
self,
generate_dummy_client, push_blocks_to_client, get_test_client_with_blocks, get_good_dummy_block_seq,
generate_dummy_client_with_data, get_good_dummy_block, get_bad_state_dummy_block
};
use verification::queue::kind::blocks::Unverified;
use rustc_hex::ToHex;
#[test]
fn imports_from_empty() {
@@ -383,3 +389,79 @@ fn reset_blockchain() {
assert!(client.block_header(BlockId::Number(15)).is_some());
}
#[test]
fn import_export_hex() {
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
let block_rlps = (15..20)
.filter_map(|num| client.block(BlockId::Number(num)))
.map(|header| {
header.raw().to_hex()
})
.collect::<Vec<_>>();
let mut out = Vec::new();
client.export_blocks(
Box::new(&mut out),
BlockId::Number(15),
BlockId::Number(20),
Some(DataFormat::Hex)
).unwrap();
let written = from_utf8(&out)
.unwrap()
.split("\n")
// last line is empty, ignore it.
.take(5)
.collect::<Vec<_>>();
assert_eq!(block_rlps, written);
assert!(client.reset(5).is_ok());
client.chain().clear_cache();
assert!(client.block_header(BlockId::Number(20)).is_none());
assert!(client.block_header(BlockId::Number(19)).is_none());
assert!(client.block_header(BlockId::Number(18)).is_none());
assert!(client.block_header(BlockId::Number(17)).is_none());
assert!(client.block_header(BlockId::Number(16)).is_none());
client.import_blocks(Box::new(&*out), Some(DataFormat::Hex)).unwrap();
assert!(client.block_header(BlockId::Number(20)).is_some());
assert!(client.block_header(BlockId::Number(19)).is_some());
assert!(client.block_header(BlockId::Number(18)).is_some());
assert!(client.block_header(BlockId::Number(17)).is_some());
assert!(client.block_header(BlockId::Number(16)).is_some());
}
#[test]
fn import_export_binary() {
let client = get_test_client_with_blocks(get_good_dummy_block_seq(19));
let mut out = Vec::new();
client.export_blocks(
Box::new(&mut out),
BlockId::Number(15),
BlockId::Number(20),
Some(DataFormat::Binary)
).unwrap();
assert!(client.reset(5).is_ok());
client.chain().clear_cache();
assert!(client.block_header(BlockId::Number(20)).is_none());
assert!(client.block_header(BlockId::Number(19)).is_none());
assert!(client.block_header(BlockId::Number(18)).is_none());
assert!(client.block_header(BlockId::Number(17)).is_none());
assert!(client.block_header(BlockId::Number(16)).is_none());
client.import_blocks(Box::new(&*out), Some(DataFormat::Binary)).unwrap();
assert!(client.block_header(BlockId::Number(19)).is_some());
assert!(client.block_header(BlockId::Number(18)).is_some());
assert!(client.block_header(BlockId::Number(20)).is_some());
assert!(client.block_header(BlockId::Number(17)).is_some());
assert!(client.block_header(BlockId::Number(16)).is_some());
}