New Transaction Queue implementation (#8074)

* Implementation of Verifier, Scoring and Ready.

* Queue in progress.

* TransactionPool.

* Prepare for txpool release.

* Miner refactor [WiP]

* WiP reworking miner.

* Make it compile.

* Add some docs.

* Split blockchain access to a separate file.

* Work on miner API.

* Fix ethcore tests.

* Refactor miner interface for sealing/work packages.

* Implement next nonce.

* RPC compiles.

* Implement couple of missing methdods for RPC.

* Add transaction queue listeners.

* Compiles!

* Clean-up and parallelize.

* Get rid of RefCell in header.

* Revert "Get rid of RefCell in header."

This reverts commit 0f2424c9b7319a786e1565ea2a8a6d801a21b4fb.

* Override Sync requirement.

* Fix status display.

* Unify logging.

* Extract some cheap checks.

* Measurements and optimizations.

* Fix scoring bug, heap size of bug and add cache

* Disable tx queueing and parallel verification.

* Make ethcore and ethcore-miner compile again.

* Make RPC compile again.

* Bunch of txpool tests.

* Migrate transaction queue tests.

* Nonce Cap

* Nonce cap cache and tests.

* Remove stale future transactions from the queue.

* Optimize scoring and write some tests.

* Simple penalization.

* Clean up and support for different scoring algorithms.

* Add CLI parameters for the new queue.

* Remove banning queue.

* Disable debug build.

* Change per_sender limit to be 1% instead of 5%

* Avoid cloning when propagating transactions.

* Remove old todo.

* Post-review fixes.

* Fix miner options default.

* Implement back ready transactions for light client.

* Get rid of from_pending_block

* Pass rejection reason.

* Add more details to drop.

* Rollback heap size of.

* Avoid cloning hashes when propagating and include more details on rejection.

* Fix tests.

* Introduce nonces cache.

* Remove uneccessary hashes allocation.

* Lower the mem limit.

* Re-enable parallel verification.

* Add miner log. Don't check the type if not below min_gas_price.

* Add more traces, fix disabling miner.

* Fix creating pending blocks twice on AuRa authorities.

* Fix tests.

* re-use pending blocks in AuRa

* Use reseal_min_period to prevent too frequent update_sealing.

* Fix log to contain hash not sender.

* Optimize local transactions.

* Fix aura tests.

* Update locks comments.

* Get rid of unsafe Sync impl.

* Review fixes.

* Remove excessive matches.

* Fix compilation errors.

* Use new pool in private transactions.

* Fix private-tx test.

* Fix secret store tests.

* Actually use gas_floor_target

* Fix config tests.

* Fix pool tests.

* Address grumbles.
This commit is contained in:
Tomasz Drwięga
2018-04-13 17:34:27 +02:00
committed by Marek Kotewicz
parent 03b96a7c0a
commit 1cd93e4ceb
105 changed files with 5185 additions and 5784 deletions

View File

@@ -66,8 +66,6 @@ pub enum SignError {
Hardware(HardwareError),
/// Low-level error from store
SStore(SSError),
/// Inappropriate chain
InappropriateChain,
}
impl fmt::Display for SignError {
@@ -77,7 +75,6 @@ impl fmt::Display for SignError {
SignError::NotFound => write!(f, "Account does not exist"),
SignError::Hardware(ref e) => write!(f, "{}", e),
SignError::SStore(ref e) => write!(f, "{}", e),
SignError::InappropriateChain => write!(f, "Inappropriate chain"),
}
}
}

View File

