diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ab1c21af1..8f6d607ba 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -37,7 +37,7 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, TraceFilter}; +use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, ExtendedBlockChainClient, TraceFilter}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -448,81 +448,10 @@ impl BlockChainClient for Client where V: Verifier { Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options) } - // TODO [todr] Should be moved to miner crate eventually. - fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result { - block.try_seal(self.engine.deref().deref(), seal) - } - fn vm_factory(&self) -> &EvmFactory { &self.vm_factory } - // TODO [todr] Should be moved to miner crate eventually. - fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec) - -> (Option, HashSet) { - let engine = self.engine.deref().deref(); - let h = self.chain.best_block_hash(); - let mut invalid_transactions = HashSet::new(); - - let mut b = OpenBlock::new( - engine, - &self.vm_factory, - false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. - self.state_db.lock().unwrap().boxed_clone(), - match self.chain.block_header(&h) { Some(ref x) => x, None => { return (None, invalid_transactions) } }, - self.build_last_hashes(h.clone()), - author, - gas_floor_target, - extra_data, - ); - - // Add uncles - self.chain - .find_uncle_headers(&h, engine.maximum_uncle_age()) - .unwrap() - .into_iter() - .take(engine.maximum_uncle_count()) - .foreach(|h| { - b.push_uncle(h).unwrap(); - }); - - // Add transactions - let block_number = b.block().header().number(); - let min_tx_gas = U256::from(self.engine.schedule(&b.env_info()).tx_gas); - - for tx in transactions { - // Push transaction to block - let hash = tx.hash(); - let import = b.push_transaction(tx, None); - - match import { - Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => { - trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); - // Exit early if gas left is smaller then min_tx_gas - if gas_limit - gas_used < min_tx_gas { - break; - } - }, - Err(e) => { - invalid_transactions.insert(hash); - trace!(target: "miner", - "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", - block_number, hash, e); - }, - _ => {} - } - } - - // And close - let b = b.close(); - trace!(target: "miner", "Sealing: number={}, hash={}, diff={}", - b.block().header().number(), - b.hash(), - b.block().header().difficulty() - ); - (Some(b), invalid_transactions) - } - fn block_header(&self, id: BlockID) -> Option { Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())) } @@ -776,6 +705,79 @@ impl BlockChainClient for Client where V: Verifier { } } +impl ExtendedBlockChainClient for Client where V: Verifier { + // TODO [todr] Should be moved to miner crate eventually. + fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec) + -> (Option, HashSet) { + let engine = self.engine.deref().deref(); + let h = self.chain.best_block_hash(); + let mut invalid_transactions = HashSet::new(); + + let mut b = OpenBlock::new( + engine, + &self.vm_factory, + false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. + self.state_db.lock().unwrap().boxed_clone(), + match self.chain.block_header(&h) { Some(ref x) => x, None => { return (None, invalid_transactions) } }, + self.build_last_hashes(h.clone()), + author, + gas_floor_target, + extra_data, + ); + + // Add uncles + self.chain + .find_uncle_headers(&h, engine.maximum_uncle_age()) + .unwrap() + .into_iter() + .take(engine.maximum_uncle_count()) + .foreach(|h| { + b.push_uncle(h).unwrap(); + }); + + // Add transactions + let block_number = b.block().header().number(); + let min_tx_gas = U256::from(self.engine.schedule(&b.env_info()).tx_gas); + + for tx in transactions { + // Push transaction to block + let hash = tx.hash(); + let import = b.push_transaction(tx, None); + + match import { + Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => { + trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); + // Exit early if gas left is smaller then min_tx_gas + if gas_limit - gas_used < min_tx_gas { + break; + } + }, + Err(e) => { + invalid_transactions.insert(hash); + trace!(target: "miner", + "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", + block_number, hash, e); + }, + _ => {} + } + } + + // And close + let b = b.close(); + trace!(target: "miner", "Sealing: number={}, hash={}, diff={}", + b.block().header().number(), + b.hash(), + b.block().header().difficulty() + ); + (Some(b), invalid_transactions) + } + + // TODO [todr] Should be moved to miner crate eventually. + fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result { + block.try_seal(self.engine.deref().deref(), seal) + } +} + impl MayPanic for Client { fn on_panic(&self, closure: F) where F: OnPanicListener { self.panic_handler.on_panic(closure); diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 8e0a7b2dd..5537f8e66 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -154,15 +154,6 @@ pub trait BlockChainClient : Sync + Send { /// Returns logs matching given filter. fn logs(&self, filter: Filter) -> Vec; - // TODO [todr] Should be moved to miner crate eventually. - /// Returns ClosedBlock prepared for sealing. - fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec) - -> (Option, HashSet); - - // TODO [todr] Should be moved to miner crate eventually. - /// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error. - fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result; - /// Makes a non-persistent transaction call. fn call(&self, t: &SignedTransaction) -> Result; @@ -185,3 +176,14 @@ pub trait BlockChainClient : Sync + Send { fn last_hashes(&self) -> LastHashes; } +/// Extended client interface used for mining +pub trait ExtendedBlockChainClient : BlockChainClient { + // TODO [todr] Should be moved to miner crate eventually. + /// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error. + fn try_seal(&self, block: LockedBlock, seal: Vec) -> Result; + + // TODO [todr] Should be moved to miner crate eventually. + /// Returns ClosedBlock prepared for sealing. + fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec) + -> (Option, HashSet); +} diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index de2973029..5c093b720 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; use util::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; -use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes}; +use client::{BlockChainClient, ExtendedBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; @@ -232,6 +232,17 @@ impl TestBlockChainClient { } } +impl ExtendedBlockChainClient for TestBlockChainClient { + fn try_seal(&self, block: LockedBlock, _seal: Vec) -> Result { + Err(block) + } + + + fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec) -> (Option, HashSet) { + (None, HashSet::new()) + } +} + impl BlockChainClient for TestBlockChainClient { fn call(&self, _t: &SignedTransaction) -> Result { Ok(self.execution_result.read().unwrap().clone().unwrap()) @@ -296,14 +307,6 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec) -> (Option, HashSet) { - (None, HashSet::new()) - } - - fn try_seal(&self, block: LockedBlock, _seal: Vec) -> Result { - Err(block) - } - fn block_header(&self, id: BlockID) -> Option { self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) } diff --git a/miner/src/lib.rs b/miner/src/lib.rs index a1780efff..db3315376 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -62,7 +62,7 @@ pub use external::{ExternalMiner, ExternalMinerService}; use std::collections::BTreeMap; use util::{H256, U256, Address, Bytes}; -use ethcore::client::{BlockChainClient, Executed}; +use ethcore::client::{ExtendedBlockChainClient, Executed}; use ethcore::block::ClosedBlock; use ethcore::receipt::Receipt; use ethcore::error::{Error, ExecutionError}; @@ -110,7 +110,7 @@ pub trait MinerService : Send + Sync { where T: Fn(&Address) -> AccountDetails, Self: Sized; /// Imports own (node owner) transaction to queue. - fn import_own_transaction(&self, chain: &BlockChainClient, transaction: SignedTransaction, fetch_account: T) -> + fn import_own_transaction(&self, chain: &ExtendedBlockChainClient, transaction: SignedTransaction, fetch_account: T) -> Result where T: Fn(&Address) -> AccountDetails, Self: Sized; @@ -118,20 +118,20 @@ pub trait MinerService : Send + Sync { fn pending_transactions_hashes(&self) -> Vec; /// Removes all transactions from the queue and restart mining operation. - fn clear_and_reset(&self, chain: &BlockChainClient); + fn clear_and_reset(&self, chain: &ExtendedBlockChainClient); /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, chain: &BlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]); + fn chain_new_blocks(&self, chain: &ExtendedBlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]); /// New chain head event. Restart mining operation. - fn update_sealing(&self, chain: &BlockChainClient); + fn update_sealing(&self, chain: &ExtendedBlockChainClient); /// 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(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error>; + fn submit_seal(&self, chain: &ExtendedBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error>; /// Get the sealing work package and if `Some`, apply some transform. - fn map_sealing_work(&self, chain: &BlockChainClient, f: F) -> Option + fn map_sealing_work(&self, chain: &ExtendedBlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T, Self: Sized; /// Query pending transactions for hash. @@ -156,19 +156,19 @@ pub trait MinerService : Send + Sync { fn sensible_gas_limit(&self) -> U256 { x!(21000) } /// Latest account balance in pending state. - fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256; + fn balance(&self, chain: &ExtendedBlockChainClient, address: &Address) -> U256; /// Call into contract code using pending state. - fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result; + fn call(&self, chain: &ExtendedBlockChainClient, t: &SignedTransaction) -> Result; /// Get storage value in pending state. - fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256; + fn storage_at(&self, chain: &ExtendedBlockChainClient, address: &Address, position: &H256) -> H256; /// Get account nonce in pending state. - fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256; + fn nonce(&self, chain: &ExtendedBlockChainClient, address: &Address) -> U256; /// Get contract code in pending state. - fn code(&self, chain: &BlockChainClient, address: &Address) -> Option; + fn code(&self, chain: &ExtendedBlockChainClient, address: &Address) -> Option; } /// Mining status diff --git a/miner/src/miner.rs b/miner/src/miner.rs index fc63aec6c..7438e7e9b 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -20,7 +20,7 @@ use std::sync::atomic::AtomicBool; use util::*; use util::keys::store::{AccountService, AccountProvider}; use ethcore::views::{BlockView, HeaderView}; -use ethcore::client::{BlockChainClient, BlockID}; +use ethcore::client::{ExtendedBlockChainClient, BlockID}; use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::error::*; use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions}; @@ -104,7 +104,7 @@ impl Miner { /// Prepares new block for sealing including top transactions from queue. #[cfg_attr(feature="dev", allow(match_same_arms))] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] - fn prepare_sealing(&self, chain: &BlockChainClient) { + fn prepare_sealing(&self, chain: &ExtendedBlockChainClient) { trace!(target: "miner", "prepare_sealing: entering"); let transactions = self.transaction_queue.lock().unwrap().top_transactions(); let mut sealing_work = self.sealing_work.lock().unwrap(); @@ -206,14 +206,14 @@ impl Miner { trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); } - fn update_gas_limit(&self, chain: &BlockChainClient) { + fn update_gas_limit(&self, chain: &ExtendedBlockChainClient) { let gas_limit = HeaderView::new(&chain.best_block_header()).gas_limit(); let mut queue = self.transaction_queue.lock().unwrap(); queue.set_gas_limit(gas_limit); } /// Returns true if we had to prepare new pending block - fn enable_and_prepare_sealing(&self, chain: &BlockChainClient) -> bool { + fn enable_and_prepare_sealing(&self, chain: &ExtendedBlockChainClient) -> bool { trace!(target: "miner", "enable_and_prepare_sealing: entering"); let have_work = self.sealing_work.lock().unwrap().peek_last_ref().is_some(); trace!(target: "miner", "enable_and_prepare_sealing: have_work={}", have_work); @@ -237,7 +237,7 @@ const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; impl MinerService for Miner { - fn clear_and_reset(&self, chain: &BlockChainClient) { + fn clear_and_reset(&self, chain: &ExtendedBlockChainClient) { self.transaction_queue.lock().unwrap().clear(); self.update_sealing(chain); } @@ -252,7 +252,7 @@ impl MinerService for Miner { } } - fn call(&self, chain: &BlockChainClient, t: &SignedTransaction) -> Result { + fn call(&self, chain: &ExtendedBlockChainClient, t: &SignedTransaction) -> Result { let sealing_work = self.sealing_work.lock().unwrap(); match sealing_work.peek_last_ref() { Some(work) => { @@ -288,7 +288,7 @@ impl MinerService for Miner { } } - fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256 { + fn balance(&self, chain: &ExtendedBlockChainClient, address: &Address) -> U256 { let sealing_work = self.sealing_work.lock().unwrap(); sealing_work.peek_last_ref().map_or_else( || chain.latest_balance(address), @@ -296,7 +296,7 @@ impl MinerService for Miner { ) } - fn storage_at(&self, chain: &BlockChainClient, address: &Address, position: &H256) -> H256 { + fn storage_at(&self, chain: &ExtendedBlockChainClient, address: &Address, position: &H256) -> H256 { let sealing_work = self.sealing_work.lock().unwrap(); sealing_work.peek_last_ref().map_or_else( || chain.latest_storage_at(address, position), @@ -304,12 +304,12 @@ impl MinerService for Miner { ) } - fn nonce(&self, chain: &BlockChainClient, address: &Address) -> U256 { + fn nonce(&self, chain: &ExtendedBlockChainClient, address: &Address) -> U256 { let sealing_work = self.sealing_work.lock().unwrap(); sealing_work.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address)) } - fn code(&self, chain: &BlockChainClient, address: &Address) -> Option { + fn code(&self, chain: &ExtendedBlockChainClient, address: &Address) -> Option { let sealing_work = self.sealing_work.lock().unwrap(); sealing_work.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address)) } @@ -376,7 +376,7 @@ impl MinerService for Miner { .collect() } - fn import_own_transaction(&self, chain: &BlockChainClient, transaction: SignedTransaction, fetch_account: T) -> + fn import_own_transaction(&self, chain: &ExtendedBlockChainClient, transaction: SignedTransaction, fetch_account: T) -> Result where T: Fn(&Address) -> AccountDetails { let hash = transaction.hash(); @@ -470,7 +470,7 @@ impl MinerService for Miner { self.transaction_queue.lock().unwrap().last_nonce(address) } - fn update_sealing(&self, chain: &BlockChainClient) { + fn update_sealing(&self, chain: &ExtendedBlockChainClient) { if self.sealing_enabled.load(atomic::Ordering::Relaxed) { let current_no = chain.chain_info().best_block_number; let has_local_transactions = self.transaction_queue.lock().unwrap().has_local_pending_transactions(); @@ -490,7 +490,7 @@ impl MinerService for Miner { } } - fn map_sealing_work(&self, chain: &BlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { + fn map_sealing_work(&self, chain: &ExtendedBlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { trace!(target: "miner", "map_sealing_work: entering"); self.enable_and_prepare_sealing(chain); trace!(target: "miner", "map_sealing_work: sealing prepared"); @@ -500,7 +500,7 @@ impl MinerService for Miner { ret.map(f) } - fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> { + fn submit_seal(&self, chain: &ExtendedBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> { if let Some(b) = self.sealing_work.lock().unwrap().take_used_if(|b| &b.hash() == &pow_hash) { match chain.try_seal(b.lock(), seal) { Err(_) => { @@ -523,8 +523,8 @@ impl MinerService for Miner { } } - fn chain_new_blocks(&self, chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { - fn fetch_transactions(chain: &BlockChainClient, hash: &H256) -> Vec { + fn chain_new_blocks(&self, chain: &ExtendedBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { + fn fetch_transactions(chain: &ExtendedBlockChainClient, hash: &H256) -> Vec { let block = chain .block(BlockID::Hash(*hash)) // Client should send message after commit to db and inserting to chain. @@ -591,7 +591,7 @@ mod tests { use ethcore::client::{TestBlockChainClient, EachBlockWith}; use ethcore::block::*; - // TODO [ToDr] To uncomment when TestBlockChainClient can actually return a ClosedBlock. + // TODO [ToDr] To uncomment` when TestBlockChainClient can actually return a ClosedBlock. #[ignore] #[test] fn should_prepare_block_to_seal() {