Using pending block only if not old (#2514)

This commit is contained in:
Tomasz Drwięga 2016-10-07 12:13:15 +02:00 committed by Gav Wood
parent 7756031d06
commit 4655fd04a5
7 changed files with 152 additions and 79 deletions

View File

@ -1078,7 +1078,7 @@ impl BlockChainClient for Client {
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions() self.miner.pending_transactions(self.chain.read().best_block_number())
} }
} }

View File

@ -602,6 +602,6 @@ impl BlockChainClient for TestBlockChainClient {
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions() self.miner.pending_transactions(self.chain_info().best_block_number)
} }
} }

View File

@ -493,6 +493,21 @@ impl Miner {
/// Are we allowed to do a non-mandatory reseal? /// Are we allowed to do a non-mandatory reseal?
fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() } fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() }
fn from_pending_block<H, F, G>(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H
where F: Fn() -> H, G: Fn(&ClosedBlock) -> H {
let sealing_work = self.sealing_work.lock();
sealing_work.queue.peek_last_ref().map_or_else(
|| from_chain(),
|b| {
if b.block().header().number() > latest_block_number {
map_block(b)
} else {
from_chain()
}
}
)
}
} }
const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5;
@ -565,29 +580,35 @@ impl MinerService for Miner {
} }
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 { fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
sealing_work.queue.peek_last_ref().map_or_else( chain.chain_info().best_block_number,
|| chain.latest_balance(address), || chain.latest_balance(address),
|b| b.block().fields().state.balance(address) |b| b.block().fields().state.balance(address)
) )
} }
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 { fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
sealing_work.queue.peek_last_ref().map_or_else( chain.chain_info().best_block_number,
|| chain.latest_storage_at(address, position), || chain.latest_storage_at(address, position),
|b| b.block().fields().state.storage_at(address, position) |b| b.block().fields().state.storage_at(address, position)
) )
} }
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 { fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address)) chain.chain_info().best_block_number,
|| chain.latest_nonce(address),
|b| b.block().fields().state.nonce(address)
)
} }
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> { fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address).map(|c| (*c).clone())) chain.chain_info().best_block_number,
|| chain.latest_code(address),
|b| b.block().fields().state.code(address).map(|c| (*c).clone())
)
} }
fn set_author(&self, author: Address) { fn set_author(&self, author: Address) {
@ -737,50 +758,74 @@ impl MinerService for Miner {
queue.top_transactions() queue.top_transactions()
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction> {
let queue = self.transaction_queue.lock(); let queue = self.transaction_queue.lock();
let sw = self.sealing_work.lock(); match self.options.pending_set {
// TODO: should only use the sealing_work when it's current (it could be an old block) PendingSet::AlwaysQueue => queue.top_transactions(),
let sealing_set = match sw.enabled { PendingSet::SealingOrElseQueue => {
true => sw.queue.peek_last_ref(), self.from_pending_block(
false => None, best_block,
}; || queue.top_transactions(),
match (&self.options.pending_set, sealing_set) { |sealing| sealing.transactions().to_owned()
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.top_transactions(), )
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().to_owned()), },
PendingSet::AlwaysSealing => {
self.from_pending_block(
best_block,
|| vec![],
|sealing| sealing.transactions().to_owned()
)
},
} }
} }
fn pending_transactions_hashes(&self) -> Vec<H256> { fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256> {
let queue = self.transaction_queue.lock(); let queue = self.transaction_queue.lock();
let sw = self.sealing_work.lock(); match self.options.pending_set {
let sealing_set = match sw.enabled { PendingSet::AlwaysQueue => queue.pending_hashes(),
true => sw.queue.peek_last_ref(), PendingSet::SealingOrElseQueue => {
false => None, self.from_pending_block(
}; best_block,
match (&self.options.pending_set, sealing_set) { || queue.pending_hashes(),
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.pending_hashes(), |sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().iter().map(|t| t.hash()).collect()), )
},
PendingSet::AlwaysSealing => {
self.from_pending_block(
best_block,
|| vec![],
|sealing| sealing.transactions().iter().map(|t| t.hash()).collect()
)
},
} }
} }
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> { fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction> {
let queue = self.transaction_queue.lock(); let queue = self.transaction_queue.lock();
let sw = self.sealing_work.lock(); match self.options.pending_set {
let sealing_set = match sw.enabled { PendingSet::AlwaysQueue => queue.find(hash),
true => sw.queue.peek_last_ref(), PendingSet::SealingOrElseQueue => {
false => None, self.from_pending_block(
}; best_block,
match (&self.options.pending_set, sealing_set) { || queue.find(hash),
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.find(hash), |sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
(_, sealing) => sealing.and_then(|s| s.transactions().iter().find(|t| &t.hash() == hash).cloned()), )
},
PendingSet::AlwaysSealing => {
self.from_pending_block(
best_block,
|| None,
|sealing| sealing.transactions().iter().find(|t| &t.hash() == hash).cloned()
)
},
} }
} }
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt> { fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) { best_block,
(true, Some(pending)) => { || None,
|pending| {
let txs = pending.transactions(); let txs = pending.transactions();
txs.iter() txs.iter()
.map(|t| t.hash()) .map(|t| t.hash())
@ -801,15 +846,15 @@ impl MinerService for Miner {
logs: receipt.logs.clone(), logs: receipt.logs.clone(),
} }
}) })
}, }
_ => None )
}
} }
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> { fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
let sealing_work = self.sealing_work.lock(); self.from_pending_block(
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) { best_block,
(true, Some(pending)) => { || BTreeMap::new(),
|pending| {
let hashes = pending.transactions() let hashes = pending.transactions()
.iter() .iter()
.map(|t| t.hash()); .map(|t| t.hash());
@ -817,9 +862,8 @@ impl MinerService for Miner {
let receipts = pending.receipts().iter().cloned(); let receipts = pending.receipts().iter().cloned();
hashes.zip(receipts).collect() hashes.zip(receipts).collect()
}, }
_ => BTreeMap::new() )
}
} }
fn last_nonce(&self, address: &Address) -> Option<U256> { fn last_nonce(&self, address: &Address) -> Option<U256> {
@ -1044,34 +1088,54 @@ mod tests {
let client = TestBlockChainClient::default(); let client = TestBlockChainClient::default();
let miner = miner(); let miner = miner();
let transaction = transaction(); let transaction = transaction();
let best_block = 0;
// when // when
let res = miner.import_own_transaction(&client, transaction); let res = miner.import_own_transaction(&client, transaction);
// then // then
assert_eq!(res.unwrap(), TransactionImportResult::Current); assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.all_transactions().len(), 1); assert_eq!(miner.all_transactions().len(), 1);
assert_eq!(miner.pending_transactions().len(), 1); assert_eq!(miner.pending_transactions(best_block).len(), 1);
assert_eq!(miner.pending_transactions_hashes().len(), 1); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1);
assert_eq!(miner.pending_receipts().len(), 1); assert_eq!(miner.pending_receipts(best_block).len(), 1);
// This method will let us know if pending block was created (before calling that method) // This method will let us know if pending block was created (before calling that method)
assert!(!miner.prepare_work_sealing(&client)); assert!(!miner.prepare_work_sealing(&client));
} }
#[test]
fn should_not_use_pending_block_if_best_block_is_higher() {
// given
let client = TestBlockChainClient::default();
let miner = miner();
let transaction = transaction();
let best_block = 10;
// when
let res = miner.import_own_transaction(&client, transaction);
// then
assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.all_transactions().len(), 1);
assert_eq!(miner.pending_transactions(best_block).len(), 0);
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
assert_eq!(miner.pending_receipts(best_block).len(), 0);
}
#[test] #[test]
fn should_import_external_transaction() { fn should_import_external_transaction() {
// given // given
let client = TestBlockChainClient::default(); let client = TestBlockChainClient::default();
let miner = miner(); let miner = miner();
let transaction = transaction(); let transaction = transaction();
let best_block = 0;
// when // when
let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap(); let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap();
// then // then
assert_eq!(res.unwrap(), TransactionImportResult::Current); assert_eq!(res.unwrap(), TransactionImportResult::Current);
assert_eq!(miner.all_transactions().len(), 1); assert_eq!(miner.all_transactions().len(), 1);
assert_eq!(miner.pending_transactions_hashes().len(), 0); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
assert_eq!(miner.pending_transactions().len(), 0); assert_eq!(miner.pending_transactions(best_block).len(), 0);
assert_eq!(miner.pending_receipts().len(), 0); assert_eq!(miner.pending_receipts(best_block).len(), 0);
// This method will let us know if pending block was created (before calling that method) // This method will let us know if pending block was created (before calling that method)
assert!(miner.prepare_work_sealing(&client)); assert!(miner.prepare_work_sealing(&client));
} }