@@ -337,8 +337,33 @@ impl<'x> OpenBlock<'x> {
}
/// Push transactions onto the block.
pub fn push_transactions(&mut self, transactions: &[SignedTransaction]) -> Result<(), Error> {
push_transactions(self, transactions)
#[cfg(not(feature = "slow-blocks"))]
fn push_transactions(&mut self, transactions: Vec<SignedTransaction>) -> Result<(), Error> {
for t in transactions {
self.push_transaction(t, None)?;
}
Ok(())
}
/// Push transactions onto the block.
#[cfg(feature = "slow-blocks")]
fn push_transactions(&mut self, transactions: Vec<SignedTransaction>) -> Result<(), Error> {
use std::time;
let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100);
for t in transactions {
let hash = t.hash();
let start = time::Instant::now();
self.push_transaction(t, None)?;
let took = start.elapsed();
let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000;
if took > time::Duration::from_millis(slow_tx) {
warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, block.header().number(), hash);
}
debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms);
}
Ok(())
}
/// Populate self from a header.
@@ -534,10 +559,10 @@ impl IsBlock for SealedBlock {
}
/// Enact the block given by block header, transactions and uncles
pub fn enact(
header: &Header,
transactions: &[SignedTransaction],
uncles: &[Header],
fn enact(
header: Header,
transactions: Vec<SignedTransaction>,
uncles: Vec<Header>,
engine: &EthEngine,
tracing: bool,
db: StateDB,
@@ -568,11 +593,11 @@ pub fn enact(
is_epoch_begin,
)?;
b.populate_from(header);
b.populate_from(&header);
b.push_transactions(transactions)?;
for u in uncles {
b.push_uncle(u.clone())?;
b.push_uncle(u)?;
}
if strip_receipts {
@@ -584,38 +609,9 @@ pub fn enact(
Ok(b.close_and_lock())
}
#[inline]
#[cfg(not(feature = "slow-blocks"))]
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
for t in transactions {
block.push_transaction(t.clone(), None)?;
}
Ok(())
}
#[cfg(feature = "slow-blocks")]
fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> {
use std::time;
let slow_tx = option_env!("SLOW_TX_DURATION").and_then(|v| v.parse().ok()).unwrap_or(100);
for t in transactions {
let hash = t.hash();
let start = time::Instant::now();
block.push_transaction(t.clone(), None)?;
let took = start.elapsed();
let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000;
if took > time::Duration::from_millis(slow_tx) {
warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, block.header().number(), hash);
}
debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms);
}
Ok(())
}
// TODO [ToDr] Pass `PreverifiedBlock` by move, this will avoid unecessary allocation
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified(
block: &PreverifiedBlock,
block: PreverifiedBlock,
engine: &EthEngine,
tracing: bool,
db: StateDB,
@@ -629,9 +625,9 @@ pub fn enact_verified(
let view = BlockView::new(&block.bytes);
enact(
&block.header,
&block.transactions,
&view.uncles(),
block.header,
block.transactions,
view.uncles(),
engine,
tracing,
db,
@@ -700,7 +696,7 @@ mod tests {
)?;
b.populate_from(&header);
b.push_transactions(&transactions)?;
b.push_transactions(transactions)?;
for u in &block.uncles() {
b.push_uncle(u.clone())?;
@@ -793,3 +789,4 @@ mod tests {
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
}
}

View File

@@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{H256, U256};
use encoded;
use header::{Header, BlockNumber};

View File

@@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::H256;
use bytes::Bytes;
use ethereum_types::H256;
use transaction::UnverifiedTransaction;
/// Messages to broadcast via chain
pub enum ChainMessageType {
@@ -59,7 +60,7 @@ pub trait ChainNotify : Send + Sync {
/// fires when new transactions are received from a peer
fn transactions_received(&self,
_hashes: Vec<H256>,
_txs: &[UnverifiedTransaction],
_peer_id: usize,
) {
// does nothing by default

View File

@@ -44,7 +44,7 @@ use client::{
};
use client::{
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
TraceFilter, CallAnalytics, BlockImportError, Mode,
ChainNotify, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType
};
use encoded;
@@ -58,6 +58,7 @@ use header::{BlockNumber, Header};
use io::IoChannel;
use log_entry::LocalizedLogEntry;
use miner::{Miner, MinerService};
use ethcore_miner::pool::VerifiedTransaction;
use parking_lot::{Mutex, RwLock};
use rand::OsRng;
use receipt::{Receipt, LocalizedReceipt};
@@ -68,7 +69,7 @@ use state_db::StateDB;
use state::{self, State};
use trace;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Transaction, PendingTransaction, Action};
use transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Transaction, Action};
use types::filter::Filter;
use types::mode::Mode as IpcMode;
use verification;
@@ -103,10 +104,10 @@ pub struct ClientReport {
impl ClientReport {
/// Alter internal reporting to reflect the additional `block` has been processed.
pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
pub fn accrue_block(&mut self, header: &Header, transactions: usize) {
self.blocks_imported += 1;
self.transactions_applied += block.transactions.len();
self.gas_processed = self.gas_processed + block.header.gas_used().clone();
self.transactions_applied += transactions;
self.gas_processed = self.gas_processed + *header.gas_used();
}
}
@@ -295,23 +296,29 @@ impl Importer {
let start = Instant::now();
for block in blocks {
let header = &block.header;
let header = block.header.clone();
let bytes = block.bytes.clone();
let hash = header.hash();
let is_invalid = invalid_blocks.contains(header.parent_hash());
if is_invalid {
invalid_blocks.insert(header.hash());
invalid_blocks.insert(hash);
continue;
}
if let Ok(closed_block) = self.check_and_close_block(&block, client) {
if self.engine.is_proposal(&block.header) {
self.block_queue.mark_as_good(&[header.hash()]);
proposed_blocks.push(block.bytes);
} else {
imported_blocks.push(header.hash());
let route = self.commit_block(closed_block, &header, &block.bytes, client);
if let Ok(closed_block) = self.check_and_close_block(block, client) {
if self.engine.is_proposal(&header) {
self.block_queue.mark_as_good(&[hash]);
proposed_blocks.push(bytes);
} else {
imported_blocks.push(hash);
let transactions_len = closed_block.transactions().len();
let route = self.commit_block(closed_block, &header, &bytes, client);
import_results.push(route);
client.report.write().accrue_block(&block);
client.report.write().accrue_block(&header, transactions_len);
}
} else {
invalid_blocks.insert(header.hash());
@@ -337,7 +344,7 @@ impl Importer {
let (enacted, retracted) = self.calculate_enacted_retracted(&import_results);
if is_empty {
self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, &enacted, &retracted);
self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, &enacted, &retracted, false);
}
client.notify(|notify| {
@@ -358,9 +365,9 @@ impl Importer {
imported
}
fn check_and_close_block(&self, block: &PreverifiedBlock, client: &Client) -> Result<LockedBlock, ()> {
fn check_and_close_block(&self, block: PreverifiedBlock, client: &Client) -> Result<LockedBlock, ()> {
let engine = &*self.engine;
let header = &block.header;
let header = block.header.clone();
// Check the block isn't so old we won't be able to enact it.
let best_block_number = client.chain.read().best_block_number();
@@ -381,7 +388,7 @@ impl Importer {
let chain = client.chain.read();
// Verify Block Family
let verify_family_result = self.verifier.verify_block_family(
header,
&header,
&parent,
engine,
Some(verification::FullFamilyParams {
@@ -397,7 +404,7 @@ impl Importer {
return Err(());
};
let verify_external_result = self.verifier.verify_block_external(header, engine);
let verify_external_result = self.verifier.verify_block_external(&header, engine);
if let Err(e) = verify_external_result {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
@@ -409,7 +416,8 @@ impl Importer {
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
let strip_receipts = header.number() < engine.params().validate_receipts_transition;
let enact_result = enact_verified(block,
let enact_result = enact_verified(
block,
engine,
client.tracedb.read().tracing_enabled(),
db,
@@ -425,7 +433,7 @@ impl Importer {
})?;
// Final Verification
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
if let Err(e) = self.verifier.verify_block_final(&header, locked_block.block().header()) {
warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
}
@@ -975,19 +983,24 @@ impl Client {
/// Import transactions from the IO queue
pub fn import_queued_transactions(&self, transactions: &[Bytes], peer_id: usize) -> usize {
trace!(target: "external_tx", "Importing queued");
trace_time!("import_queued_transactions");
self.queue_transactions.fetch_sub(transactions.len(), AtomicOrdering::SeqCst);
let txs: Vec<UnverifiedTransaction> = transactions.iter().filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok()).collect();
let hashes: Vec<_> = txs.iter().map(|tx| tx.hash()).collect();
let txs: Vec<UnverifiedTransaction> = transactions
.iter()
.filter_map(|bytes| UntrustedRlp::new(bytes).as_val().ok())
.collect();
self.notify(|notify| {
notify.transactions_received(hashes.clone(), peer_id);
notify.transactions_received(&txs, peer_id);
});
let results = self.importer.miner.import_external_transactions(self, txs);
results.len()
}
/// Get shared miner reference.
#[cfg(test)]
pub fn miner(&self) -> Arc<Miner> {
self.importer.miner.clone()
}
@@ -1915,12 +1928,8 @@ impl BlockChainClient for Client {
}
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
let (number, timestamp) = {
let chain = self.chain.read();
(chain.best_block_number(), chain.best_block_timestamp())
};
self.importer.miner.ready_transactions(number, timestamp)
fn ready_transactions(&self) -> Vec<Arc<VerifiedTransaction>> {
self.importer.miner.ready_transactions(self)
}
fn queue_consensus_message(&self, message: Bytes) {
@@ -1951,17 +1960,19 @@ impl BlockChainClient for Client {
}
}
fn transact_contract(&self, address: Address, data: Bytes) -> Result<transaction::ImportResult, EthcoreError> {
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
let authoring_params = self.importer.miner.authoring_params();
let transaction = Transaction {
nonce: self.latest_nonce(&self.importer.miner.author()),
nonce: self.latest_nonce(&authoring_params.author),
action: Action::Call(address),
gas: self.importer.miner.gas_floor_target(),
gas: self.importer.miner.sensible_gas_limit(),
gas_price: self.importer.miner.sensible_gas_price(),
value: U256::zero(),
data: data,
};
let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
let signature = self.engine.sign(transaction.hash(chain_id))?;
let signature = self.engine.sign(transaction.hash(chain_id))
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
self.importer.miner.import_own_transaction(self, signed.into())
}
@@ -2070,7 +2081,7 @@ impl ImportSealedBlock for Client {
route
};
let (enacted, retracted) = self.importer.calculate_enacted_retracted(&[route]);
self.importer.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
self.importer.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted, true);
self.notify(|notify| {
notify.new_blocks(
vec![h.clone()],
@@ -2108,11 +2119,8 @@ impl BroadcastProposalBlock for Client {
impl SealedBlockImporter for Client {}
impl MiningBlockChainClient for Client {
fn vm_factory(&self) -> &VmFactory {
&self.factories.vm
}
}
impl ::miner::TransactionVerifierClient for Client {}
impl ::miner::BlockChainClient for Client {}
impl super::traits::EngineClient for Client {
fn update_sealing(&self) {
@@ -2120,8 +2128,9 @@ impl super::traits::EngineClient for Client {
}
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
if self.importer.miner.submit_seal(self, block_hash, seal).is_err() {
warn!(target: "poa", "Wrong internal seal submission!")
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);
}
}

View File

@@ -38,7 +38,7 @@ pub use self::traits::{
};
//pub use self::private_notify::PrivateNotify;
pub use state::StateInfo;
pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient, ProvingBlockChainClient};
pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient};
pub use types::ids::*;
pub use types::trace_filter::Filter as TraceFilter;

View File

@@ -31,11 +31,12 @@ use kvdb_memorydb;
use bytes::Bytes;
use rlp::{UntrustedRlp, RlpStream};
use ethkey::{Generator, Random};
use transaction::{self, Transaction, LocalizedTransaction, PendingTransaction, SignedTransaction, Action};
use ethcore_miner::pool::VerifiedTransaction;
use transaction::{self, Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::{TreeRoute, BlockReceipts};
use client::{
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, CallContract, TransactionInfo, RegistryInfo,
PrepareOpenBlock, BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId,
PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId,
TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock,
Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter
@@ -45,9 +46,7 @@ use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
use error::{ImportResult, Error as EthcoreError};
use evm::VMType;
use factory::VmFactory;
use error::ImportResult;
use vm::Schedule;
use miner::{Miner, MinerService};
use spec::Spec;
@@ -102,8 +101,6 @@ pub struct TestBlockChainClient {
pub miner: Arc<Miner>,
/// Spec
pub spec: Spec,
/// VM Factory
pub vm_factory: VmFactory,
/// Timestamp assigned to latest sealed block
pub latest_block_timestamp: RwLock<u64>,
/// Ancient block info.
@@ -174,9 +171,8 @@ impl TestBlockChainClient {
receipts: RwLock::new(HashMap::new()),
logs: RwLock::new(Vec::new()),
queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::with_spec(&spec)),
miner: Arc::new(Miner::new_for_tests(&spec, None)),
spec: spec,
vm_factory: VmFactory::new(VMType::Interpreter, 1024 * 1024),
latest_block_timestamp: RwLock::new(10_000_000),
ancient_block: RwLock::new(None),
first_block: RwLock::new(None),
@@ -345,8 +341,8 @@ impl TestBlockChainClient {
self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into());
let hash = signed_tx.hash();
let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]);
let res = res.into_iter().next().unwrap().expect("Successful import");
assert_eq!(res, transaction::ImportResult::Current);
let res = res.into_iter().next().unwrap();
assert!(res.is_ok());
hash
}
@@ -423,11 +419,8 @@ impl BroadcastProposalBlock for TestBlockChainClient {
impl SealedBlockImporter for TestBlockChainClient {}
impl MiningBlockChainClient for TestBlockChainClient {
fn vm_factory(&self) -> &VmFactory {
&self.vm_factory
}
}
impl ::miner::TransactionVerifierClient for TestBlockChainClient {}
impl ::miner::BlockChainClient for TestBlockChainClient {}
impl Nonce for TestBlockChainClient {
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
@@ -826,9 +819,8 @@ impl BlockChainClient for TestBlockChainClient {
self.spec.engine.handle_message(&message).unwrap();
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
let info = self.chain_info();
self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp)
fn ready_transactions(&self) -> Vec<Arc<VerifiedTransaction>> {
self.miner.ready_transactions(self)
}
fn signing_chain_id(&self) -> Option<u64> { None }
@@ -851,9 +843,9 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn transact_contract(&self, address: Address, data: Bytes) -> Result<transaction::ImportResult, EthcoreError> {
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
let transaction = Transaction {
nonce: self.latest_nonce(&self.miner.author()),
nonce: self.latest_nonce(&self.miner.authoring_params().author),
action: Action::Call(address),
gas: self.spec.gas_limit,
gas_price: U256::zero(),
@@ -895,8 +887,9 @@ impl super::traits::EngineClient for TestBlockChainClient {
}
fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
if self.miner.submit_seal(self, block_hash, seal).is_err() {
warn!(target: "poa", "Wrong internal seal submission!")
let import = self.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);
}
}

View File

@@ -15,28 +15,30 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeMap;
use std::sync::Arc;
use itertools::Itertools;
use block::{OpenBlock, SealedBlock, ClosedBlock};
use blockchain::TreeRoute;
use encoded;
use vm::LastHashes;
use error::{ImportResult, CallError, Error as EthcoreError, BlockImportError};
use error::{ImportResult, CallError, BlockImportError};
use evm::Schedule;
use factory::VmFactory;
use executive::Executed;
use filter::Filter;
use header::{BlockNumber};
use log_entry::LocalizedLogEntry;
use receipt::LocalizedReceipt;
use trace::LocalizedTrace;
use transaction::{LocalizedTransaction, PendingTransaction, SignedTransaction, ImportResult as TransactionImportResult};
use transaction::{self, LocalizedTransaction, SignedTransaction};
use verification::queue::QueueInfo as BlockQueueInfo;
use state::StateInfo;
use header::Header;
use engines::EthEngine;
use ethereum_types::{H256, U256, Address};
use ethcore_miner::pool::VerifiedTransaction;
use bytes::Bytes;
use hashdb::DBValue;
@@ -315,7 +317,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
fn queue_consensus_message(&self, message: Bytes);
/// List all transactions that are allowed into the next block.
fn ready_transactions(&self) -> Vec<PendingTransaction>;
fn ready_transactions(&self) -> Vec<Arc<VerifiedTransaction>>;
/// Sorted list of transaction gas prices from at least last sample_size blocks.
fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus<U256> {
@@ -366,8 +368,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
/// Returns information about pruning/data availability.
fn pruning_info(&self) -> PruningInfo;
/// Import a transaction: used for misbehaviour reporting.
fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError>;
/// Schedule state-altering transaction to be executed on the next pending block.
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error>;
/// Get the address of the registry itself.
fn registrar_address(&self) -> Option<Address>;
@@ -416,12 +418,6 @@ pub trait BroadcastProposalBlock {
/// Provides methods to import sealed block and broadcast a block proposal
pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {}
/// Extended client interface used for mining
pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + ScheduleInfo + SealedBlockImporter {
/// Returns EvmFactory.
fn vm_factory(&self) -> &VmFactory;
}
/// Client facilities used by internally sealing Engines.
pub trait EngineClient: Sync + Send + ChainInfo {
/// Make a new block and seal it.

View File

@@ -38,7 +38,7 @@ use super::validator_set::{ValidatorSet, SimpleList, new_validator_set};
use self::finality::RollingFinality;
use ethkey::{public_to_address, recover, verify_address, Signature};
use ethkey::{self, Signature};
use io::{IoContext, IoHandler, TimerToken, IoService};
use itertools::{self, Itertools};
use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, UntrustedRlp};
@@ -292,14 +292,14 @@ impl EmptyStep {
let message = keccak(empty_step_rlp(self.step, &self.parent_hash));
let correct_proposer = step_proposer(validators, &self.parent_hash, self.step);
verify_address(&correct_proposer, &self.signature.into(), &message)
ethkey::verify_address(&correct_proposer, &self.signature.into(), &message)
.map_err(|e| e.into())
}
fn author(&self) -> Result<Address, Error> {
let message = keccak(empty_step_rlp(self.step, &self.parent_hash));
let public = recover(&self.signature.into(), &message)?;
Ok(public_to_address(&public))
let public = ethkey::recover(&self.signature.into(), &message)?;
Ok(ethkey::public_to_address(&public))
}
fn sealed(&self) -> SealedEmptyStep {
@@ -555,7 +555,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans
};
let header_seal_hash = header_seal_hash(header, empty_steps_rlp);
!verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)?
!ethkey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)?
};
if is_invalid_proposer {
@@ -824,7 +824,10 @@ impl Engine<EthereumMachine> for AuthorityRound {
fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal {
// first check to avoid generating signature most of the time
// (but there's still a race to the `compare_and_swap`)
if !self.can_propose.load(AtomicOrdering::SeqCst) { return Seal::None; }
if !self.can_propose.load(AtomicOrdering::SeqCst) {
trace!(target: "engine", "Aborting seal generation. Can't propose.");
return Seal::None;
}
let header = block.header();
let parent_step: U256 = header_step(parent, self.empty_steps_transition)
@@ -1305,7 +1308,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
}
fn sign(&self, hash: H256) -> Result<Signature, Error> {
self.signer.read().sign(hash).map_err(Into::into)
Ok(self.signer.read().sign(hash)?)
}
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {

View File

@@ -19,7 +19,7 @@
use std::sync::{Weak, Arc};
use ethereum_types::{H256, H520, Address};
use parking_lot::RwLock;
use ethkey::{recover, public_to_address, Signature};
use ethkey::{self, Signature};
use account_provider::AccountProvider;
use block::*;
use engines::{Engine, Seal, ConstructedVerifier, EngineError};
@@ -61,7 +61,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Err
// Check if the signature belongs to a validator, can depend on parent state.
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
let signer = ethkey::public_to_address(&ethkey::recover(&sig.into(), &header.bare_hash())?);
if *header.author() != signer {
return Err(EngineError::NotAuthorized(*header.author()).into())
@@ -185,7 +185,7 @@ impl Engine<EthereumMachine> for BasicAuthority {
}
fn sign(&self, hash: H256) -> Result<Signature, Error> {
self.signer.read().sign(hash).map_err(Into::into)
Ok(self.signer.read().sign(hash)?)
}
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {

View File

@@ -48,7 +48,7 @@ use error::Error;
use header::{Header, BlockNumber};
use snapshot::SnapshotComponents;
use spec::CommonParams;
use transaction::{UnverifiedTransaction, SignedTransaction};
use transaction::{self, UnverifiedTransaction, SignedTransaction};
use ethkey::Signature;
use parity_machine::{Machine, LocalizedMachine as Localized};
@@ -387,14 +387,28 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
}
/// Verify a particular transaction is valid.
fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result<SignedTransaction, Error> {
///
/// Unordered verification doesn't rely on the transaction execution order,
/// i.e. it should only verify stuff that doesn't assume any previous transactions
/// has already been verified and executed.
///
/// NOTE This function consumes an `UnverifiedTransaction` and produces `SignedTransaction`
/// which implies that a heavy check of the signature is performed here.
fn verify_transaction_unordered(&self, t: UnverifiedTransaction, header: &Header) -> Result<SignedTransaction, transaction::Error> {
self.machine().verify_transaction_unordered(t, header)
}
/// Additional verification for transactions in blocks.
// TODO: Add flags for which bits of the transaction to check.
// TODO: consider including State in the params.
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> {
/// Perform basic/cheap transaction verification.
///
/// This should include all cheap checks that can be done before
/// actually checking the signature, like chain-replay protection.
///
/// NOTE This is done before the signature is recovered so avoid
/// doing any state-touching checks that might be expensive.
///
/// TODO: Add flags for which bits of the transaction to check.
/// TODO: consider including State in the params.
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> {
self.machine().verify_transaction_basic(t, header)
}

View File

@@ -37,7 +37,7 @@ use bytes::Bytes;
use error::{Error, BlockError};
use header::{Header, BlockNumber};
use rlp::UntrustedRlp;
use ethkey::{Message, public_to_address, recover, Signature};
use ethkey::{self, Message, Signature};
use account_provider::AccountProvider;
use block::*;
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
@@ -518,8 +518,8 @@ impl Engine<EthereumMachine> for Tendermint {
let message: ConsensusMessage = rlp.as_val().map_err(fmt_err)?;
if !self.votes.is_old_or_known(&message) {
let msg_hash = keccak(rlp.at(1).map_err(fmt_err)?.as_raw());
let sender = public_to_address(
&recover(&message.signature.into(), &msg_hash).map_err(fmt_err)?
let sender = ethkey::public_to_address(
&ethkey::recover(&message.signature.into(), &msg_hash).map_err(fmt_err)?
);
if !self.is_authority(&sender) {
@@ -614,7 +614,7 @@ impl Engine<EthereumMachine> for Tendermint {
};
let address = match self.votes.get(&precommit) {
Some(a) => a,
None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
None => ethkey::public_to_address(&ethkey::recover(&precommit.signature.into(), &precommit_hash)?),
};
if !self.validators.contains(header.parent_hash(), &address) {
return Err(EngineError::NotAuthorized(address.to_owned()).into());
@@ -669,7 +669,7 @@ impl Engine<EthereumMachine> for Tendermint {
let verifier = Box::new(EpochVerifier {
subchain_validators: list,
recover: |signature: &Signature, message: &Message| {
Ok(public_to_address(&::ethkey::recover(&signature, &message)?))
Ok(ethkey::public_to_address(&ethkey::recover(&signature, &message)?))
},
});
@@ -690,7 +690,7 @@ impl Engine<EthereumMachine> for Tendermint {
}
fn sign(&self, hash: H256) -> Result<Signature, Error> {
self.signer.read().sign(hash).map_err(Into::into)
Ok(self.signer.read().sign(hash)?)
}
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
@@ -1026,7 +1026,7 @@ mod tests {
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_test_tendermint, Some(tap.clone()));
let engine = client.engine();
client.miner().set_engine_signer(v1.clone(), "1".into()).unwrap();
client.miner().set_author(v1.clone(), Some("1".into())).unwrap();
let notify = Arc::new(TestNotify::default());
client.add_notify(notify.clone());

View File

@@ -169,8 +169,8 @@ mod tests {
let validator_contract = "0000000000000000000000000000000000000005".parse::<Address>().unwrap();
// Make sure reporting can be done.
client.miner().set_gas_floor_target(1_000_000.into());
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into()));
client.miner().set_author(v1, Some("".into())).unwrap();
// Check a block that is a bit in future, reject it but don't report the validator.
let mut header = Header::default();

View File

@@ -171,22 +171,22 @@ mod tests {
client.engine().register_client(Arc::downgrade(&client) as _);
// Make sure txs go through.
client.miner().set_gas_floor_target(1_000_000.into());
client.miner().set_gas_range_target((1_000_000.into(), 1_000_000.into()));
// Wrong signer for the first block.
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.miner().set_author(v1, Some("".into())).unwrap();
client.transact_contract(Default::default(), Default::default()).unwrap();
::client::EngineClient::update_sealing(&*client);
assert_eq!(client.chain_info().best_block_number, 0);
// Right signer for the first block.
client.miner().set_engine_signer(v0, "".into()).unwrap();
client.miner().set_author(v0, Some("".into())).unwrap();
::client::EngineClient::update_sealing(&*client);
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);
assert_eq!(client.chain_info().best_block_number, 1);
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.miner().set_author(v1, Some("".into())).unwrap();
::client::EngineClient::update_sealing(&*client);
assert_eq!(client.chain_info().best_block_number, 2);
// v1 is still good.

View File

@@ -484,7 +484,7 @@ mod tests {
client.engine().register_client(Arc::downgrade(&client) as _);
let validator_contract = "0000000000000000000000000000000000000005".parse::<Address>().unwrap();
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.miner().set_author(v1, Some("".into())).unwrap();
// Remove "1" validator.
let tx = Transaction {
nonce: 0.into(),
@@ -512,11 +512,11 @@ mod tests {
assert_eq!(client.chain_info().best_block_number, 1);
// Switch to the validator that is still there.
client.miner().set_engine_signer(v0, "".into()).unwrap();
client.miner().set_author(v0, Some("".into())).unwrap();
::client::EngineClient::update_sealing(&*client);
assert_eq!(client.chain_info().best_block_number, 2);
// Switch back to the added validator, since the state is updated.
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.miner().set_author(v1, Some("".into())).unwrap();
let tx = Transaction {
nonce: 2.into(),
gas_price: 0.into(),

View File

@@ -64,7 +64,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec<String> {
config,
&spec,
db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
for b in &blockchain.blocks_rlp() {

View File

@@ -71,7 +71,6 @@ extern crate ethcore_transaction as transaction;
extern crate ethereum_types;
extern crate ethjson;
extern crate ethkey;
extern crate futures_cpupool;
extern crate hardware_wallet;
extern crate hashdb;
extern crate itertools;
@@ -80,7 +79,6 @@ extern crate num_cpus;
extern crate num;
extern crate parity_machine;
extern crate parking_lot;
extern crate price_info;
extern crate rand;
extern crate rayon;
extern crate rlp;
@@ -99,18 +97,10 @@ extern crate util_error;
extern crate snappy;
extern crate ethabi;
#[macro_use]
extern crate ethabi_derive;
#[macro_use]
extern crate ethabi_contract;
#[macro_use]
extern crate rlp_derive;
extern crate rustc_hex;
extern crate stats;
extern crate stop_guard;
extern crate using_queue;
extern crate table;
extern crate vm;
extern crate wasm;
extern crate memory_cache;
@@ -119,13 +109,20 @@ extern crate journaldb;
extern crate tempdir;
#[macro_use]
extern crate macros;
extern crate ethabi_derive;
#[macro_use]
extern crate ethabi_contract;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate macros;
#[macro_use]
extern crate rlp_derive;
#[macro_use]
extern crate trace_time;
#[cfg_attr(test, macro_use)]
extern crate evm;

View File

@@ -334,12 +334,12 @@ impl EthereumMachine {
}
/// Verify a particular transaction is valid, regardless of order.
pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result<SignedTransaction, Error> {
pub fn verify_transaction_unordered(&self, t: UnverifiedTransaction, _header: &Header) -> Result<SignedTransaction, transaction::Error> {
Ok(SignedTransaction::new(t)?)
}
/// Does basic verification of the transaction.
pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), Error> {
pub fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> Result<(), transaction::Error> {
let check_low_s = match self.ethash_extensions {
Some(ref ext) => header.number() >= ext.homestead_transition,
None => true,
@@ -358,9 +358,9 @@ impl EthereumMachine {
}
/// Does verification of the transaction against the parent state.
// TODO: refine the bound here to be a "state provider" or similar as opposed
// to full client functionality.
pub fn verify_transaction<C: BlockInfo + CallContract>(&self, t: &SignedTransaction, header: &Header, client: &C) -> Result<(), Error> {
pub fn verify_transaction<C: BlockInfo + CallContract>(&self, t: &SignedTransaction, header: &Header, client: &C)
-> Result<(), transaction::Error>
{
if let Some(ref filter) = self.tx_filter.as_ref() {
if !filter.transaction_allowed(header.parent_hash(), t, client) {
return Err(transaction::Error::NotAllowed.into())

File diff suppressed because it is too large Load Diff

View File

@@ -17,173 +17,87 @@
#![warn(missing_docs)]
//! Miner module
//! Keeps track of transactions and mined block.
//!
//! Usage example:
//!
//! ```rust
//! extern crate ethcore;
//! use std::env;
//! use ethcore::ethereum;
//! use ethcore::client::{Client, ClientConfig};
//! use ethcore::miner::{Miner, MinerService};
//!
//! fn main() {
//! let miner: Miner = Miner::with_spec(&ethereum::new_foundation(&env::temp_dir()));
//! // get status
//! assert_eq!(miner.status().transactions_in_pending_queue, 0);
//!
//! // Check block for sealing
//! //assert!(miner.sealing_block(&*client).lock().is_some());
//! }
//! ```
//! Keeps track of transactions and currently sealed pending block.
mod miner;
mod stratum;
mod service_transaction_checker;
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOptions};
pub mod pool_client;
pub mod stratum;
pub use ethcore_miner::local_transactions::Status as LocalTransactionStatus;
pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams};
use std::sync::Arc;
use std::collections::BTreeMap;
use block::{ClosedBlock, Block};
use bytes::Bytes;
use client::{
MiningBlockChainClient, CallContract, RegistryInfo, ScheduleInfo,
BlockChain, AccountData, BlockProducer, SealedBlockImporter
};
use error::{Error};
use ethereum_types::{H256, U256, Address};
use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions};
use block::{Block, SealedBlock};
use client::{
CallContract, RegistryInfo, ScheduleInfo,
BlockChain, BlockProducer, SealedBlockImporter, ChainInfo,
AccountData, Nonce,
};
use error::Error;
use header::{BlockNumber, Header};
use receipt::{RichReceipt, Receipt};
use transaction::{UnverifiedTransaction, PendingTransaction, ImportResult as TransactionImportResult};
use transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction};
use state::StateInfo;
/// Provides methods to verify incoming external transactions
pub trait TransactionVerifierClient: Send + Sync
// Required for ServiceTransactionChecker
+ CallContract + RegistryInfo
// Required for verifiying transactions
+ BlockChain + ScheduleInfo + AccountData
{}
/// Extended client interface used for mining
pub trait BlockChainClient: TransactionVerifierClient + BlockProducer + SealedBlockImporter {}
/// Miner client API
pub trait MinerService : Send + Sync {
/// Type representing chain state
type State: StateInfo + 'static;
/// Returns miner's status.
fn status(&self) -> MinerStatus;
/// Get the author that we will seal blocks as.
fn author(&self) -> Address;
/// Set the author that we will seal blocks as.
fn set_author(&self, author: Address);
/// Set info necessary to sign consensus messages.
fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::SignError>;
/// Get the extra_data that we will seal blocks with.
fn extra_data(&self) -> Bytes;
/// Set the extra_data that we will seal blocks with.
fn set_extra_data(&self, extra_data: Bytes);
/// Get current minimal gas price for transactions accepted to queue.
fn minimal_gas_price(&self) -> U256;
/// Set minimal gas price of transaction to be accepted for mining.
fn set_minimal_gas_price(&self, min_gas_price: U256);
/// Get the lower bound of the gas limit we wish to target when sealing a new block.
fn gas_floor_target(&self) -> U256;
/// Get the upper bound of the gas limit we wish to target when sealing a new block.
fn gas_ceil_target(&self) -> U256;
// TODO: coalesce into single set_range function.
/// Set the lower bound of gas limit we wish to target when sealing a new block.
fn set_gas_floor_target(&self, target: U256);
/// Set the upper bound of gas limit we wish to target when sealing a new block.
fn set_gas_ceil_target(&self, target: U256);
/// Get current transactions limit in queue.
fn transactions_limit(&self) -> usize;
/// Set maximal number of transactions kept in the queue (both current and future).
fn set_transactions_limit(&self, limit: usize);
/// Set maximum amount of gas allowed for any single transaction to mine.
fn set_tx_gas_limit(&self, limit: U256);
/// Imports transactions to transaction queue.
fn import_external_transactions<C: MiningBlockChainClient>(&self, client: &C, transactions: Vec<UnverifiedTransaction>) ->
Vec<Result<TransactionImportResult, Error>>;
/// Imports own (node owner) transaction to queue.
fn import_own_transaction<C: MiningBlockChainClient>(&self, chain: &C, transaction: PendingTransaction) ->
Result<TransactionImportResult, Error>;
/// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256>;
/// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset<C: MiningBlockChainClient>(&self, chain: &C);
/// Called when blocks are imported to chain, updates transactions queue.
fn chain_new_blocks<C>(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256])
where C: AccountData + BlockChain + CallContract + RegistryInfo + BlockProducer + ScheduleInfo + SealedBlockImporter;
/// PoW chain - can produce work package
fn can_produce_work_package(&self) -> bool;
/// New chain head event. Restart mining operation.
fn update_sealing<C>(&self, chain: &C)
where C: AccountData + BlockChain + RegistryInfo + CallContract + BlockProducer + SealedBlockImporter;
// Sealing
/// Submit `seal` as a valid solution for the header of `pow_hash`.
/// Will check the seal, but not actually insert the block into the chain.
fn submit_seal<C: SealedBlockImporter>(&self, chain: &C, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
/// Get the sealing work package and if `Some`, apply some transform.
fn map_sealing_work<C, F, T>(&self, client: &C, f: F) -> Option<T>
where C: AccountData + BlockChain + BlockProducer + CallContract,
F: FnOnce(&ClosedBlock) -> T,
Self: Sized;
/// Query pending transactions for hash.
fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<PendingTransaction>;
/// Removes transaction from the queue.
/// NOTE: The transaction is not removed from pending block if mining.
fn remove_pending_transaction<C: AccountData>(&self, chain: &C, hash: &H256) -> Option<PendingTransaction>;
/// Get a list of all pending transactions in the queue.
fn pending_transactions(&self) -> Vec<PendingTransaction>;
/// Get a list of all transactions that can go into the given block.
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction>;
/// Get a list of all future transactions.
fn future_transactions(&self) -> Vec<PendingTransaction>;
/// Get a list of local transactions with statuses.
fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus>;
/// Get a list of all pending receipts.
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
/// Get a particular reciept.
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
/// Returns highest transaction nonce for given address.
fn last_nonce(&self, address: &Address) -> Option<U256>;
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<SealedBlock, Error>;
/// Is it currently sealing?
fn is_currently_sealing(&self) -> bool;
/// Suggested gas price.
fn sensible_gas_price(&self) -> U256;
/// Get the sealing work package preparing it if doesn't exist yet.
///
/// Returns `None` if engine seals internally.
fn work_package<C>(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)>
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
/// Suggested gas limit.
fn sensible_gas_limit(&self) -> U256 { 21000.into() }
/// Update current pending block
fn update_sealing<C>(&self, chain: &C)
where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync;
// Notifications
/// Called when blocks are imported to chain, updates transactions queue.
/// `is_internal_import` indicates that the block has just been created in miner and internally sealed by the engine,
/// so we shouldn't attempt creating new block again.
fn chain_new_blocks<C>(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool)
where C: BlockChainClient;
// Pending block
/// Get a list of all pending receipts from pending block.
fn pending_receipts(&self, best_block: BlockNumber) -> Option<BTreeMap<H256, Receipt>>;
/// Get a particular receipt from pending block.
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
fn pending_state(&self, latest_block_number: BlockNumber) -> Option<Self::State>;
@@ -193,15 +107,79 @@ pub trait MinerService : Send + Sync {
/// Get `Some` `clone()` of the current pending block or `None` if we're not sealing.
fn pending_block(&self, latest_block_number: BlockNumber) -> Option<Block>;
}
/// Mining status
#[derive(Debug)]
pub struct MinerStatus {
/// Number of transactions in queue with state `pending` (ready to be included in block)
pub transactions_in_pending_queue: usize,
/// Number of transactions in queue with state `future` (not yet ready to be included in block)
pub transactions_in_future_queue: usize,
/// Number of transactions included in currently mined block
pub transactions_in_pending_block: usize,
/// Get `Some` `clone()` of the current pending block transactions or `None` if we're not sealing.
fn pending_transactions(&self, latest_block_number: BlockNumber) -> Option<Vec<SignedTransaction>>;
// Block authoring
/// Get current authoring parameters.
fn authoring_params(&self) -> AuthoringParams;
/// Set the lower and upper bound of gas limit we wish to target when sealing a new block.
fn set_gas_range_target(&self, gas_range_target: (U256, U256));
/// Set the extra_data that we will seal blocks with.
fn set_extra_data(&self, extra_data: Bytes);
/// Set info necessary to sign consensus messages and block authoring.
///
/// On PoW password is optional.
fn set_author(&self, address: Address, password: Option<String>) -> Result<(), ::account_provider::SignError>;
// Transaction Pool
/// Imports transactions to transaction queue.
fn import_external_transactions<C>(&self, client: &C, transactions: Vec<UnverifiedTransaction>)
-> Vec<Result<(), transaction::Error>>
where C: BlockChainClient;
/// Imports own (node owner) transaction to queue.
fn import_own_transaction<C>(&self, chain: &C, transaction: PendingTransaction)
-> Result<(), transaction::Error>
where C: BlockChainClient;
/// Removes transaction from the pool.
///
/// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers)
/// there is a good chance that the transaction will actually be removed.
/// NOTE: The transaction is not removed from pending block if there is one.
fn remove_transaction(&self, hash: &H256) -> Option<Arc<VerifiedTransaction>>;
/// Query transaction from the pool given it's hash.
fn transaction(&self, hash: &H256) -> Option<Arc<VerifiedTransaction>>;
/// Returns next valid nonce for given address.
///
/// This includes nonces of all transactions from this address in the pending queue
/// if they are consecutive.
/// NOTE: pool may contain some future transactions that will become pending after
/// transaction with nonce returned from this function is signed on.
fn next_nonce<C>(&self, chain: &C, address: &Address) -> U256
where C: Nonce + Sync;
/// Get a list of all ready transactions.
///
/// Depending on the settings may look in transaction pool or only in pending block.
fn ready_transactions<C>(&self, chain: &C) -> Vec<Arc<VerifiedTransaction>>
where C: ChainInfo + Nonce + Sync;
/// Get a list of all transactions in the pool (some of them might not be ready for inclusion yet).
fn queued_transactions(&self) -> Vec<Arc<VerifiedTransaction>>;
/// Get a list of local transactions with statuses.
fn local_transactions(&self) -> BTreeMap<H256, local_transactions::Status>;
/// Get current queue status.
///
/// Status includes verification thresholds and current pool utilization and limits.
fn queue_status(&self) -> QueueStatus;
// Misc
/// Suggested gas price.
fn sensible_gas_price(&self) -> U256;
/// Suggested gas limit.
fn sensible_gas_limit(&self) -> U256;
}

View File

@@ -0,0 +1,216 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain access for transaction pool.
use std::fmt;
use std::collections::HashMap;
use ethereum_types::{H256, U256, Address};
use ethcore_miner::pool;
use ethcore_miner::pool::client::NonceClient;
use transaction::{
self,
UnverifiedTransaction,
SignedTransaction,
};
use parking_lot::RwLock;
use account_provider::AccountProvider;
use client::{TransactionId, BlockInfo, CallContract, Nonce};
use engines::EthEngine;
use header::Header;
use miner;
use miner::service_transaction_checker::ServiceTransactionChecker;
type NoncesCache = RwLock<HashMap<Address, U256>>;
const MAX_NONCE_CACHE_SIZE: usize = 4096;
const EXPECTED_NONCE_CACHE_SIZE: usize = 2048;
/// Blockchain accesss for transaction pool.
pub struct PoolClient<'a, C: 'a> {
chain: &'a C,
cached_nonces: CachedNonceClient<'a, C>,
engine: &'a EthEngine,
accounts: Option<&'a AccountProvider>,
best_block_header: Header,
service_transaction_checker: Option<ServiceTransactionChecker>,
}
impl<'a, C: 'a> Clone for PoolClient<'a, C> {
fn clone(&self) -> Self {
PoolClient {
chain: self.chain,
cached_nonces: self.cached_nonces.clone(),
engine: self.engine,
accounts: self.accounts.clone(),
best_block_header: self.best_block_header.clone(),
service_transaction_checker: self.service_transaction_checker.clone(),
}
}
}
impl<'a, C: 'a> PoolClient<'a, C> where
C: BlockInfo + CallContract,
{
/// Creates new client given chain, nonce cache, accounts and service transaction verifier.
pub fn new(
chain: &'a C,
cache: &'a NoncesCache,
engine: &'a EthEngine,
accounts: Option<&'a AccountProvider>,
refuse_service_transactions: bool,
) -> Self {
let best_block_header = chain.best_block_header();
PoolClient {
chain,
cached_nonces: CachedNonceClient::new(chain, cache),
engine,
accounts,
best_block_header,
service_transaction_checker: if refuse_service_transactions {
None
} else {
Some(Default::default())
},
}
}
/// Verifies if signed transaction is executable.
///
/// This should perform any verifications that rely on chain status.
pub fn verify_signed(&self, tx: &SignedTransaction) -> Result<(), transaction::Error> {
self.engine.machine().verify_transaction(&tx, &self.best_block_header, self.chain)
}
}
impl<'a, C: 'a> fmt::Debug for PoolClient<'a, C> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "PoolClient")
}
}
impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where
C: miner::TransactionVerifierClient + Sync,
{
fn transaction_already_included(&self, hash: &H256) -> bool {
self.chain.transaction_block(TransactionId::Hash(*hash)).is_some()
}
fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result<SignedTransaction, transaction::Error> {
self.engine.verify_transaction_basic(&tx, &self.best_block_header)?;
let tx = self.engine.verify_transaction_unordered(tx, &self.best_block_header)?;
self.verify_signed(&tx)?;
Ok(tx)
}
fn account_details(&self, address: &Address) -> pool::client::AccountDetails {
pool::client::AccountDetails {
nonce: self.cached_nonces.account_nonce(address),
balance: self.chain.latest_balance(address),
is_local: self.accounts.map_or(false, |accounts| accounts.has_account(*address).unwrap_or(false)),
}
}
fn required_gas(&self, tx: &transaction::Transaction) -> U256 {
tx.gas_required(&self.chain.latest_schedule()).into()
}
fn transaction_type(&self, tx: &SignedTransaction) -> pool::client::TransactionType {
match self.service_transaction_checker {
None => pool::client::TransactionType::Regular,
Some(ref checker) => match checker.check(self.chain, &tx) {
Ok(true) => pool::client::TransactionType::Service,
Ok(false) => pool::client::TransactionType::Regular,
Err(e) => {
debug!(target: "txqueue", "Unable to verify service transaction: {:?}", e);
pool::client::TransactionType::Regular
},
}
}
}
}
impl<'a, C: 'a> NonceClient for PoolClient<'a, C> where
C: Nonce + Sync,
{
fn account_nonce(&self, address: &Address) -> U256 {
self.cached_nonces.account_nonce(address)
}
}
pub(crate) struct CachedNonceClient<'a, C: 'a> {
client: &'a C,
cache: &'a NoncesCache,
}
impl<'a, C: 'a> Clone for CachedNonceClient<'a, C> {
fn clone(&self) -> Self {
CachedNonceClient {
client: self.client,
cache: self.cache,
}
}
}
impl<'a, C: 'a> fmt::Debug for CachedNonceClient<'a, C> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("CachedNonceClient")
.field("cache", &self.cache.read().len())
.finish()
}
}
impl<'a, C: 'a> CachedNonceClient<'a, C> {
pub fn new(client: &'a C, cache: &'a NoncesCache) -> Self {
CachedNonceClient {
client,
cache,
}
}
}
impl<'a, C: 'a> NonceClient for CachedNonceClient<'a, C> where
C: Nonce + Sync,
{
fn account_nonce(&self, address: &Address) -> U256 {
if let Some(nonce) = self.cache.read().get(address) {
return *nonce;
}
// We don't check again if cache has been populated.
// It's not THAT expensive to fetch the nonce from state.
let mut cache = self.cache.write();
let nonce = self.client.latest_nonce(address);
cache.insert(*address, nonce);
if cache.len() < MAX_NONCE_CACHE_SIZE {
return nonce
}
// Remove excessive amount of entries from the cache
while cache.len() > EXPECTED_NONCE_CACHE_SIZE {
// Just remove random entry
if let Some(key) = cache.keys().next().cloned() {
cache.remove(&key);
}
}
nonce
}
}

View File

@@ -16,33 +16,38 @@
//! A service transactions contract checker.
use client::{RegistryInfo, CallContract};
use client::{RegistryInfo, CallContract, BlockId};
use transaction::SignedTransaction;
use types::ids::BlockId;
use_contract!(service_transaction, "ServiceTransaction", "res/contracts/service_transaction.json");
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";
/// Service transactions checker.
#[derive(Default)]
#[derive(Default, Clone)]
pub struct ServiceTransactionChecker {
contract: service_transaction::ServiceTransaction,
}
impl ServiceTransactionChecker {
/// Checks if service transaction can be appended to the transaction queue.
/// Checks if given address is whitelisted to send service transactions.
pub fn check<C: CallContract + RegistryInfo>(&self, client: &C, tx: &SignedTransaction) -> Result<bool, String> {
assert!(tx.gas_price.is_zero());
let sender = tx.sender();
let hash = tx.hash();
// Skip checking the contract if the transaction does not have zero gas price
if !tx.gas_price.is_zero() {
return Ok(false)
}
let address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest)
.ok_or_else(|| "contract is not configured")?;
trace!(target: "txqueue", "Checking service transaction checker contract from {}", address);
trace!(target: "txqueue", "[{:?}] Checking service transaction checker contract from {}", hash, sender);
self.contract.functions()
.certified()
.call(tx.sender(), &|data| client.call_contract(BlockId::Latest, address, data))
.call(sender, &|data| client.call_contract(BlockId::Latest, address, data))
.map_err(|e| e.to_string())
}
}

View File

@@ -20,8 +20,7 @@ use std::sync::{Arc, Weak};
use std::net::{SocketAddr, AddrParseError};
use std::fmt;
use block::IsBlock;
use client::Client;
use client::{Client, ImportSealedBlock};
use ethereum_types::{H64, H256, clean_0x, U256};
use ethereum::ethash::Ethash;
use ethash::SeedHashCompute;
@@ -30,7 +29,7 @@ use ethcore_stratum::{
JobDispatcher, PushWorkHandler,
Stratum as StratumService, Error as StratumServiceError,
};
use miner::{self, Miner, MinerService};
use miner::{Miner, MinerService};
use parking_lot::Mutex;
use rlp::encode;
@@ -120,14 +119,9 @@ impl JobDispatcher for StratumJobDispatcher {
}
fn job(&self) -> Option<String> {
self.with_core(|client, miner| miner.map_sealing_work(&*client, |b| {
let pow_hash = b.hash();
let number = b.block().header().number();
let difficulty = b.block().header().difficulty();
self.payload(pow_hash, *difficulty, number)
})
)
self.with_core(|client, miner| miner.work_package(&*client).map(|(pow_hash, number, _timestamp, difficulty)| {
self.payload(pow_hash, difficulty, number)
}))
}
fn submit(&self, payload: Vec<String>) -> Result<(), StratumServiceError> {
@@ -145,7 +139,10 @@ impl JobDispatcher for StratumJobDispatcher {
self.with_core_result(|client, miner| {
let seal = vec![encode(&payload.mix_hash).into_vec(), encode(&payload.nonce).into_vec()];
match miner.submit_seal(&*client, payload.pow_hash, seal) {
let import = miner.submit_seal(payload.pow_hash, seal)
.and_then(|block| client.import_sealed_block(block));
match import {
Ok(_) => Ok(()),
Err(e) => {
warn!(target: "stratum", "submit_seal error: {:?}", e);
@@ -247,8 +244,8 @@ impl Stratum {
/// Start STRATUM job dispatcher and register it in the miner
pub fn register(cfg: &Options, miner: Arc<Miner>, client: Weak<Client>) -> Result<(), Error> {
let stratum = miner::Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?;
miner.push_notifier(Box::new(stratum) as Box<NotifyWork>);
let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?;
miner.add_work_listener(Box::new(stratum) as Box<NotifyWork>);
Ok(())
}
}

View File

@@ -107,13 +107,11 @@ fn make_chain(accounts: Arc<AccountProvider>, blocks_beyond: usize, transitions:
trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}",
n, txs.len(), signers[idx]);
client.miner().set_author(signers[idx]);
client.miner().set_author(signers[idx], Some(PASS.into())).unwrap();
client.miner().import_external_transactions(&*client,
txs.into_iter().map(Into::into).collect());
let engine = client.engine();
engine.set_signer(accounts.clone(), signers[idx], PASS.to_owned());
engine.step();
client.engine().step();
assert_eq!(client.chain_info().best_block_number, n);
};

View File

@@ -58,7 +58,7 @@ fn restored_is_equivalent() {
Default::default(),
&spec,
Arc::new(client_db),
Arc::new(::miner::Miner::with_spec(&spec)),
Arc::new(::miner::Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();

View File

@@ -120,7 +120,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data<F>(test_spec: F, accoun
ClientConfig::default(),
&test_spec,
client_db,
Arc::new(Miner::with_spec_and_accounts(&test_spec, accounts)),
Arc::new(Miner::new_for_tests(&test_spec, accounts)),
IoChannel::disconnected(),
).unwrap();
let test_engine = &*test_spec.engine;
@@ -243,7 +243,7 @@ pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> Arc<Client> {
ClientConfig::default(),
&test_spec,
client_db,
Arc::new(Miner::with_spec(&test_spec)),
Arc::new(Miner::new_for_tests(&test_spec, None)),
IoChannel::disconnected(),
).unwrap();

View File

@@ -49,7 +49,7 @@ fn imports_from_empty() {
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
client.import_verified_blocks();
@@ -67,7 +67,7 @@ fn should_return_registrar() {
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
let params = client.additional_params();
@@ -97,7 +97,7 @@ fn imports_good_block() {
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
let good_block = get_good_dummy_block();
@@ -122,7 +122,7 @@ fn query_none_block() {
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
let non_existant = client.block_header(BlockId::Number(188));
@@ -277,7 +277,7 @@ fn change_history_size() {
ClientConfig::default(),
&test_spec,
client_db.clone(),
Arc::new(Miner::with_spec(&test_spec)),
Arc::new(Miner::new_for_tests(&test_spec, None)),
IoChannel::disconnected()
).unwrap();
@@ -295,7 +295,7 @@ fn change_history_size() {
config,
&test_spec,
client_db,
Arc::new(Miner::with_spec(&test_spec)),
Arc::new(Miner::new_for_tests(&test_spec, None)),
IoChannel::disconnected(),
).unwrap();
assert_eq!(client.state().balance(&address).unwrap(), 100.into());
@@ -326,11 +326,11 @@ fn does_not_propagate_delayed_transactions() {
client.miner().import_own_transaction(&*client, tx0).unwrap();
client.miner().import_own_transaction(&*client, tx1).unwrap();
assert_eq!(0, client.ready_transactions().len());
assert_eq!(2, client.miner().pending_transactions().len());
assert_eq!(0, client.miner().ready_transactions(&*client).len());
push_blocks_to_client(&client, 53, 2, 2);
client.flush_queue();
assert_eq!(2, client.ready_transactions().len());
assert_eq!(2, client.miner().pending_transactions().len());
assert_eq!(2, client.miner().ready_transactions(&*client).len());
}
#[test]

View File

@@ -50,7 +50,7 @@ fn can_trace_block_and_uncle_reward() {
client_config,
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();

View File

@@ -164,7 +164,7 @@ mod test {
ClientConfig::default(),
&spec,
client_db,
Arc::new(Miner::with_spec(&spec)),
Arc::new(Miner::new_for_tests(&spec, None)),
IoChannel::disconnected(),
).unwrap();
let key1 = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000001")).unwrap();