@@ -75,21 +75,36 @@ where
7575 // calculate readiness based on state nonce + pooled txs from same sender
7676 let is_ready = |replace : & ReplaceTransaction < T > | {
7777 let mut nonce = state. account_nonce ( replace. sender ( ) ) ;
78+ let mut tx_same_nonce = None ;
7879 if let Some ( txs) = replace. pooled_by_sender {
7980 for tx in txs. iter ( ) {
8081 if nonce == tx. nonce ( ) && * tx. transaction != * * * replace. transaction {
81- nonce = nonce. saturating_add ( U256 :: from ( 1 ) )
82+ nonce = nonce. saturating_add ( U256 :: from ( 1 ) ) ;
83+ if tx. nonce ( ) == replace. nonce ( ) {
84+ tx_same_nonce = Some ( tx. clone ( ) ) ;
85+ }
8286 } else {
8387 break
8488 }
8589 }
8690 }
87- nonce == replace. nonce ( )
91+ ( nonce == replace. nonce ( ) , tx_same_nonce )
8892 } ;
8993
90- if !is_ready ( new) && is_ready ( old) {
91- // prevent a ready transaction being replace by a non-ready transaction
92- Choice :: RejectNew
94+ let ( new_is_ready, tx_same_nonce) = is_ready ( new) ;
95+ let ( old_is_ready, _) = is_ready ( old) ;
96+
97+ if !new_is_ready && old_is_ready {
98+ let new_is_same_nonce_and_better = tx_same_nonce
99+ . map_or ( false , |existing_tx| {
100+ new. priority ( ) . is_local ( ) && self . scoring . choose ( & existing_tx, & new) != Choice :: RejectNew
101+ } ) ;
102+ if new_is_same_nonce_and_better {
103+ Choice :: InsertNew
104+ } else {
105+ // prevent a ready transaction being replace by a non-ready transaction
106+ Choice :: RejectNew
107+ }
93108 } else {
94109 Choice :: ReplaceOld
95110 }
@@ -412,4 +427,46 @@ mod tests {
412427
413428 assert_eq ! ( replace. should_replace( & old, & new) , ReplaceOld ) ;
414429 }
430+
431+ #[ test]
432+ fn should_accept_local_tx_with_same_sender_and_nonce_with_better_gas_price ( ) {
433+ let scoring = NonceAndGasPrice ( PrioritizationStrategy :: GasPriceOnly ) ;
434+ let client = TestClient :: new ( ) . with_nonce ( 1 ) ;
435+ let replace = ReplaceByScoreAndReadiness :: new ( scoring, client) ;
436+
437+ // current transaction is ready
438+ let old_tx = {
439+ let tx = Tx {
440+ nonce : 1 ,
441+ gas_price : 1 ,
442+ ..Default :: default ( )
443+ } ;
444+ tx. signed ( ) . verified ( )
445+ } ;
446+
447+ let new_sender = Random . generate ( ) . unwrap ( ) ;
448+ let tx_new_ready_1 = local_tx_verified ( Tx {
449+ nonce : 1 ,
450+ gas_price : 1 ,
451+ ..Default :: default ( )
452+ } , & new_sender) ;
453+
454+ let tx_new_ready_2 = local_tx_verified ( Tx {
455+ nonce : 1 ,
456+ gas_price : 2 , // same nonce, higher gas price
457+ ..Default :: default ( )
458+ } , & new_sender) ;
459+
460+ let old_tx = txpool:: Transaction { insertion_id : 0 , transaction : Arc :: new ( old_tx) } ;
461+
462+ let new_tx = txpool:: Transaction { insertion_id : 0 , transaction : Arc :: new ( tx_new_ready_2) } ;
463+ let pooled_txs = [
464+ txpool:: Transaction { insertion_id : 0 , transaction : Arc :: new ( tx_new_ready_1) } ,
465+ ] ;
466+
467+ let old = ReplaceTransaction :: new ( & old_tx, None ) ;
468+ let new = ReplaceTransaction :: new ( & new_tx, Some ( & pooled_txs) ) ;
469+
470+ assert_eq ! ( replace. should_replace( & old, & new) , InsertNew ) ;
471+ }
415472}
0 commit comments