v2.5.10 stable (#11239)
* ropsten #6631425 foundation #8798209 (#11201) * [stable] builtin, istanbul and mordor testnet backports (#11234) * ethcore-builtin (#10850) * [builtin]: support `multiple prices and activations` in chain spec (#11039) * [chain specs]: activate `Istanbul` on mainnet (#11228) * ethcore/res: add mordor testnet configuration (#11200) * Update list of bootnodes for xDai chain (#11236) * ethcore: remove `test-helper feat` from build (#11047) * Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) * [stable]: backport #10691 and #10683 (#11143) * Fix compiler warning (that will become an error) (#10683) * Refactor Clique stepping (#10691) * Add Constantinople eips to the dev (instant_seal) config (#10809) * Add cargo-remote dir to .gitignore (?) * Insert explicit warning into the panic hook (#11225) * Fix docker centos build (#11226) * Update MIX bootnodes. (#11203) * Use provided usd-per-eth value if an endpoint is specified (#11209) * Add new line after writing block to hex file. (#10984) * Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) * Upgrade jsonrpc to latest (#11206) * [CI] check evmbin build (#11096) * Correct EIP-712 encoding (#11092) * [client]: Fix for incorrectly dropped consensus messages (#11086) * Fix block detail updating (#11015) * Switching sccache from local to Redis (#10971) * Made ecrecover implementation trait public (#11188) * [dependencies]: jsonrpc `14.0.1` (#11183) * [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) * [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) * 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) * Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) * Cleanup stratum a bit (#11161) * Upgrade to jsonrpc v14 (#11151) * SecretStore: expose restore_key_public in HTTP API (#10241)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -17,23 +17,27 @@
|
||||
use std::cmp;
|
||||
use std::collections::{HashSet, BTreeMap, VecDeque};
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::str::from_utf8;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicI64, AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::{Instant, Duration};
|
||||
use std::io::{BufReader, BufRead};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey};
|
||||
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 error::Error;
|
||||
use ethereum_types::{Address, H256, H264, U256};
|
||||
use hash::keccak;
|
||||
use io::IoChannel;
|
||||
use call_contract::CallContract;
|
||||
use ethcore_miner::pool::VerifiedTransaction;
|
||||
use itertools::Itertools;
|
||||
use journaldb;
|
||||
use kvdb::{DBValue, KeyValueDB, DBTransaction};
|
||||
use kvdb::{DBTransaction, DBValue, KeyValueDB};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rand::OsRng;
|
||||
use rlp::PayloadInfo;
|
||||
use rustc_hex::FromHex;
|
||||
use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action};
|
||||
use trie::{TrieSpec, TrieFactory, Trie};
|
||||
use types::ancestry_action::AncestryAction;
|
||||
@@ -43,6 +47,7 @@ use types::log_entry::LocalizedLogEntry;
|
||||
use types::receipt::{Receipt, LocalizedReceipt};
|
||||
use types::{BlockNumber, header::{Header, ExtendedHeader}};
|
||||
use vm::{EnvInfo, LastHashes};
|
||||
use types::data_format::DataFormat;
|
||||
|
||||
use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
|
||||
use client::ancient_import::AncientVerifier;
|
||||
@@ -51,19 +56,19 @@ use client::{
|
||||
ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock,
|
||||
BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call,
|
||||
AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter,
|
||||
ClientIoMessage, BlockChainReset
|
||||
ClientIoMessage, BlockChainReset, ImportExportBlocks
|
||||
};
|
||||
use client::{
|
||||
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
||||
TraceFilter, CallAnalytics, Mode,
|
||||
ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
|
||||
IoClient, BadBlocks,
|
||||
IoClient, BadBlocks, traits::ForceUpdateSealing
|
||||
};
|
||||
use client::bad_blocks;
|
||||
use engines::{MAX_UNCLE_AGE, EthEngine, EpochTransition, ForkChoice, EngineError};
|
||||
use engines::epoch::PendingTransition;
|
||||
use error::{
|
||||
ImportErrorKind, ExecutionError, CallError, BlockError,
|
||||
ImportErrorKind, ExecutionError, CallError, BlockError, ImportError,
|
||||
QueueError, QueueErrorKind, Error as EthcoreError, EthcoreResult, ErrorKind as EthcoreErrorKind
|
||||
};
|
||||
use executive::{Executive, Executed, TransactOptions, contract_address};
|
||||
@@ -80,7 +85,9 @@ use verification::queue::kind::blocks::Unverified;
|
||||
use verification::{PreverifiedBlock, Verifier, BlockQueue};
|
||||
use verification;
|
||||
use ansi_term::Colour;
|
||||
|
||||
use call_contract::RegistryInfo;
|
||||
use io::IoChannel;
|
||||
use vm::Schedule;
|
||||
// re-export
|
||||
pub use types::blockchain_info::BlockChainInfo;
|
||||
pub use types::block_status::BlockStatus;
|
||||
@@ -153,7 +160,7 @@ struct Importer {
|
||||
pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead?
|
||||
|
||||
/// Used to verify blocks
|
||||
pub verifier: Box<Verifier<Client>>,
|
||||
pub verifier: Box<dyn Verifier<Client>>,
|
||||
|
||||
/// Queue containing pending blocks
|
||||
pub block_queue: BlockQueue,
|
||||
@@ -196,7 +203,7 @@ pub struct Client {
|
||||
pruning: journaldb::Algorithm,
|
||||
|
||||
/// Client uses this to store blocks, traces, etc.
|
||||
db: RwLock<Arc<BlockChainDB>>,
|
||||
db: RwLock<Arc<dyn BlockChainDB>>,
|
||||
|
||||
state_db: RwLock<StateDB>,
|
||||
|
||||
@@ -210,7 +217,7 @@ pub struct Client {
|
||||
io_channel: RwLock<IoChannel<ClientIoMessage>>,
|
||||
|
||||
/// List of actors to be notified on certain chain events
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
notify: RwLock<Vec<Weak<dyn ChainNotify>>>,
|
||||
|
||||
/// Queued transactions from IO
|
||||
queue_transactions: IoChannelQueue,
|
||||
@@ -232,12 +239,12 @@ pub struct Client {
|
||||
history: u64,
|
||||
|
||||
/// An action to be done if a mode/spec_name change happens
|
||||
on_user_defaults_change: Mutex<Option<Box<FnMut(Option<Mode>) + 'static + Send>>>,
|
||||
on_user_defaults_change: Mutex<Option<Box<dyn FnMut(Option<Mode>) + 'static + Send>>>,
|
||||
|
||||
registrar_address: Option<Address>,
|
||||
|
||||
/// A closure to call when we want to restart the client
|
||||
exit_handler: Mutex<Option<Box<Fn(String) + 'static + Send>>>,
|
||||
exit_handler: Mutex<Option<Box<dyn Fn(String) + 'static + Send>>>,
|
||||
|
||||
importer: Importer,
|
||||
}
|
||||
@@ -248,8 +255,13 @@ impl Importer {
|
||||
engine: Arc<EthEngine>,
|
||||
message_channel: IoChannel<ClientIoMessage>,
|
||||
miner: Arc<Miner>,
|
||||
) -> Result<Importer, ::error::Error> {
|
||||
let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal());
|
||||
) -> Result<Importer, EthcoreError> {
|
||||
let block_queue = BlockQueue::new(
|
||||
config.queue.clone(),
|
||||
engine.clone(),
|
||||
message_channel.clone(),
|
||||
config.verifier_type.verifying_seal()
|
||||
);
|
||||
|
||||
Ok(Importer {
|
||||
import_lock: Mutex::new(()),
|
||||
@@ -475,7 +487,16 @@ impl Importer {
|
||||
//
|
||||
// The header passed is from the original block data and is sealed.
|
||||
// TODO: should return an error if ImportRoute is none, issue #9910
|
||||
fn commit_block<B>(&self, block: B, header: &Header, block_data: encoded::Block, pending: Option<PendingTransition>, client: &Client) -> ImportRoute where B: Drain {
|
||||
fn commit_block<B>(
|
||||
&self,
|
||||
block: B,
|
||||
header: &Header,
|
||||
block_data: encoded::Block,
|
||||
pending: Option<PendingTransition>,
|
||||
client: &Client
|
||||
) -> ImportRoute
|
||||
where B: Drain
|
||||
{
|
||||
let hash = &header.hash();
|
||||
let number = header.number();
|
||||
let parent = header.parent_hash();
|
||||
@@ -1017,15 +1038,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.
|
||||
@@ -1035,9 +1057,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) {
|
||||
@@ -1071,8 +1093,9 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Get a copy of the best block's state.
|
||||
pub fn state(&self) -> Box<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.
|
||||
@@ -1525,8 +1548,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> {
|
||||
@@ -2079,7 +2102,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>> {
|
||||
@@ -2401,7 +2424,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
|
||||
@@ -2489,19 +2514,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> {
|
||||
@@ -2558,13 +2586,139 @@ impl ProvingBlockChainClient for Client {
|
||||
|
||||
impl SnapshotClient for Client {}
|
||||
|
||||
impl Drop for Client {
|
||||
fn drop(&mut self) {
|
||||
if let Some(c) = Arc::get_mut(&mut self.engine) {
|
||||
c.stop()
|
||||
} else {
|
||||
warn!(target: "shutdown", "unable to get mut ref for engine for shutdown.");
|
||||
|
||||
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(Error(EthcoreErrorKind::Import(ImportErrorKind::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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2613,8 +2767,52 @@ fn transaction_receipt(
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue some items to be processed by IO client.
|
||||
struct IoChannelQueue {
|
||||
/// 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) -> EthcoreResult<()> where
|
||||
F: Fn(&Client) + Send + Sync + 'static,
|
||||
{
|
||||
let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed);
|
||||
if queue_size >= self.limit {
|
||||
let err_limit = usize::try_from(self.limit).unwrap_or(usize::max_value());
|
||||
bail!("The queue is full ({})", err_limit);
|
||||
};
|
||||
|
||||
let count = i64::try_from(count).unwrap_or(i64::max_value());
|
||||
|
||||
let currently_queued = self.currently_queued.clone();
|
||||
let _ok = channel.send(ClientIoMessage::execute(move |client| {
|
||||
currently_queued.fetch_sub(count, AtomicOrdering::SeqCst);
|
||||
fun(client);
|
||||
}))?;
|
||||
|
||||
self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test_helpers::{generate_dummy_client, generate_dummy_client_with_data, generate_dummy_client_with_spec_and_data, get_good_dummy_block_hash};
|
||||
use blockchain::{BlockProvider, ExtrasInsert};
|
||||
use spec::Spec;
|
||||
|
||||
#[test]
|
||||
fn should_not_cache_details_before_commit() {
|
||||
@@ -2626,7 +2824,6 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use kvdb::DBTransaction;
|
||||
use blockchain::ExtrasInsert;
|
||||
use types::encoded;
|
||||
|
||||
let client = generate_dummy_client(0);
|
||||
@@ -2768,40 +2965,23 @@ mod tests {
|
||||
outcome: TransactionOutcome::StateRoot(state_root),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue some items to be processed by IO client.
|
||||
struct IoChannelQueue {
|
||||
currently_queued: Arc<AtomicUsize>,
|
||||
limit: usize,
|
||||
}
|
||||
#[test]
|
||||
fn should_mark_finalization_correctly_for_parent() {
|
||||
let client = generate_dummy_client_with_spec_and_data(Spec::new_test_with_finality, 2, 0, &[]);
|
||||
let chain = client.chain();
|
||||
|
||||
impl IoChannelQueue {
|
||||
pub fn new(limit: usize) -> Self {
|
||||
IoChannelQueue {
|
||||
currently_queued: Default::default(),
|
||||
limit,
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
pub fn queue<F>(&self, channel: &IoChannel<ClientIoMessage>, count: usize, fun: F) -> Result<(), QueueError> where
|
||||
F: Fn(&Client) + Send + Sync + 'static,
|
||||
{
|
||||
let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed);
|
||||
ensure!(queue_size < self.limit, QueueErrorKind::Full(self.limit));
|
||||
|
||||
let currently_queued = self.currently_queued.clone();
|
||||
let result = 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) => bail!(QueueErrorKind::Channel(e)),
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ mod config;
|
||||
mod evm_test_client;
|
||||
mod io_message;
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
mod test_client;
|
||||
pub mod test_client;
|
||||
mod trace;
|
||||
|
||||
pub use self::client::*;
|
||||
@@ -38,7 +38,7 @@ pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType,
|
||||
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
|
||||
};
|
||||
pub use state::StateInfo;
|
||||
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient};
|
||||
|
||||
@@ -56,7 +56,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, traits::ForceUpdateSealing
|
||||
};
|
||||
use engines::EthEngine;
|
||||
use error::{Error, EthcoreResult};
|
||||
@@ -586,7 +586,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,7 +605,10 @@ impl Call for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
impl StateInfo for () {
|
||||
/// 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!() }
|
||||
@@ -614,14 +617,14 @@ 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 = ();
|
||||
type State = TestState;
|
||||
|
||||
fn latest_state(&self) -> Self::State {
|
||||
()
|
||||
fn latest_state_and_header(&self) -> (Self::State, Header) {
|
||||
(TestState, self.best_block_header())
|
||||
}
|
||||
|
||||
fn state_at(&self, _id: BlockId) -> Option<Self::State> {
|
||||
Some(())
|
||||
Some(TestState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -928,8 +931,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>) {
|
||||
|
||||
@@ -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;
|
||||
@@ -144,8 +145,8 @@ pub trait StateClient {
|
||||
/// Type representing chain state
|
||||
type State: StateInfo;
|
||||
|
||||
/// Get a copy of the best block's state.
|
||||
fn latest_state(&self) -> Self::State;
|
||||
/// Get a copy of the best block's state and header.
|
||||
fn latest_state_and_header(&self) -> (Self::State, Header);
|
||||
|
||||
/// Attempt to get a copy of a specific block's final state.
|
||||
///
|
||||
@@ -422,10 +423,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>);
|
||||
@@ -477,3 +487,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, traits::ForceUpdateSealing};
|
||||
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
|
||||
use engines::block_reward;
|
||||
use engines::block_reward::{BlockRewardContract, RewardKind};
|
||||
@@ -908,7 +908,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -936,7 +936,7 @@ impl Engine<EthereumMachine> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use block::ExecutedBlock;
|
||||
use bytes::Bytes;
|
||||
use client::{BlockId, EngineClient};
|
||||
use client::{BlockId, EngineClient, traits::ForceUpdateSealing};
|
||||
use engines::clique::util::{extract_signers, recover_creator};
|
||||
use engines::{Engine, EngineError, Seal};
|
||||
use error::{BlockError, Error};
|
||||
@@ -88,11 +88,9 @@ use types::header::{ExtendedHeader, Header};
|
||||
|
||||
use self::block_state::CliqueBlockState;
|
||||
use self::params::CliqueParams;
|
||||
use self::step_service::StepService;
|
||||
|
||||
mod params;
|
||||
mod block_state;
|
||||
mod step_service;
|
||||
mod util;
|
||||
|
||||
// TODO(niklasad1): extract tester types into a separate mod to be shared in the code base
|
||||
@@ -167,7 +165,6 @@ pub struct Clique {
|
||||
block_state_by_hash: RwLock<LruCache<H256, CliqueBlockState>>,
|
||||
proposals: RwLock<HashMap<Address, VoteType>>,
|
||||
signer: RwLock<Option<Box<EngineSigner>>>,
|
||||
step_service: Option<Arc<StepService>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -180,30 +177,45 @@ pub struct Clique {
|
||||
pub block_state_by_hash: RwLock<LruCache<H256, CliqueBlockState>>,
|
||||
pub proposals: RwLock<HashMap<Address, VoteType>>,
|
||||
pub signer: RwLock<Option<Box<EngineSigner>>>,
|
||||
pub step_service: Option<Arc<StepService>>,
|
||||
}
|
||||
|
||||
impl Clique {
|
||||
/// Initialize Clique engine from empty state.
|
||||
pub fn new(our_params: CliqueParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
|
||||
let mut engine = Clique {
|
||||
epoch_length: our_params.epoch,
|
||||
period: our_params.period,
|
||||
pub fn new(params: CliqueParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
|
||||
/// Step Clique at most every 2 seconds
|
||||
const SEALING_FREQ: Duration = Duration::from_secs(2);
|
||||
|
||||
let engine = Clique {
|
||||
epoch_length: params.epoch,
|
||||
period: params.period,
|
||||
client: Default::default(),
|
||||
block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)),
|
||||
proposals: Default::default(),
|
||||
signer: Default::default(),
|
||||
machine,
|
||||
step_service: None,
|
||||
};
|
||||
let engine = Arc::new(engine);
|
||||
let weak_eng = Arc::downgrade(&engine);
|
||||
|
||||
let res = Arc::new(engine);
|
||||
thread::Builder::new().name("StepService".into())
|
||||
.spawn(move || {
|
||||
loop {
|
||||
let next_step_at = Instant::now() + SEALING_FREQ;
|
||||
trace!(target: "miner", "StepService: triggering sealing");
|
||||
if let Some(eng) = weak_eng.upgrade() {
|
||||
eng.step()
|
||||
} else {
|
||||
warn!(target: "shutdown", "StepService: engine is dropped; exiting.");
|
||||
break;
|
||||
}
|
||||
|
||||
if our_params.period > 0 {
|
||||
engine.step_service = Some(StepService::start(Arc::downgrade(&res) as Weak<Engine<_>>));
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
let now = Instant::now();
|
||||
if now < next_step_at {
|
||||
thread::sleep(next_step_at - now);
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(engine)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -221,7 +233,6 @@ impl Clique {
|
||||
proposals: Default::default(),
|
||||
signer: Default::default(),
|
||||
machine: Spec::new_test_machine(),
|
||||
step_service: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +706,7 @@ impl Engine<EthereumMachine> for Clique {
|
||||
trace!(target: "engine", "populate_from_parent in sealing");
|
||||
|
||||
// It's unclear how to prevent creating new blocks unless we are authorized, the best way (and geth does this too)
|
||||
// it's just to ignore setting an correct difficulty here, we will check authorization in next step in generate_seal anyway.
|
||||
// it's just to ignore setting a correct difficulty here, we will check authorization in next step in generate_seal anyway.
|
||||
if let Some(signer) = self.signer.read().as_ref() {
|
||||
let state = match self.state(&parent) {
|
||||
Err(e) => {
|
||||
@@ -738,20 +749,12 @@ impl Engine<EthereumMachine> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
if let Some(mut s) = self.step_service.as_mut() {
|
||||
Arc::get_mut(&mut s).map(|x| x.stop());
|
||||
} else {
|
||||
warn!(target: "engine", "Stopping `CliqueStepService` failed requires mutable access");
|
||||
}
|
||||
}
|
||||
|
||||
/// Clique timestamp is set to parent + period , or current time which ever is higher.
|
||||
fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 {
|
||||
let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default();
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use std::sync::Weak;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
use std::sync::Arc;
|
||||
|
||||
use engines::Engine;
|
||||
use machine::Machine;
|
||||
|
||||
/// Service that is managing the engine
|
||||
pub struct StepService {
|
||||
shutdown: Arc<AtomicBool>,
|
||||
thread: Option<thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl StepService {
|
||||
/// Start the `StepService`
|
||||
pub fn start<M: Machine + 'static>(engine: Weak<Engine<M>>) -> Arc<Self> {
|
||||
let shutdown = Arc::new(AtomicBool::new(false));
|
||||
let s = shutdown.clone();
|
||||
|
||||
let thread = thread::Builder::new()
|
||||
.name("CliqueStepService".into())
|
||||
.spawn(move || {
|
||||
// startup delay.
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
|
||||
loop {
|
||||
// see if we are in shutdown.
|
||||
if shutdown.load(Ordering::Acquire) {
|
||||
trace!(target: "miner", "CliqueStepService: received shutdown signal!");
|
||||
break;
|
||||
}
|
||||
|
||||
trace!(target: "miner", "CliqueStepService: triggering sealing");
|
||||
|
||||
// Try sealing
|
||||
engine.upgrade().map(|x| x.step());
|
||||
|
||||
// Yield
|
||||
thread::sleep(Duration::from_millis(2000));
|
||||
}
|
||||
trace!(target: "miner", "CliqueStepService: shutdown.");
|
||||
}).expect("CliqueStepService thread failed");
|
||||
|
||||
Arc::new(StepService {
|
||||
shutdown: s,
|
||||
thread: Some(thread),
|
||||
})
|
||||
}
|
||||
|
||||
/// Stop the `StepService`
|
||||
pub fn stop(&mut self) {
|
||||
trace!(target: "miner", "CliqueStepService: shutting down.");
|
||||
self.shutdown.store(true, Ordering::Release);
|
||||
if let Some(t) = self.thread.take() {
|
||||
t.join().expect("CliqueStepService thread panicked!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,13 @@ impl<M: Machine> Engine<M> for InstantSeal<M> {
|
||||
|
||||
fn seals_internally(&self) -> Option<bool> { Some(true) }
|
||||
|
||||
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();
|
||||
|
||||
@@ -307,6 +307,14 @@ pub trait Engine<M: Machine>: Sync + Send {
|
||||
/// Some(false) means that the node might seal internally but is not qualified now.
|
||||
fn seals_internally(&self) -> Option<bool> { None }
|
||||
|
||||
/// 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.
|
||||
@@ -425,9 +433,6 @@ pub trait Engine<M: Machine>: Sync + Send {
|
||||
/// Trigger next step of the consensus engine.
|
||||
fn step(&self) {}
|
||||
|
||||
/// Stops any services that the may hold the Engine and makes it safe to drop.
|
||||
fn stop(&mut self) {}
|
||||
|
||||
/// Create a factory for building snapshot chunks and restoring from them.
|
||||
/// Returning `None` indicates that this engine doesn't support snapshot creation.
|
||||
fn snapshot_components(&self) -> Option<Box<SnapshotComponents>> {
|
||||
|
||||
@@ -20,6 +20,7 @@ use ethereum_types::U256;
|
||||
use machine::Machine;
|
||||
use types::BlockNumber;
|
||||
use types::header::{Header, ExtendedHeader};
|
||||
use types::ancestry_action::AncestryAction;
|
||||
use block::ExecutedBlock;
|
||||
|
||||
/// Params for a null engine.
|
||||
@@ -27,12 +28,15 @@ use block::ExecutedBlock;
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,4 +110,13 @@ impl<M: Machine> Engine<M> for NullEngine<M> {
|
||||
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, ImportBlock, traits::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);
|
||||
::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);
|
||||
::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);
|
||||
::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);
|
||||
::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);
|
||||
::client::EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
|
||||
assert_eq!(client.chain_info().best_block_number, 3);
|
||||
|
||||
// Check syncing.
|
||||
|
||||
@@ -447,7 +447,7 @@ mod tests {
|
||||
use spec::Spec;
|
||||
use accounts::AccountProvider;
|
||||
use types::transaction::{Transaction, Action};
|
||||
use client::{ChainInfo, BlockInfo, ImportBlock};
|
||||
use client::{ChainInfo, BlockInfo, ImportBlock, traits::{ForceUpdateSealing, EngineClient}};
|
||||
use ethkey::Secret;
|
||||
use miner::{self, MinerService};
|
||||
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
|
||||
@@ -488,7 +488,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 {
|
||||
@@ -500,14 +500,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()));
|
||||
@@ -521,7 +521,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 Morodor 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.
|
||||
|
||||
@@ -406,7 +406,7 @@ impl<'a> CallCreateExecutive<'a> {
|
||||
if let Err(e) = result {
|
||||
state.revert_to_checkpoint();
|
||||
|
||||
Err(e.into())
|
||||
Err(vm::Error::BuiltIn(e))
|
||||
} else {
|
||||
state.discard_checkpoint();
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#![recursion_limit="128"]
|
||||
|
||||
extern crate ansi_term;
|
||||
extern crate bn;
|
||||
extern crate byteorder;
|
||||
extern crate common_types as types;
|
||||
extern crate crossbeam_utils;
|
||||
@@ -67,6 +66,7 @@ extern crate ethabi;
|
||||
extern crate ethash;
|
||||
extern crate ethcore_blockchain as blockchain;
|
||||
extern crate ethcore_bloom_journal as bloom_journal;
|
||||
extern crate ethcore_builtin as builtin;
|
||||
extern crate ethcore_call_contract as call_contract;
|
||||
extern crate ethcore_db as db;
|
||||
extern crate ethcore_io as io;
|
||||
@@ -86,10 +86,8 @@ extern crate len_caching_lock;
|
||||
extern crate lru_cache;
|
||||
extern crate memory_cache;
|
||||
extern crate memory_db;
|
||||
extern crate num;
|
||||
extern crate num_cpus;
|
||||
extern crate parity_bytes as bytes;
|
||||
extern crate parity_crypto;
|
||||
extern crate parity_snappy as snappy;
|
||||
extern crate parking_lot;
|
||||
extern crate trie_db as trie;
|
||||
@@ -154,7 +152,6 @@ extern crate fetch;
|
||||
extern crate parity_runtime;
|
||||
|
||||
pub mod block;
|
||||
pub mod builtin;
|
||||
pub mod client;
|
||||
pub mod engines;
|
||||
pub mod error;
|
||||
|
||||
@@ -52,7 +52,7 @@ use client::{
|
||||
BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce, TransactionInfo, TransactionId
|
||||
};
|
||||
use client::{BlockId, ClientIoMessage};
|
||||
use client::traits::EngineClient;
|
||||
use client::traits::{EngineClient, ForceUpdateSealing};
|
||||
use engines::{EthEngine, Seal, EngineSigner};
|
||||
use error::{Error, ErrorKind};
|
||||
use executed::ExecutionError;
|
||||
@@ -276,6 +276,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 {
|
||||
@@ -294,7 +295,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
|
||||
@@ -829,7 +830,7 @@ impl Miner {
|
||||
preparation_status
|
||||
}
|
||||
|
||||
/// Prepare pending block, check whether sealing is needed, and then update sealing.
|
||||
/// Prepare pending block, check whether sealing is needed, and then update sealing.
|
||||
fn prepare_and_update_sealing<C: miner::BlockChainClient>(&self, chain: &C) {
|
||||
|
||||
// Make sure to do it after transaction is imported and lock is dropped.
|
||||
@@ -838,7 +839,7 @@ impl Miner {
|
||||
// If new block has not been prepared (means we already had one)
|
||||
// or Engine might be able to seal internally,
|
||||
// we need to update sealing.
|
||||
self.update_sealing(chain);
|
||||
self.update_sealing(chain, ForceUpdateSealing::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1109,6 +1110,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,
|
||||
@@ -1131,14 +1137,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;
|
||||
}
|
||||
|
||||
@@ -1168,6 +1176,7 @@ impl miner::MinerService for Miner {
|
||||
if self.seal_and_import_block_internally(chain, block) {
|
||||
trace!(target: "miner", "update_sealing: imported internally sealed block");
|
||||
}
|
||||
return
|
||||
},
|
||||
Some(false) => {
|
||||
trace!(target: "miner", "update_sealing: engine is not keen to seal internally right now");
|
||||
@@ -1176,9 +1185,9 @@ impl miner::MinerService for Miner {
|
||||
},
|
||||
None => {
|
||||
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 {
|
||||
@@ -1279,7 +1288,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1307,8 +1316,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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1317,8 +1327,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1638,14 +1649,14 @@ mod tests {
|
||||
let import = miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).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);
|
||||
|
||||
assert!(miner.import_own_transaction(&*client, 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);
|
||||
@@ -1673,7 +1684,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());
|
||||
}
|
||||
@@ -1684,7 +1695,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());
|
||||
}
|
||||
@@ -1695,7 +1706,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());
|
||||
}
|
||||
@@ -1714,7 +1725,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());
|
||||
}
|
||||
@@ -1753,6 +1764,7 @@ mod tests {
|
||||
},
|
||||
fetch,
|
||||
p,
|
||||
"fake_endpoint".to_owned()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ use call_contract::{CallContract, RegistryInfo};
|
||||
use client::{
|
||||
ScheduleInfo,
|
||||
BlockChain, BlockProducer, SealedBlockImporter, ChainInfo,
|
||||
AccountData, Nonce,
|
||||
AccountData, Nonce, traits::ForceUpdateSealing
|
||||
};
|
||||
use error::Error;
|
||||
use state::StateInfo;
|
||||
@@ -83,7 +83,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
|
||||
|
||||
@@ -217,8 +217,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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -231,16 +229,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
|
||||
|
||||
@@ -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) -> EthereumMachine {
|
||||
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);
|
||||
@@ -967,6 +977,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,32 @@ 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 types::{
|
||||
data_format::DataFormat,
|
||||
ids::BlockId,
|
||||
transaction::{PendingTransaction, Transaction, Action, Condition},
|
||||
filter::Filter,
|
||||
view,
|
||||
views::BlockView,
|
||||
};
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
use client::{Client, ClientConfig, PrepareOpenBlock, ImportSealedBlock};
|
||||
use client::traits::{
|
||||
BlockInfo, BlockChainClient, BlockChainReset, ChainInfo,
|
||||
ImportExportBlocks, ImportBlock
|
||||
};
|
||||
use spec;
|
||||
use ethereum;
|
||||
use executive::{Executive, TransactOptions};
|
||||
use miner::{Miner, PendingOrdering, MinerService};
|
||||
use spec::Spec;
|
||||
use state::{self, State, CleanupMode};
|
||||
use state::{self, State, CleanupMode, StateInfo};
|
||||
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() {
|
||||
@@ -120,7 +129,7 @@ fn query_none_block() {
|
||||
Arc::new(Miner::new_for_tests(&spec, None)),
|
||||
IoChannel::disconnected(),
|
||||
).unwrap();
|
||||
let non_existant = client.block_header(BlockId::Number(188));
|
||||
let non_existant = client.block_header(BlockId::Number(188));
|
||||
assert!(non_existant.is_none());
|
||||
}
|
||||
|
||||
@@ -386,3 +395,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