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:
committed by
Marek Kotewicz
parent
03b96a7c0a
commit
1cd93e4ceb
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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(ðkey::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>> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
ðkey::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(ðkey::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(ðkey::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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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(ðereum::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;
|
||||
}
|
||||
|
||||
216
ethcore/src/miner/pool_client.rs
Normal file
216
ethcore/src/miner/pool_client.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user