Merge pull request #1232 from ethcore/simplified_block_opening

simplified block opening
This commit is contained in:
Marek Kotewicz 2016-06-13 06:56:24 -07:00 committed by GitHub
commit a8831fe896
5 changed files with 69 additions and 107 deletions

View File

@ -756,18 +756,16 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
} }
impl<V> MiningBlockChainClient for Client<V> where V: Verifier { impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec<SignedTransaction>) fn prepare_open_block(&self, author: Address, gas_floor_target: U256, extra_data: Bytes) -> OpenBlock {
-> (Option<ClosedBlock>, HashSet<H256>) {
let engine = self.engine.deref().deref(); let engine = self.engine.deref().deref();
let h = self.chain.best_block_hash(); let h = self.chain.best_block_hash();
let mut invalid_transactions = HashSet::new();
let mut b = OpenBlock::new( let mut open_block = OpenBlock::new(
engine, engine,
&self.vm_factory, &self.vm_factory,
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().unwrap().boxed_clone(), self.state_db.lock().unwrap().boxed_clone(),
match self.chain.block_header(&h) { Some(ref x) => x, None => { return (None, invalid_transactions) } }, &self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"),
self.build_last_hashes(h.clone()), self.build_last_hashes(h.clone()),
author, author,
gas_floor_target, gas_floor_target,
@ -782,44 +780,10 @@ impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
.into_iter() .into_iter()
.take(engine.maximum_uncle_count()) .take(engine.maximum_uncle_count())
.foreach(|h| { .foreach(|h| {
b.push_uncle(h).unwrap(); open_block.push_uncle(h).unwrap();
}); });
// Add transactions open_block
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 try_seal(&self, block: LockedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock> { fn try_seal(&self, block: LockedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock> {

View File

@ -31,13 +31,12 @@ pub use self::trace::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions}; pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo}; pub use env_info::{LastHashes, EnvInfo};
use std::collections::HashSet;
use util::bytes::Bytes; use util::bytes::Bytes;
use util::hash::{Address, H256, H2048}; use util::hash::{Address, H256, H2048};
use util::numbers::U256; use util::numbers::U256;
use blockchain::TreeRoute; use blockchain::TreeRoute;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{ClosedBlock, LockedBlock, SealedBlock}; use block::{LockedBlock, SealedBlock, OpenBlock};
use header::{BlockNumber, Header}; use header::{BlockNumber, Header};
use transaction::{LocalizedTransaction, SignedTransaction}; use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
@ -201,7 +200,7 @@ pub trait MiningBlockChainClient : BlockChainClient {
/// Attempts to seal given block. Returns `SealedBlock` on success and the same block in case of error. /// 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<Bytes>) -> Result<SealedBlock, LockedBlock>; fn try_seal(&self, block: LockedBlock, seal: Vec<Bytes>) -> Result<SealedBlock, LockedBlock>;
/// Returns ClosedBlock prepared for sealing. /// Returns OpenBlock prepared for closing.
fn prepare_sealing(&self, author: Address, gas_floor_target: U256, extra_data: Bytes, transactions: Vec<SignedTransaction>) fn prepare_open_block(&self, author: Address, gas_floor_target: U256, extra_data: Bytes)
-> (Option<ClosedBlock>, HashSet<H256>); -> OpenBlock;
} }

View File

@ -31,7 +31,7 @@ use evm::Factory as EvmFactory;
use miner::{Miner, MinerService}; use miner::{Miner, MinerService};
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{SealedBlock, ClosedBlock, LockedBlock}; use block::{SealedBlock, LockedBlock, OpenBlock};
use executive::Executed; use executive::Executed;
use error::{ExecutionError}; use error::{ExecutionError};
use trace::LocalizedTrace; use trace::LocalizedTrace;
@ -245,8 +245,8 @@ impl MiningBlockChainClient for TestBlockChainClient {
} }
fn prepare_sealing(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes, _transactions: Vec<SignedTransaction>) -> (Option<ClosedBlock>, HashSet<H256>) { fn prepare_open_block(&self, _author: Address, _gas_floor_target: U256, _extra_data: Bytes) -> OpenBlock {
(None, HashSet::new()) unimplemented!();
} }
} }

View File

@ -117,90 +117,89 @@ impl Miner {
// otherwise, leave everything alone. // otherwise, leave everything alone.
// otherwise, author a fresh block. // otherwise, author a fresh block.
*/ */
let mut open_block = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
let (b, invalid_transactions) = match sealing_work.pop_if(|b| b.block().fields().header.parent_hash() == &best_hash) {
Some(old_block) => { Some(old_block) => {
trace!(target: "miner", "Already have previous work; updating and returning"); trace!(target: "miner", "Already have previous work; updating and returning");
// add transactions to old_block // add transactions to old_block
let e = self.engine(); let e = self.engine();
let mut invalid_transactions = HashSet::new(); old_block.reopen(e, chain.vm_factory())
let mut block = old_block.reopen(e, chain.vm_factory());
let block_number = block.block().fields().header.number();
// TODO: push new uncles, too.
// TODO: refactor with chain.prepare_sealing
for tx in transactions {
let hash = tx.hash();
let res = block.push_transaction(tx, None);
match res {
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
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas {
break;
}
},
Err(Error::Transaction(TransactionError::AlreadyImported)) => {} // already have transaction - ignore
Err(e) => {
invalid_transactions.insert(hash);
trace!(target: "miner",
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
block_number, hash, e);
},
_ => {} // imported ok
}
}
(Some(block.close()), invalid_transactions)
} }
None => { None => {
// block not found - create it. // block not found - create it.
trace!(target: "miner", "No existing work - making new block"); trace!(target: "miner", "No existing work - making new block");
chain.prepare_sealing( chain.prepare_open_block(
self.author(), self.author(),
self.gas_floor_target(), self.gas_floor_target(),
self.extra_data(), self.extra_data()
transactions,
) )
} }
}; };
let mut invalid_transactions = HashSet::new();
let block_number = open_block.block().fields().header.number();
// TODO: push new uncles, too.
for tx in transactions {
let hash = tx.hash();
match open_block.push_transaction(tx, None) {
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
let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly.
if gas_limit - gas_used < min_tx_gas {
break;
}
},
Err(Error::Transaction(TransactionError::AlreadyImported)) => {} // already have transaction - ignore
Err(e) => {
invalid_transactions.insert(hash);
trace!(target: "miner",
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
block_number, hash, e);
},
_ => {} // imported ok
}
}
let block = open_block.close();
let mut queue = self.transaction_queue.lock().unwrap(); let mut queue = self.transaction_queue.lock().unwrap();
let fetch_account = |a: &Address| AccountDetails { let fetch_account = |a: &Address| AccountDetails {
nonce: chain.latest_nonce(a), nonce: chain.latest_nonce(a),
balance: chain.latest_balance(a), balance: chain.latest_balance(a),
}; };
for hash in invalid_transactions.into_iter() { for hash in invalid_transactions.into_iter() {
queue.remove_invalid(&hash, &fetch_account); queue.remove_invalid(&hash, &fetch_account);
} }
if let Some(block) = b {
if !block.transactions().is_empty() { if !block.transactions().is_empty() {
trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal."); trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal.");
// block with transactions - see if we can seal immediately. // block with transactions - see if we can seal immediately.
let s = self.engine().generate_seal(block.block(), match self.accounts { let s = self.engine().generate_seal(block.block(), match self.accounts {
Some(ref x) => Some(&**x), Some(ref x) => Some(&**x),
None => None, None => None,
}); });
if let Some(seal) = s { if let Some(seal) = s {
trace!(target: "miner", "prepare_sealing: managed internal seal. importing..."); trace!(target: "miner", "prepare_sealing: managed internal seal. importing...");
if let Ok(sealed) = chain.try_seal(block.lock(), seal) { if let Ok(sealed) = chain.try_seal(block.lock(), seal) {
if let Ok(_) = chain.import_block(sealed.rlp_bytes()) { if let Ok(_) = chain.import_block(sealed.rlp_bytes()) {
trace!(target: "miner", "prepare_sealing: sealed internally and imported. leaving."); trace!(target: "miner", "prepare_sealing: sealed internally and imported. leaving.");
} else {
warn!("prepare_sealing: ERROR: could not import internally sealed block. WTF?");
}
} else { } else {
warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?"); warn!("prepare_sealing: ERROR: could not import internally sealed block. WTF?");
} }
return;
} else { } else {
trace!(target: "miner", "prepare_sealing: unable to generate seal internally"); warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?");
} }
} return;
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) { } else {
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash()); trace!(target: "miner", "prepare_sealing: unable to generate seal internally");
sealing_work.push(block);
} }
} }
if sealing_work.peek_last_ref().map_or(true, |pb| pb.block().fields().header.hash() != block.block().fields().header.hash()) {
trace!(target: "miner", "Pushing a new, refreshed or borrowed pending {}...", block.block().fields().header.hash());
sealing_work.push(block);
}
trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash())); trace!(target: "miner", "prepare_sealing: leaving (last={:?})", sealing_work.peek_last_ref().map(|b| b.block().fields().header.hash()));
} }

View File

@ -137,7 +137,7 @@ fn can_mine() {
let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]);
let client = client_result.reference(); let client = client_result.reference();
let b = client.prepare_sealing(Address::default(), 31415926.into(), vec![], vec![]).0.unwrap(); let b = client.prepare_open_block(Address::default(), 31415926.into(), vec![]).close();
assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3()); assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3());
assert!(client.try_seal(b.lock(), vec![]).is_ok()); assert!(client.try_seal(b.lock(), vec![]).is_ok());