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:
Talha Cross
2019-11-11 21:57:38 +01:00
committed by s3krit
parent 5ee54b7298
commit f3cdd7bf21
159 changed files with 12295 additions and 3918 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}

View File

@@ -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};

View File

@@ -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>) {

View File

@@ -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>;
}

View File

@@ -25,7 +25,7 @@ use std::sync::{Weak, Arc};
use std::time::{UNIX_EPOCH, Duration};
use block::*;
use client::EngineClient;
use client::{EngineClient, 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);
}
}
}

View File

@@ -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();

View File

@@ -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!");
}
}
}

View File

@@ -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();

View File

@@ -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>> {

View File

@@ -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()
}
}
}

View File

@@ -151,7 +151,7 @@ mod tests {
use std::collections::BTreeMap;
use hash::keccak;
use accounts::AccountProvider;
use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock};
use client::{BlockChainClient, ChainInfo, BlockInfo, 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.

View File

@@ -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);

View File

@@ -134,6 +134,11 @@ pub fn new_sokol<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/poasokol.json"))
}
/// Create a new 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.

View File

@@ -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();

View File

@@ -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;

View File

@@ -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()
)
)
}

View File

@@ -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

View File

@@ -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

View File

@@ -17,6 +17,7 @@
//! Parameters for a block chain.
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
@@ -534,19 +535,28 @@ impl From<SpecHardcodedSync> for ethjson::spec::HardcodedSync {
}
fn load_machine_from(s: ethjson::spec::Spec) -> 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"))]

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::str::FromStr;
use std::str::{FromStr, from_utf8};
use std::sync::Arc;
use ethereum_types::{U256, Address};
@@ -22,23 +22,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());
}