v2.5.8-stable (#11041)
* add more tx tests (#11038) * Fix parallel transactions race-condition (#10995) * Add blake2_f precompile (#11017) * [trace] introduce trace failed to Ext (#11019) * Edit publish-onchain.sh to use https (#11016) * Fix deadlock in network-devp2p (#11013) * EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) * xDai chain support and nodes list update (#10989) * EIP 2028: transaction gas lowered from 68 to 16 (#10987) * EIP-1344 Add CHAINID op-code (#10983) * manual publish jobs for releases, no changes for nightlies (#10977) * [blooms-db] Fix benchmarks (#10974) * Verify transaction against its block during import (#10954) * Better error message for rpc gas price errors (#10931) * tx-pool: accept local tx with higher gas price when pool full (#10901) * Fix fork choice (#10837) * Cleanup unused vm dependencies (#10787) * Fix compilation on recent nightlies (#10991)
This commit is contained in:
@@ -587,7 +587,7 @@ fn convert_error<H: fmt::Debug + fmt::LowerHex>(err: txpool::Error<H>) -> transa
|
||||
match err {
|
||||
Error::AlreadyImported(..) => transaction::Error::AlreadyImported,
|
||||
Error::TooCheapToEnter(..) => transaction::Error::LimitReached,
|
||||
Error::TooCheapToReplace(..) => transaction::Error::TooCheapToReplace
|
||||
Error::TooCheapToReplace(..) => transaction::Error::TooCheapToReplace { prev: None, new: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,20 @@ where
|
||||
let old_score = (old.priority(), old.gas_price());
|
||||
let new_score = (new.priority(), new.gas_price());
|
||||
if new_score > old_score {
|
||||
// Check if this is a replacement transaction.
|
||||
//
|
||||
// With replacement transactions we can safely return `InsertNew` here, because
|
||||
// we don't need to remove `old` (worst transaction in the pool) since `new` will replace
|
||||
// some other transaction in the pool so we will never go above limit anyway.
|
||||
if let Some(txs) = new.pooled_by_sender {
|
||||
if let Ok(index) = txs.binary_search_by(|old| self.scoring.compare(old, new)) {
|
||||
return match self.scoring.choose(&txs[index], new) {
|
||||
Choice::ReplaceOld => Choice::InsertNew,
|
||||
choice => choice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = &self.client;
|
||||
// calculate readiness based on state nonce + pooled txs from same sender
|
||||
let is_ready = |replace: &ReplaceTransaction<T>| {
|
||||
@@ -412,4 +426,88 @@ mod tests {
|
||||
|
||||
assert_eq!(replace.should_replace(&old, &new), ReplaceOld);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_accept_local_tx_with_same_sender_and_nonce_with_better_gas_price() {
|
||||
let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly);
|
||||
let client = TestClient::new().with_nonce(1);
|
||||
let replace = ReplaceByScoreAndReadiness::new(scoring, client);
|
||||
|
||||
// current transaction is ready
|
||||
let old_tx = {
|
||||
let tx = Tx {
|
||||
nonce: 1,
|
||||
gas_price: 1,
|
||||
..Default::default()
|
||||
};
|
||||
tx.signed().verified()
|
||||
};
|
||||
|
||||
let new_sender = Random.generate().unwrap();
|
||||
let tx_new_ready_1 = local_tx_verified(Tx {
|
||||
nonce: 1,
|
||||
gas_price: 1,
|
||||
..Default::default()
|
||||
}, &new_sender);
|
||||
|
||||
let tx_new_ready_2 = local_tx_verified(Tx {
|
||||
nonce: 1,
|
||||
gas_price: 2, // same nonce, higher gas price
|
||||
..Default::default()
|
||||
}, &new_sender);
|
||||
|
||||
let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(old_tx) };
|
||||
|
||||
let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_2) };
|
||||
let pooled_txs = [
|
||||
txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_1) },
|
||||
];
|
||||
|
||||
let old = ReplaceTransaction::new(&old_tx, None);
|
||||
let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs));
|
||||
|
||||
assert_eq!(replace.should_replace(&old, &new), InsertNew);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reject_local_tx_with_same_sender_and_nonce_with_worse_gas_price() {
|
||||
let scoring = NonceAndGasPrice(PrioritizationStrategy::GasPriceOnly);
|
||||
let client = TestClient::new().with_nonce(1);
|
||||
let replace = ReplaceByScoreAndReadiness::new(scoring, client);
|
||||
|
||||
// current transaction is ready
|
||||
let old_tx = {
|
||||
let tx = Tx {
|
||||
nonce: 1,
|
||||
gas_price: 1,
|
||||
..Default::default()
|
||||
};
|
||||
tx.signed().verified()
|
||||
};
|
||||
|
||||
let new_sender = Random.generate().unwrap();
|
||||
let tx_new_ready_1 = local_tx_verified(Tx {
|
||||
nonce: 1,
|
||||
gas_price: 2,
|
||||
..Default::default()
|
||||
}, &new_sender);
|
||||
|
||||
let tx_new_ready_2 = local_tx_verified(Tx {
|
||||
nonce: 1,
|
||||
gas_price: 1, // same nonce, lower gas price
|
||||
..Default::default()
|
||||
}, &new_sender);
|
||||
|
||||
let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(old_tx) };
|
||||
|
||||
let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_2) };
|
||||
let pooled_txs = [
|
||||
txpool::Transaction { insertion_id: 0, transaction: Arc::new(tx_new_ready_1) },
|
||||
];
|
||||
|
||||
let old = ReplaceTransaction::new(&old_tx, None);
|
||||
let new = ReplaceTransaction::new(&new_tx, Some(&pooled_txs));
|
||||
|
||||
assert_eq!(replace.should_replace(&old, &new), RejectNew);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
// then
|
||||
assert_eq!(res, vec![Ok(()), Ok(())]);
|
||||
assert_eq!(res2, vec![
|
||||
// The error here indicates reaching the limit
|
||||
// and minimal effective gas price taken into account.
|
||||
Err(transaction::Error::InsufficientGasPrice { minimal: 2.into(), got: 1.into() }),
|
||||
Ok(())
|
||||
// The error here indicates reaching the limit
|
||||
// and minimal effective gas price taken into account.
|
||||
Err(transaction::Error::TooCheapToReplace { prev: Some(2.into()), new: Some(1.into()) }),
|
||||
Ok(())
|
||||
]);
|
||||
assert_eq!(txq.status().status.transaction_count, 3);
|
||||
// tx2 transaction got dropped because of limit
|
||||
@@ -585,7 +585,7 @@ fn should_not_replace_same_transaction_if_the_fee_is_less_than_minimal_bump() {
|
||||
let res = txq.import(client.clone(), vec![tx2, tx4].local());
|
||||
|
||||
// then
|
||||
assert_eq!(res, vec![Err(transaction::Error::TooCheapToReplace), Ok(())]);
|
||||
assert_eq!(res, vec![Err(transaction::Error::TooCheapToReplace { prev: None, new: None }), Ok(())]);
|
||||
assert_eq!(txq.status().status.transaction_count, 2);
|
||||
assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0].signed().gas_price, U256::from(20));
|
||||
assert_eq!(txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1].signed().gas_price, U256::from(2));
|
||||
@@ -1027,9 +1027,9 @@ fn should_reject_early_in_case_gas_price_is_less_than_min_effective() {
|
||||
let client = TestClient::new();
|
||||
let tx1 = Tx::default().signed().unverified();
|
||||
let res = txq.import(client.clone(), vec![tx1]);
|
||||
assert_eq!(res, vec![Err(transaction::Error::InsufficientGasPrice {
|
||||
minimal: 2.into(),
|
||||
got: 1.into(),
|
||||
assert_eq!(res, vec![Err(transaction::Error::TooCheapToReplace {
|
||||
prev: Some(2.into()),
|
||||
new: Some(1.into()),
|
||||
})]);
|
||||
assert!(!client.was_verification_triggered());
|
||||
|
||||
|
||||
@@ -231,9 +231,9 @@ impl<C: Client> txpool::Verifier<Transaction> for Verifier<C, ::pool::scoring::N
|
||||
tx.gas_price(),
|
||||
vtx.transaction.gas_price,
|
||||
);
|
||||
bail!(transaction::Error::InsufficientGasPrice {
|
||||
minimal: vtx.transaction.gas_price,
|
||||
got: *tx.gas_price(),
|
||||
return Err(transaction::Error::TooCheapToReplace {
|
||||
prev: Some(vtx.transaction.gas_price),
|
||||
new: Some(*tx.gas_price()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user