Even more detailed errors for transaction queue (#969)
* Even more detailed errors for transaction queue * Small rename * Removing macros in favour of functions+try!()
This commit is contained in:
parent
55051951f8
commit
caf4d179a2
@ -62,13 +62,18 @@ pub enum ExecutionError {
|
|||||||
Internal
|
Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
/// Errors concerning transaction processing.
|
/// Errors concerning transaction processing.
|
||||||
pub enum TransactionError {
|
pub enum TransactionError {
|
||||||
/// Transaction is already imported to the queue
|
/// Transaction is already imported to the queue
|
||||||
AlreadyImported,
|
AlreadyImported,
|
||||||
/// Transaction is not valid anymore (state already has higher nonce)
|
/// Transaction is not valid anymore (state already has higher nonce)
|
||||||
Old,
|
Old,
|
||||||
|
/// Transaction has too low fee
|
||||||
|
/// (there is already a transaction with the same sender-nonce but higher gas price)
|
||||||
|
TooCheapToReplace,
|
||||||
|
/// Transaction was not imported to the queue because limit has been reached.
|
||||||
|
LimitReached,
|
||||||
/// Transaction's gas price is below threshold.
|
/// Transaction's gas price is below threshold.
|
||||||
InsufficientGasPrice {
|
InsufficientGasPrice {
|
||||||
/// Minimal expected gas price
|
/// Minimal expected gas price
|
||||||
@ -153,7 +158,7 @@ pub enum BlockError {
|
|||||||
UnknownUncleParent(H256),
|
UnknownUncleParent(H256),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
/// Import to the block queue result
|
/// Import to the block queue result
|
||||||
pub enum ImportError {
|
pub enum ImportError {
|
||||||
/// Already in the block chain.
|
/// Already in the block chain.
|
||||||
|
@ -61,7 +61,7 @@ extern crate rayon;
|
|||||||
mod miner;
|
mod miner;
|
||||||
mod transaction_queue;
|
mod transaction_queue;
|
||||||
|
|
||||||
pub use transaction_queue::{TransactionQueue, AccountDetails};
|
pub use transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult};
|
||||||
pub use miner::{Miner};
|
pub use miner::{Miner};
|
||||||
|
|
||||||
use util::{H256, U256, Address, Bytes};
|
use util::{H256, U256, Address, Bytes};
|
||||||
@ -145,15 +145,6 @@ pub trait MinerService : Send + Sync {
|
|||||||
fn sensible_gas_limit(&self) -> U256 { x!(21000) }
|
fn sensible_gas_limit(&self) -> U256 { x!(21000) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the result of importing transaction.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum TransactionImportResult {
|
|
||||||
/// Transaction was imported to current queue.
|
|
||||||
Current,
|
|
||||||
/// Transaction was imported to future queue.
|
|
||||||
Future
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mining status
|
/// Mining status
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MinerStatus {
|
pub struct MinerStatus {
|
||||||
|
@ -86,14 +86,12 @@
|
|||||||
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::cmp::{Ordering};
|
use std::cmp::{Ordering};
|
||||||
use std::collections::{HashMap, BTreeSet};
|
use std::collections::{HashMap, HashSet, BTreeSet};
|
||||||
use util::numbers::{Uint, U256};
|
use util::numbers::{Uint, U256};
|
||||||
use util::hash::{Address, H256};
|
use util::hash::{Address, H256};
|
||||||
use util::table::*;
|
use util::table::*;
|
||||||
use ethcore::transaction::*;
|
use ethcore::transaction::*;
|
||||||
use ethcore::error::{Error, TransactionError};
|
use ethcore::error::{Error, TransactionError};
|
||||||
use super::TransactionImportResult;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/// Light structure used to identify transaction and it's order
|
/// Light structure used to identify transaction and it's order
|
||||||
@ -206,10 +204,11 @@ impl TransactionSet {
|
|||||||
/// Remove low priority transactions if there is more then specified by given `limit`.
|
/// Remove low priority transactions if there is more then specified by given `limit`.
|
||||||
///
|
///
|
||||||
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||||
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
/// Returns hashes of transactions removed because of limit.
|
||||||
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> Option<HashSet<H256>> {
|
||||||
let len = self.by_priority.len();
|
let len = self.by_priority.len();
|
||||||
if len <= self.limit {
|
if len <= self.limit {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_drop : Vec<(Address, U256)> = {
|
let to_drop : Vec<(Address, U256)> = {
|
||||||
@ -222,10 +221,17 @@ impl TransactionSet {
|
|||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
for (sender, nonce) in to_drop {
|
Some(to_drop
|
||||||
let order = self.drop(&sender, &nonce).expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
.into_iter()
|
||||||
by_hash.remove(&order.hash).expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
|
.map(|(sender, nonce)| {
|
||||||
}
|
let order = self.drop(&sender, &nonce)
|
||||||
|
.expect("Transaction has just been found in `by_priority`; so it is in `by_address` also.");
|
||||||
|
|
||||||
|
by_hash.remove(&order.hash)
|
||||||
|
.expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`");
|
||||||
|
order.hash
|
||||||
|
})
|
||||||
|
.collect::<HashSet<H256>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
||||||
@ -253,6 +259,15 @@ pub struct TransactionQueueStatus {
|
|||||||
pub future: usize,
|
pub future: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
/// Represents the result of importing transaction.
|
||||||
|
pub enum TransactionImportResult {
|
||||||
|
/// Transaction was imported to current queue.
|
||||||
|
Current,
|
||||||
|
/// Transaction was imported to future queue.
|
||||||
|
Future
|
||||||
|
}
|
||||||
|
|
||||||
/// Details of account
|
/// Details of account
|
||||||
pub struct AccountDetails {
|
pub struct AccountDetails {
|
||||||
/// Most recent account nonce
|
/// Most recent account nonce
|
||||||
@ -580,6 +595,7 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
|
let hash = tx.hash();
|
||||||
|
|
||||||
let next_nonce = self.last_nonces
|
let next_nonce = self.last_nonces
|
||||||
.get(&address)
|
.get(&address)
|
||||||
@ -589,8 +605,8 @@ impl TransactionQueue {
|
|||||||
// Check height
|
// Check height
|
||||||
if nonce > next_nonce {
|
if nonce > next_nonce {
|
||||||
// We have a gap - put to future
|
// We have a gap - put to future
|
||||||
Self::replace_transaction(tx, next_nonce, &mut self.future, &mut self.by_hash);
|
try!(check_too_cheap(Self::replace_transaction(tx, next_nonce, &mut self.future, &mut self.by_hash)));
|
||||||
self.future.enforce_limit(&mut self.by_hash);
|
try!(check_if_removed(&hash, self.future.enforce_limit(&mut self.by_hash)));
|
||||||
return Ok(TransactionImportResult::Future);
|
return Ok(TransactionImportResult::Future);
|
||||||
} else if nonce < state_nonce {
|
} else if nonce < state_nonce {
|
||||||
// Droping transaction
|
// Droping transaction
|
||||||
@ -598,7 +614,7 @@ impl TransactionQueue {
|
|||||||
return Err(TransactionError::Old);
|
return Err(TransactionError::Old);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash);
|
try!(check_too_cheap(Self::replace_transaction(tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
||||||
// Keep track of highest nonce stored in current
|
// Keep track of highest nonce stored in current
|
||||||
self.last_nonces.insert(address, nonce);
|
self.last_nonces.insert(address, nonce);
|
||||||
// Update nonces of transactions in future
|
// Update nonces of transactions in future
|
||||||
@ -610,10 +626,10 @@ impl TransactionQueue {
|
|||||||
if let Some(order) = self.future.drop(&address, &nonce) {
|
if let Some(order) = self.future.drop(&address, &nonce) {
|
||||||
// Let's insert that transaction to current (if it has higher gas_price)
|
// Let's insert that transaction to current (if it has higher gas_price)
|
||||||
let future_tx = self.by_hash.remove(&order.hash).unwrap();
|
let future_tx = self.by_hash.remove(&order.hash).unwrap();
|
||||||
Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash);
|
try!(check_too_cheap(Self::replace_transaction(future_tx, state_nonce, &mut self.current, &mut self.by_hash)));
|
||||||
}
|
}
|
||||||
// Also enforce the limit
|
// Also enforce the limit
|
||||||
self.current.enforce_limit(&mut self.by_hash);
|
try!(check_if_removed(&hash, self.current.enforce_limit(&mut self.by_hash)));
|
||||||
|
|
||||||
trace!(target: "miner", "status: {:?}", self.status());
|
trace!(target: "miner", "status: {:?}", self.status());
|
||||||
Ok(TransactionImportResult::Current)
|
Ok(TransactionImportResult::Current)
|
||||||
@ -623,13 +639,17 @@ impl TransactionQueue {
|
|||||||
///
|
///
|
||||||
/// If there is already transaction with same `(sender, nonce)` it will be replaced iff `gas_price` is higher.
|
/// If there is already transaction with same `(sender, nonce)` it will be replaced iff `gas_price` is higher.
|
||||||
/// One of the transactions is dropped from set and also removed from queue entirely (from `by_hash`).
|
/// One of the transactions is dropped from set and also removed from queue entirely (from `by_hash`).
|
||||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
///
|
||||||
|
/// Returns `true` if transaction actually got to the queue (`false` if there was already a transaction with higher
|
||||||
|
/// gas_price)
|
||||||
|
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) -> bool {
|
||||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
let address = tx.sender();
|
let address = tx.sender();
|
||||||
let nonce = tx.nonce();
|
let nonce = tx.nonce();
|
||||||
|
|
||||||
by_hash.insert(hash, tx);
|
by_hash.insert(hash, tx);
|
||||||
|
|
||||||
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
if let Some(old) = set.insert(address, nonce, order.clone()) {
|
||||||
// There was already transaction in queue. Let's check which one should stay
|
// There was already transaction in queue. Let's check which one should stay
|
||||||
let old_fee = old.gas_price;
|
let old_fee = old.gas_price;
|
||||||
@ -639,14 +659,35 @@ impl TransactionQueue {
|
|||||||
set.insert(address, nonce, old);
|
set.insert(address, nonce, old);
|
||||||
// and remove new one
|
// and remove new one
|
||||||
by_hash.remove(&hash);
|
by_hash.remove(&hash);
|
||||||
|
false
|
||||||
} else {
|
} else {
|
||||||
// Make sure we remove old transaction entirely
|
// Make sure we remove old transaction entirely
|
||||||
by_hash.remove(&old.hash);
|
by_hash.remove(&old.hash);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_too_cheap(is_in: bool) -> Result<(), TransactionError> {
|
||||||
|
if !is_in {
|
||||||
|
Err(TransactionError::TooCheapToReplace)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_if_removed(hash: &H256, dropped: Option<HashSet<H256>>) -> Result<(), TransactionError> {
|
||||||
|
match dropped {
|
||||||
|
Some(ref dropped) if dropped.contains(hash) => {
|
||||||
|
Err(TransactionError::LimitReached)
|
||||||
|
},
|
||||||
|
_ => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
@ -654,9 +695,17 @@ mod test {
|
|||||||
use util::table::*;
|
use util::table::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethcore::transaction::*;
|
use ethcore::transaction::*;
|
||||||
|
use ethcore::error::{Error, TransactionError};
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||||
|
|
||||||
|
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
|
||||||
|
match err.unwrap_err() {
|
||||||
|
Error::Transaction(e) => e,
|
||||||
|
_ => panic!("Expected transaction error!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
fn new_unsigned_tx(nonce: U256) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
action: Action::Create,
|
action: Action::Create,
|
||||||
@ -698,11 +747,16 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
fn new_txs(second_nonce: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
|
new_txs_with_gas_price_diff(second_nonce, U256::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_txs_with_gas_price_diff(second_nonce: U256, gas_price: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
let keypair = KeyPair::create().unwrap();
|
let keypair = KeyPair::create().unwrap();
|
||||||
let secret = &keypair.secret();
|
let secret = &keypair.secret();
|
||||||
let nonce = U256::from(123);
|
let nonce = U256::from(123);
|
||||||
let tx = new_unsigned_tx(nonce);
|
let tx = new_unsigned_tx(nonce);
|
||||||
let tx2 = new_unsigned_tx(nonce + second_nonce);
|
let mut tx2 = new_unsigned_tx(nonce + second_nonce);
|
||||||
|
tx2.gas_price = tx2.gas_price + gas_price;
|
||||||
|
|
||||||
(tx.sign(secret), tx2.sign(secret))
|
(tx.sign(secret), tx2.sign(secret))
|
||||||
}
|
}
|
||||||
@ -795,14 +849,14 @@ mod test {
|
|||||||
|
|
||||||
// First insert one transaction to future
|
// First insert one transaction to future
|
||||||
let res = txq.add(tx, &prev_nonce);
|
let res = txq.add(tx, &prev_nonce);
|
||||||
assert!(res.is_ok());
|
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
// now import second transaction to current
|
// now import second transaction to current
|
||||||
let res = txq.add(tx2.clone(), &default_nonce);
|
let res = txq.add(tx2.clone(), &default_nonce);
|
||||||
|
|
||||||
// and then there should be only one transaction in current (the one with higher gas_price)
|
// and then there should be only one transaction in current (the one with higher gas_price)
|
||||||
assert!(res.is_ok());
|
assert_eq!(unwrap_tx_err(res), TransactionError::TooCheapToReplace);
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
assert_eq!(txq.status().future, 0);
|
assert_eq!(txq.status().future, 0);
|
||||||
assert_eq!(txq.current.by_priority.len(), 1);
|
assert_eq!(txq.current.by_priority.len(), 1);
|
||||||
@ -821,7 +875,7 @@ mod test {
|
|||||||
let res = txq.add(tx, &default_nonce);
|
let res = txq.add(tx, &default_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(res.is_ok());
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
}
|
}
|
||||||
@ -845,12 +899,18 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::new();
|
let mut txq = TransactionQueue::new();
|
||||||
let tx = new_tx();
|
let tx = new_tx();
|
||||||
txq.set_gas_limit(tx.gas / U256::from(2));
|
let gas = tx.gas;
|
||||||
|
let limit = gas / U256::from(2);
|
||||||
|
txq.set_gas_limit(limit);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &default_nonce).unwrap_err();
|
let res = txq.add(tx, &default_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
||||||
|
limit: U256::from(55_000), // Should be 110% of set_gas_limit
|
||||||
|
got: gas,
|
||||||
|
});
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 0);
|
assert_eq!(stats.pending, 0);
|
||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
@ -868,9 +928,13 @@ mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &account).unwrap_err();
|
let res = txq.add(tx, &account);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance {
|
||||||
|
balance: U256::from(1),
|
||||||
|
cost: U256::from(100_100),
|
||||||
|
});
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 0);
|
assert_eq!(stats.pending, 0);
|
||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
@ -884,9 +948,13 @@ mod test {
|
|||||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &default_nonce).unwrap_err();
|
let res = txq.add(tx, &default_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: U256::from(2),
|
||||||
|
got: U256::from(1),
|
||||||
|
});
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 0);
|
assert_eq!(stats.pending, 0);
|
||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
@ -961,10 +1029,12 @@ mod test {
|
|||||||
let (tx, tx2) = new_txs(U256::from(2));
|
let (tx, tx2) = new_txs(U256::from(2));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx.clone(), &default_nonce).unwrap();
|
let res1 = txq.add(tx.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx2.clone(), &default_nonce).unwrap();
|
let res2 = txq.add(tx2.clone(), &default_nonce).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(res1, TransactionImportResult::Current);
|
||||||
|
assert_eq!(res2, TransactionImportResult::Future);
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 1);
|
assert_eq!(stats.pending, 1);
|
||||||
assert_eq!(stats.future, 1);
|
assert_eq!(stats.future, 1);
|
||||||
@ -1091,10 +1161,11 @@ mod test {
|
|||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx2.clone(), &default_nonce).unwrap();
|
let res = txq.add(tx2.clone(), &default_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let t = txq.top_transactions();
|
let t = txq.top_transactions();
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::LimitReached);
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
assert_eq!(t.len(), 1);
|
assert_eq!(t.len(), 1);
|
||||||
assert_eq!(t[0], tx);
|
assert_eq!(t[0], tx);
|
||||||
@ -1103,8 +1174,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_limit_future_transactions() {
|
fn should_limit_future_transactions() {
|
||||||
let mut txq = TransactionQueue::with_limits(10, 1);
|
let mut txq = TransactionQueue::with_limits(10, 1);
|
||||||
let (tx1, tx2) = new_txs(U256::from(4));
|
let (tx1, tx2) = new_txs_with_gas_price_diff(U256::from(4), U256::from(1));
|
||||||
let (tx3, tx4) = new_txs(U256::from(4));
|
let (tx3, tx4) = new_txs_with_gas_price_diff(U256::from(4), U256::from(2));
|
||||||
txq.add(tx1.clone(), &default_nonce).unwrap();
|
txq.add(tx1.clone(), &default_nonce).unwrap();
|
||||||
txq.add(tx3.clone(), &default_nonce).unwrap();
|
txq.add(tx3.clone(), &default_nonce).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
@ -1126,9 +1197,10 @@ mod test {
|
|||||||
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
let fetch_last_nonce = |_a: &Address| AccountDetails{ nonce: last_nonce, balance: !U256::zero() };
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, &fetch_last_nonce).unwrap_err();
|
let res = txq.add(tx, &fetch_last_nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::Old);
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 0);
|
assert_eq!(stats.pending, 0);
|
||||||
assert_eq!(stats.future, 0);
|
assert_eq!(stats.future, 0);
|
||||||
@ -1146,9 +1218,10 @@ mod test {
|
|||||||
assert_eq!(txq.status().pending, 0);
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx2.clone(), &nonce).unwrap_err();
|
let res = txq.add(tx2.clone(), &nonce);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported);
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.future, 1);
|
assert_eq!(stats.future, 1);
|
||||||
assert_eq!(stats.pending, 0);
|
assert_eq!(stats.pending, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user