Merge branch 'master' into canon-cache-size
This commit is contained in:
@@ -1079,7 +1079,7 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain.read().best_block_number())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -602,6 +602,6 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +493,21 @@ impl Miner {
|
||||
|
||||
/// Are we allowed to do a non-mandatory reseal?
|
||||
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;
|
||||
@@ -565,29 +580,35 @@ impl MinerService for Miner {
|
||||
}
|
||||
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_balance(address),
|
||||
|b| b.block().fields().state.balance(address)
|
||||
)
|
||||
}
|
||||
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| chain.latest_storage_at(address, position),
|
||||
|b| b.block().fields().state.storage_at(address, position)
|
||||
)
|
||||
}
|
||||
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> U256 {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_nonce(address), |b| b.block().fields().state.nonce(address))
|
||||
self.from_pending_block(
|
||||
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> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address).map(|c| (*c).clone()))
|
||||
self.from_pending_block(
|
||||
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) {
|
||||
@@ -737,50 +758,74 @@ impl MinerService for Miner {
|
||||
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 sw = self.sealing_work.lock();
|
||||
// TODO: should only use the sealing_work when it's current (it could be an old block)
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.top_transactions(),
|
||||
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().to_owned()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.top_transactions(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.top_transactions(),
|
||||
|sealing| sealing.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 sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.pending_hashes(),
|
||||
(_, sealing) => sealing.map_or_else(Vec::new, |s| s.transactions().iter().map(|t| t.hash()).collect()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.pending_hashes(),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.pending_hashes(),
|
||||
|sealing| sealing.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 sw = self.sealing_work.lock();
|
||||
let sealing_set = match sw.enabled {
|
||||
true => sw.queue.peek_last_ref(),
|
||||
false => None,
|
||||
};
|
||||
match (&self.options.pending_set, sealing_set) {
|
||||
(&PendingSet::AlwaysQueue, _) | (&PendingSet::SealingOrElseQueue, None) => queue.find(hash),
|
||||
(_, sealing) => sealing.and_then(|s| s.transactions().iter().find(|t| &t.hash() == hash).cloned()),
|
||||
match self.options.pending_set {
|
||||
PendingSet::AlwaysQueue => queue.find(hash),
|
||||
PendingSet::SealingOrElseQueue => {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| queue.find(hash),
|
||||
|sealing| sealing.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> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipt(&self, best_block: BlockNumber, hash: &H256) -> Option<RichReceipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| None,
|
||||
|pending| {
|
||||
let txs = pending.transactions();
|
||||
txs.iter()
|
||||
.map(|t| t.hash())
|
||||
@@ -801,15 +846,15 @@ impl MinerService for Miner {
|
||||
logs: receipt.logs.clone(),
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match (sealing_work.enabled, sealing_work.queue.peek_last_ref()) {
|
||||
(true, Some(pending)) => {
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt> {
|
||||
self.from_pending_block(
|
||||
best_block,
|
||||
|| BTreeMap::new(),
|
||||
|pending| {
|
||||
let hashes = pending.transactions()
|
||||
.iter()
|
||||
.map(|t| t.hash());
|
||||
@@ -817,9 +862,8 @@ impl MinerService for Miner {
|
||||
let receipts = pending.receipts().iter().cloned();
|
||||
|
||||
hashes.zip(receipts).collect()
|
||||
},
|
||||
_ => BTreeMap::new()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256> {
|
||||
@@ -1044,34 +1088,54 @@ mod tests {
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = transaction();
|
||||
let best_block = 0;
|
||||
// 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().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 1);
|
||||
assert_eq!(miner.pending_receipts().len(), 1);
|
||||
assert_eq!(miner.pending_transactions(best_block).len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).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)
|
||||
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]
|
||||
fn should_import_external_transaction() {
|
||||
// given
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
let transaction = transaction();
|
||||
let best_block = 0;
|
||||
// when
|
||||
let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||
assert_eq!(miner.all_transactions().len(), 1);
|
||||
assert_eq!(miner.pending_transactions_hashes().len(), 0);
|
||||
assert_eq!(miner.pending_transactions().len(), 0);
|
||||
assert_eq!(miner.pending_receipts().len(), 0);
|
||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
||||
assert_eq!(miner.pending_transactions(best_block).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)
|
||||
assert!(miner.prepare_work_sealing(&client));
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||
use block::ClosedBlock;
|
||||
use header::BlockNumber;
|
||||
use receipt::{RichReceipt, Receipt};
|
||||
use error::{Error, CallError};
|
||||
use transaction::SignedTransaction;
|
||||
@@ -115,7 +116,7 @@ pub trait MinerService : Send + Sync {
|
||||
Result<TransactionImportResult, Error>;
|
||||
|
||||
/// 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.
|
||||
fn clear_and_reset(&self, chain: &MiningBlockChainClient);
|
||||
@@ -135,19 +136,19 @@ pub trait MinerService : Send + Sync {
|
||||
where F: FnOnce(&ClosedBlock) -> T, Self: Sized;
|
||||
|
||||
/// 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.
|
||||
fn all_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// 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.
|
||||
fn pending_receipts(&self) -> BTreeMap<H256, Receipt>;
|
||||
fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap<H256, Receipt>;
|
||||
|
||||
/// 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.
|
||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||
|
||||
@@ -47,14 +47,16 @@ pub type ApplyResult = Result<ApplyOutcome, Error>;
|
||||
/// Account modification state. Used to check if the account was
|
||||
/// Modified in between commits and overall.
|
||||
enum AccountState {
|
||||
/// Account was never modified in this state object.
|
||||
Clean,
|
||||
/// Account was loaded from disk and never modified in this state object.
|
||||
CleanFresh,
|
||||
/// Account was loaded from the global cache and never modified.
|
||||
CleanCached,
|
||||
/// Account has been modified and is not committed to the trie yet.
|
||||
/// This is set than any of the account data is changed, including
|
||||
/// This is set if any of the account data is changed, including
|
||||
/// storage and code.
|
||||
Dirty,
|
||||
/// Account was modified and committed to the trie.
|
||||
Commited,
|
||||
Committed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -105,7 +107,15 @@ impl AccountEntry {
|
||||
fn new_clean(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::Clean,
|
||||
state: AccountState::CleanFresh,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account entry and mark it as clean and cached.
|
||||
fn new_clean_cached(account: Option<Account>) -> AccountEntry {
|
||||
AccountEntry {
|
||||
account: account,
|
||||
state: AccountState::CleanCached,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,7 +518,7 @@ impl State {
|
||||
{
|
||||
let mut trie = factories.trie.from_existing(db.as_hashdb_mut(), root).unwrap();
|
||||
for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) {
|
||||
a.state = AccountState::Commited;
|
||||
a.state = AccountState::Committed;
|
||||
match a.account {
|
||||
Some(ref mut account) => {
|
||||
try!(trie.insert(address, &account.rlp()));
|
||||
@@ -526,7 +536,7 @@ impl State {
|
||||
fn commit_cache(&mut self) {
|
||||
let mut addresses = self.cache.borrow_mut();
|
||||
trace!("Committing cache {:?} entries", addresses.len());
|
||||
for (address, a) in addresses.drain().filter(|&(_, ref a)| !a.is_dirty()) {
|
||||
for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) {
|
||||
self.db.cache_account(address, a.account);
|
||||
}
|
||||
}
|
||||
@@ -638,10 +648,7 @@ impl State {
|
||||
Self::update_account_cache(require, account, accountdb.as_hashdb());
|
||||
}
|
||||
let r = f(maybe_acc.as_ref());
|
||||
match maybe_acc {
|
||||
Some(account) => self.insert_cache(a, AccountEntry::new_clean(Some(account))),
|
||||
None => self.insert_cache(a, AccountEntry::new_clean(None)),
|
||||
}
|
||||
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
||||
r
|
||||
}
|
||||
}
|
||||
@@ -660,8 +667,7 @@ impl State {
|
||||
let contains_key = self.cache.borrow().contains_key(a);
|
||||
if !contains_key {
|
||||
match self.db.get_cached_account(a) {
|
||||
Some(Some(acc)) => self.insert_cache(a, AccountEntry::new_clean(Some(acc))),
|
||||
Some(None) => self.insert_cache(a, AccountEntry::new_clean(None)),
|
||||
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||
None => {
|
||||
let maybe_acc = if self.db.check_account_bloom(a) {
|
||||
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
|
||||
|
||||
@@ -22,7 +22,7 @@ use client::BlockID;
|
||||
use log_entry::LogEntry;
|
||||
|
||||
/// Blockchain Filter.
|
||||
#[derive(Binary)]
|
||||
#[derive(Binary, Debug, PartialEq)]
|
||||
pub struct Filter {
|
||||
/// Blockchain will be searched from this block.
|
||||
pub from_block: BlockID,
|
||||
|
||||
Reference in New Issue
Block a user