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:
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()?)),
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -66,7 +66,7 @@ fn chunk_and_restore(amount: u64) {
|
||||
&bc,
|
||||
best_hash,
|
||||
&writer,
|
||||
&Progress::default()
|
||||
&Progress::new()
|
||||
).unwrap();
|
||||
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user