View File

@ -56,6 +56,7 @@ use std::collections::BTreeMap;
use util::{H256, U256, Address, Bytes}; use util::{H256, U256, Address, Bytes};
use client::{MiningBlockChainClient, Executed, CallAnalytics}; use client::{MiningBlockChainClient, Executed, CallAnalytics};
use block::ClosedBlock; use block::ClosedBlock;
use header::BlockNumber;
use receipt::{RichReceipt, Receipt}; use receipt::{RichReceipt, Receipt};
use error::{Error, CallError}; use error::{Error, CallError};
use transaction::SignedTransaction; use transaction::SignedTransaction;
@ -115,7 +116,7 @@ pub trait MinerService : Send + Sync {
Result<TransactionImportResult, Error>; Result<TransactionImportResult, Error>;
/// Returns hashes of transactions currently in pending /// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256>; fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec<H256>;
/// Removes all transactions from the queue and restart mining operation. /// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset(&self, chain: &MiningBlockChainClient); fn clear_and_reset(&self, chain: &MiningBlockChainClient);
@ -135,19 +136,19 @@ pub trait MinerService : Send + Sync {
where F: FnOnce(&ClosedBlock) -> T, Self: Sized; where F: FnOnce(&ClosedBlock) -> T, Self: Sized;
/// Query pending transactions for hash. /// Query pending transactions for hash.
fn transaction(&self, hash: &H256) -> Option<SignedTransaction>; fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction>;
/// Get a list of all transactions. /// Get a list of all transactions.
fn all_transactions(&self) -> Vec<SignedTransaction>; fn all_transactions(&self) -> Vec<SignedTransaction>;
/// Get a list of all pending transactions. /// Get a list of all pending transactions.
fn pending_transactions(&self) -> Vec<SignedTransaction>; fn pending_transactions(&self, best_block: BlockNumber) -> Vec<SignedTransaction>;
/// Get a list of all pending receipts. /// Get a list of all pending receipts.
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>; fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
/// Get a particular reciept. /// Get a particular reciept.
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt>; fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt>;
/// Returns highest transaction nonce for given address. /// Returns highest transaction nonce for given address.
fn last_nonce(&self, address: &Address) -> Option<U256>; fn last_nonce(&self, address: &Address) -> Option<U256>;

View File

@ -33,7 +33,7 @@ use util::{FromHex, Mutex};
use rlp::{self, UntrustedRlp, View}; use rlp::{self, UntrustedRlp, View};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID}; use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID};
use ethcore::header::Header as BlockHeader; use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber};
use ethcore::block::IsBlock; use ethcore::block::IsBlock;
use ethcore::views::*; use ethcore::views::*;
use ethcore::ethereum::Ethash; use ethcore::ethereum::Ethash;
@ -198,8 +198,8 @@ impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
} }
} }
pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService { pub fn pending_logs<M>(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec<Log> where M: MinerService {
let receipts = miner.pending_receipts(); let receipts = miner.pending_receipts(best_block);
let pending_logs = receipts.into_iter() let pending_logs = receipts.into_iter()
.flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>()) .flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::<Vec<(H256, LogEntry)>>())
@ -426,7 +426,8 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
try!(self.active()); try!(self.active());
let hash: H256 = hash.into(); let hash: H256 = hash.into();
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
Ok(try!(self.transaction(TransactionID::Hash(hash))).or_else(|| miner.transaction(&hash).map(Into::into))) let client = take_weak!(self.client);
Ok(try!(self.transaction(TransactionID::Hash(hash))).or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Into::into)))
} }
fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result<Option<Transaction>, Error> { fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result<Option<Transaction>, Error> {
@ -445,8 +446,9 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
try!(self.active()); try!(self.active());
let miner = take_weak!(self.miner); let miner = take_weak!(self.miner);
let best_block = take_weak!(self.client).chain_info().best_block_number;
let hash: H256 = hash.into(); let hash: H256 = hash.into();
match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) { match (miner.pending_receipt(best_block, &hash), self.options.allow_pending_receipt_query) {
(Some(receipt), true) => Ok(Some(receipt.into())), (Some(receipt), true) => Ok(Some(receipt.into())),
_ => { _ => {
let client = take_weak!(self.client); let client = take_weak!(self.client);
@ -488,7 +490,8 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
.collect::<Vec<Log>>(); .collect::<Vec<Log>>();
if include_pending { if include_pending {
let pending = pending_logs(&*take_weak!(self.miner), &filter); let best_block = take_weak!(self.client).chain_info().best_block_number;
let pending = pending_logs(&*take_weak!(self.miner), best_block, &filter);
logs.extend(pending); logs.extend(pending);
} }

View File

@ -81,7 +81,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
try!(self.active()); try!(self.active());
let mut polls = self.polls.lock(); let mut polls = self.polls.lock();
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(); let best_block = take_weak!(self.client).chain_info().best_block_number;
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(best_block);
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
Ok(id.into()) Ok(id.into())
} }
@ -108,7 +109,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
}, },
PollFilter::PendingTransaction(ref mut previous_hashes) => { PollFilter::PendingTransaction(ref mut previous_hashes) => {
// get hashes of pending transactions // get hashes of pending transactions
let current_hashes = take_weak!(self.miner).pending_transactions_hashes(); let best_block = take_weak!(self.client).chain_info().best_block_number;
let current_hashes = take_weak!(self.miner).pending_transactions_hashes(best_block);
let new_hashes = let new_hashes =
{ {
@ -149,7 +151,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
// additionally retrieve pending logs // additionally retrieve pending logs
if include_pending { if include_pending {
let pending_logs = pending_logs(&*take_weak!(self.miner), &filter); let best_block = take_weak!(self.client).chain_info().best_block_number;
let pending_logs = pending_logs(&*take_weak!(self.miner), best_block, &filter);
// remove logs about which client was already notified about // remove logs about which client was already notified about
let new_pending_logs: Vec<_> = pending_logs.iter() let new_pending_logs: Vec<_> = pending_logs.iter()
@ -190,7 +193,8 @@ impl<C, M> EthFilter for EthFilterClient<C, M>
.collect::<Vec<Log>>(); .collect::<Vec<Log>>();
if include_pending { if include_pending {
logs.extend(pending_logs(&*take_weak!(self.miner), &filter)); let best_block = take_weak!(self.client).chain_info().best_block_number;
logs.extend(pending_logs(&*take_weak!(self.miner), best_block, &filter));
} }
let logs = limit_logs(logs, filter.limit); let logs = limit_logs(logs, filter.limit);

View File

@ -21,6 +21,7 @@ use util::standard::*;
use ethcore::error::{Error, CallError}; use ethcore::error::{Error, CallError};
use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::header::BlockNumber;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::receipt::{Receipt, RichReceipt};
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult};
@ -162,7 +163,7 @@ impl MinerService for TestMinerService {
} }
/// Returns hashes of transactions currently in pending /// Returns hashes of transactions currently in pending
fn pending_transactions_hashes(&self) -> Vec<H256> { fn pending_transactions_hashes(&self, _best_block: BlockNumber) -> Vec<H256> {
vec![] vec![]
} }
@ -186,7 +187,7 @@ impl MinerService for TestMinerService {
Some(f(&open_block.close())) Some(f(&open_block.close()))
} }
fn transaction(&self, hash: &H256) -> Option<SignedTransaction> { fn transaction(&self, _best_block: BlockNumber, hash: &H256) -> Option<SignedTransaction> {
self.pending_transactions.lock().get(hash).cloned() self.pending_transactions.lock().get(hash).cloned()
} }
@ -194,13 +195,13 @@ impl MinerService for TestMinerService {
self.pending_transactions.lock().values().cloned().collect() self.pending_transactions.lock().values().cloned().collect()
} }
fn pending_transactions(&self) -> Vec<SignedTransaction> { fn pending_transactions(&self, _best_block: BlockNumber) -> Vec<SignedTransaction> {
self.pending_transactions.lock().values().cloned().collect() self.pending_transactions.lock().values().cloned().collect()
} }
fn pending_receipt(&self, hash: &H256) -> Option<RichReceipt> { fn pending_receipt(&self, _best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
// Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested. // Not much point implementing this since the logic is complex and the only thing it relies on is pending_receipts, which is already tested.
self.pending_receipts().get(hash).map(|r| self.pending_receipts(0).get(hash).map(|r|
RichReceipt { RichReceipt {
transaction_hash: Default::default(), transaction_hash: Default::default(),
transaction_index: Default::default(), transaction_index: Default::default(),
@ -212,7 +213,7 @@ impl MinerService for TestMinerService {
) )
} }
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> { fn pending_receipts(&self, _best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
self.pending_receipts.lock().clone() self.pending_receipts.lock().clone()
} }