Merge branch 'master' into canon-cache-size
This commit is contained in:
commit
299ceb8092
@ -1079,7 +1079,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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
|
@ -47,14 +47,16 @@ pub type ApplyResult = Result<ApplyOutcome, Error>;
|
|||||||
/// Account modification state. Used to check if the account was
|
/// Account modification state. Used to check if the account was
|
||||||
/// Modified in between commits and overall.
|
/// Modified in between commits and overall.
|
||||||
enum AccountState {
|
enum AccountState {
|
||||||
/// Account was never modified in this state object.
|
/// Account was loaded from disk and never modified in this state object.
|
||||||
Clean,
|
CleanFresh,
|
||||||
|
/// Account was loaded from the global cache and never modified.
|
||||||
|
CleanCached,
|
||||||
/// Account has been modified and is not committed to the trie yet.
|
/// 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.
|
/// storage and code.
|
||||||
Dirty,
|
Dirty,
|
||||||
/// Account was modified and committed to the trie.
|
/// Account was modified and committed to the trie.
|
||||||
Commited,
|
Committed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -105,7 +107,15 @@ impl AccountEntry {
|
|||||||
fn new_clean(account: Option<Account>) -> AccountEntry {
|
fn new_clean(account: Option<Account>) -> AccountEntry {
|
||||||
AccountEntry {
|
AccountEntry {
|
||||||
account: account,
|
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();
|
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()) {
|
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 {
|
match a.account {
|
||||||
Some(ref mut account) => {
|
Some(ref mut account) => {
|
||||||
try!(trie.insert(address, &account.rlp()));
|
try!(trie.insert(address, &account.rlp()));
|
||||||
@ -526,7 +536,7 @@ impl State {
|
|||||||
fn commit_cache(&mut self) {
|
fn commit_cache(&mut self) {
|
||||||
let mut addresses = self.cache.borrow_mut();
|
let mut addresses = self.cache.borrow_mut();
|
||||||
trace!("Committing cache {:?} entries", addresses.len());
|
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);
|
self.db.cache_account(address, a.account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -638,10 +648,7 @@ impl State {
|
|||||||
Self::update_account_cache(require, account, accountdb.as_hashdb());
|
Self::update_account_cache(require, account, accountdb.as_hashdb());
|
||||||
}
|
}
|
||||||
let r = f(maybe_acc.as_ref());
|
let r = f(maybe_acc.as_ref());
|
||||||
match maybe_acc {
|
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
||||||
Some(account) => self.insert_cache(a, AccountEntry::new_clean(Some(account))),
|
|
||||||
None => self.insert_cache(a, AccountEntry::new_clean(None)),
|
|
||||||
}
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -660,8 +667,7 @@ impl State {
|
|||||||
let contains_key = self.cache.borrow().contains_key(a);
|
let contains_key = self.cache.borrow().contains_key(a);
|
||||||
if !contains_key {
|
if !contains_key {
|
||||||
match self.db.get_cached_account(a) {
|
match self.db.get_cached_account(a) {
|
||||||
Some(Some(acc)) => self.insert_cache(a, AccountEntry::new_clean(Some(acc))),
|
Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)),
|
||||||
Some(None) => self.insert_cache(a, AccountEntry::new_clean(None)),
|
|
||||||
None => {
|
None => {
|
||||||
let maybe_acc = if self.db.check_account_bloom(a) {
|
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);
|
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;
|
use log_entry::LogEntry;
|
||||||
|
|
||||||
/// Blockchain Filter.
|
/// Blockchain Filter.
|
||||||
#[derive(Binary)]
|
#[derive(Binary, Debug, PartialEq)]
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
/// Blockchain will be searched from this block.
|
/// Blockchain will be searched from this block.
|
||||||
pub from_block: BlockID,
|
pub from_block: BlockID,
|
||||||
|
@ -44,7 +44,8 @@ Account Options:
|
|||||||
ACCOUNTS is a comma-delimited list of addresses.
|
ACCOUNTS is a comma-delimited list of addresses.
|
||||||
Implies --no-signer. (default: {flag_unlock:?})
|
Implies --no-signer. (default: {flag_unlock:?})
|
||||||
--password FILE Provide a file containing a password for unlocking
|
--password FILE Provide a file containing a password for unlocking
|
||||||
an account. (default: {flag_password:?})
|
an account. Leading and trailing whitespace is trimmed.
|
||||||
|
(default: {flag_password:?})
|
||||||
--keys-iterations NUM Specify the number of iterations to use when
|
--keys-iterations NUM Specify the number of iterations to use when
|
||||||
deriving key from the password (bigger is more
|
deriving key from the password (bigger is more
|
||||||
secure) (default: {flag_keys_iterations}).
|
secure) (default: {flag_keys_iterations}).
|
||||||
|
@ -277,9 +277,10 @@ pub fn password_prompt() -> Result<String, String> {
|
|||||||
pub fn password_from_file<P>(path: P) -> Result<String, String> where P: AsRef<Path> {
|
pub fn password_from_file<P>(path: P) -> Result<String, String> where P: AsRef<Path> {
|
||||||
let mut file = try!(File::open(path).map_err(|_| "Unable to open password file."));
|
let mut file = try!(File::open(path).map_err(|_| "Unable to open password file."));
|
||||||
let mut file_content = String::new();
|
let mut file_content = String::new();
|
||||||
try!(file.read_to_string(&mut file_content).map_err(|_| "Unable to read password file."));
|
match file.read_to_string(&mut file_content) {
|
||||||
// remove eof
|
Ok(_) => Ok(file_content.trim().into()),
|
||||||
Ok((&file_content[..file_content.len() - 1]).to_owned())
|
Err(_) => Err("Unable to read password file.".into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads passwords from files. Treats each line as a separate password.
|
/// Reads passwords from files. Treats each line as a separate password.
|
||||||
@ -298,10 +299,13 @@ pub fn passwords_from_files(files: Vec<String>) -> Result<Vec<String>, String> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use devtools::RandomTempPath;
|
||||||
use util::{U256};
|
use util::{U256};
|
||||||
use ethcore::client::{Mode, BlockID};
|
use ethcore::client::{Mode, BlockID};
|
||||||
use ethcore::miner::PendingSet;
|
use ethcore::miner::PendingSet;
|
||||||
use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes};
|
use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes, password_from_file};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_duration() {
|
fn test_to_duration() {
|
||||||
@ -384,6 +388,14 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_password() {
|
||||||
|
let path = RandomTempPath::new();
|
||||||
|
let mut file = File::create(path.as_path()).unwrap();
|
||||||
|
file.write_all(b"a bc ").unwrap();
|
||||||
|
assert_eq!(password_from_file(path).unwrap().as_bytes(), b"a bc");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(feature = "dev", allow(float_cmp))]
|
#[cfg_attr(feature = "dev", allow(float_cmp))]
|
||||||
fn test_to_price() {
|
fn test_to_price() {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +85,14 @@ impl Into<EthFilter> for Filter {
|
|||||||
VariadicValue::Null => None,
|
VariadicValue::Null => None,
|
||||||
VariadicValue::Single(t) => Some(vec![t.into()]),
|
VariadicValue::Single(t) => Some(vec![t.into()]),
|
||||||
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
|
VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect())
|
||||||
}).filter_map(|m| m).collect()).into_iter();
|
}).collect()).into_iter();
|
||||||
vec![iter.next(), iter.next(), iter.next(), iter.next()]
|
|
||||||
|
vec![
|
||||||
|
iter.next().unwrap_or(None),
|
||||||
|
iter.next().unwrap_or(None),
|
||||||
|
iter.next().unwrap_or(None),
|
||||||
|
iter.next().unwrap_or(None)
|
||||||
|
]
|
||||||
},
|
},
|
||||||
limit: self.limit,
|
limit: self.limit,
|
||||||
}
|
}
|
||||||
@ -121,6 +127,8 @@ mod tests {
|
|||||||
use util::hash::*;
|
use util::hash::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use v1::types::BlockNumber;
|
use v1::types::BlockNumber;
|
||||||
|
use ethcore::filter::Filter as EthFilter;
|
||||||
|
use ethcore::client::BlockID;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn topic_deserialization() {
|
fn topic_deserialization() {
|
||||||
@ -148,4 +156,33 @@ mod tests {
|
|||||||
limit: None,
|
limit: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filter_conversion() {
|
||||||
|
let filter = Filter {
|
||||||
|
from_block: Some(BlockNumber::Earliest),
|
||||||
|
to_block: Some(BlockNumber::Latest),
|
||||||
|
address: Some(VariadicValue::Multiple(vec![])),
|
||||||
|
topics: Some(vec![
|
||||||
|
VariadicValue::Null,
|
||||||
|
VariadicValue::Single("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()),
|
||||||
|
VariadicValue::Null,
|
||||||
|
]),
|
||||||
|
limit: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let eth_filter: EthFilter = filter.into();
|
||||||
|
assert_eq!(eth_filter, EthFilter {
|
||||||
|
from_block: BlockID::Earliest,
|
||||||
|
to_block: BlockID::Latest,
|
||||||
|
address: Some(vec![]),
|
||||||
|
topics: vec![
|
||||||
|
None,
|
||||||
|
Some(vec!["000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()]),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
],
|
||||||
|
limit: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,7 +591,8 @@ impl Host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handshake_count(&self) -> usize {
|
fn handshake_count(&self) -> usize {
|
||||||
self.sessions.read().count() - self.session_count()
|
// session_count < total_count is possible because of the data race.
|
||||||
|
self.sessions.read().count().saturating_sub(self.session_count())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keep_alive(&self, io: &IoContext<NetworkIoMessage>) {
|
fn keep_alive(&self, io: &IoContext<NetworkIoMessage>) {
|
||||||
|
Loading…
Reference in New Issue
Block a user