@@ -30,6 +30,8 @@ import (
30
30
// transaction priority based on the value in the key/value pair.
31
31
type application struct {
32
32
* kvstore.Application
33
+
34
+ occupiedNonces map [string ][]uint64
33
35
}
34
36
35
37
type testTx struct {
@@ -38,6 +40,7 @@ type testTx struct {
38
40
}
39
41
40
42
func (app * application ) CheckTx (_ context.Context , req * abci.RequestCheckTx ) (* abci.ResponseCheckTxV2 , error ) {
43
+
41
44
var (
42
45
priority int64
43
46
sender string
@@ -58,7 +61,7 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*a
58
61
GasWanted : 1 ,
59
62
}}, nil
60
63
}
61
- nonce , err := strconv .ParseInt (string (parts [3 ]), 10 , 64 )
64
+ nonce , err := strconv .ParseUint (string (parts [3 ]), 10 , 64 )
62
65
if err != nil {
63
66
// could not parse
64
67
return & abci.ResponseCheckTxV2 {ResponseCheckTx : & abci.ResponseCheckTx {
@@ -67,15 +70,50 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*a
67
70
GasWanted : 1 ,
68
71
}}, nil
69
72
}
73
+ if app .occupiedNonces == nil {
74
+ app .occupiedNonces = make (map [string ][]uint64 )
75
+ }
76
+ if _ , exists := app .occupiedNonces [account ]; ! exists {
77
+ app .occupiedNonces [account ] = []uint64 {}
78
+ }
79
+ active := true
80
+ for i := uint64 (0 ); i < nonce ; i ++ {
81
+ found := false
82
+ for _ , occ := range app .occupiedNonces [account ] {
83
+ if occ == i {
84
+ found = true
85
+ break
86
+ }
87
+ }
88
+ if ! found {
89
+ active = false
90
+ break
91
+ }
92
+ }
93
+ app .occupiedNonces [account ] = append (app .occupiedNonces [account ], nonce )
70
94
return & abci.ResponseCheckTxV2 {
71
95
ResponseCheckTx : & abci.ResponseCheckTx {
72
96
Priority : v ,
73
97
Code : code .CodeTypeOK ,
74
98
GasWanted : 1 ,
75
99
},
76
- EVMNonce : uint64 (nonce ),
77
- EVMSenderAddress : account ,
78
- IsEVM : true ,
100
+ EVMNonce : nonce ,
101
+ EVMSenderAddress : account ,
102
+ IsEVM : true ,
103
+ IsPendingTransaction : ! active ,
104
+ Checker : func () abci.PendingTxCheckerResponse { return abci .Pending },
105
+ ExpireTxHandler : func () {
106
+ idx := - 1
107
+ for i , n := range app .occupiedNonces [account ] {
108
+ if n == nonce {
109
+ idx = i
110
+ break
111
+ }
112
+ }
113
+ if idx >= 0 {
114
+ app .occupiedNonces [account ] = append (app .occupiedNonces [account ][:idx ], app .occupiedNonces [account ][idx + 1 :]... )
115
+ }
116
+ },
79
117
}, nil
80
118
}
81
119
@@ -470,12 +508,14 @@ func TestTxMempool_Prioritization(t *testing.T) {
470
508
txs := [][]byte {
471
509
[]byte (fmt .Sprintf ("sender-0-1=peer=%d" , 9 )),
472
510
[]byte (fmt .Sprintf ("sender-1-1=peer=%d" , 8 )),
473
- []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 7 , 0 )),
474
- []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 9 , 1 )),
475
511
[]byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address2 , 6 , 0 )),
476
512
[]byte (fmt .Sprintf ("sender-2-1=peer=%d" , 5 )),
477
513
[]byte (fmt .Sprintf ("sender-3-1=peer=%d" , 4 )),
478
514
}
515
+ evmTxs := [][]byte {
516
+ []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 7 , 0 )),
517
+ []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 9 , 1 )),
518
+ }
479
519
480
520
// copy the slice of txs and shuffle the order randomly
481
521
txsCopy := make ([][]byte , len (txs ))
@@ -484,6 +524,16 @@ func TestTxMempool_Prioritization(t *testing.T) {
484
524
rng .Shuffle (len (txsCopy ), func (i , j int ) {
485
525
txsCopy [i ], txsCopy [j ] = txsCopy [j ], txsCopy [i ]
486
526
})
527
+ txs = [][]byte {
528
+ []byte (fmt .Sprintf ("sender-0-1=peer=%d" , 9 )),
529
+ []byte (fmt .Sprintf ("sender-1-1=peer=%d" , 8 )),
530
+ []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 7 , 0 )),
531
+ []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 9 , 1 )),
532
+ []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address2 , 6 , 0 )),
533
+ []byte (fmt .Sprintf ("sender-2-1=peer=%d" , 5 )),
534
+ []byte (fmt .Sprintf ("sender-3-1=peer=%d" , 4 )),
535
+ }
536
+ txsCopy = append (txsCopy , evmTxs ... )
487
537
488
538
for i := range txsCopy {
489
539
require .NoError (t , txmp .CheckTx (ctx , txsCopy [i ], nil , TxInfo {SenderID : peerID }))
@@ -504,6 +554,71 @@ func TestTxMempool_Prioritization(t *testing.T) {
504
554
}
505
555
}
506
556
557
+ func TestTxMempool_PendingStoreSize (t * testing.T ) {
558
+ ctx , cancel := context .WithCancel (context .Background ())
559
+ defer cancel ()
560
+
561
+ client := abciclient .NewLocalClient (log .NewNopLogger (), & application {Application : kvstore .NewApplication ()})
562
+ if err := client .Start (ctx ); err != nil {
563
+ t .Fatal (err )
564
+ }
565
+ t .Cleanup (client .Wait )
566
+
567
+ txmp := setup (t , client , 100 )
568
+ txmp .config .PendingSize = 1
569
+ peerID := uint16 (1 )
570
+
571
+ address1 := "0xeD23B3A9DE15e92B9ef9540E587B3661E15A12fA"
572
+
573
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 1 , 1 )), nil , TxInfo {SenderID : peerID }))
574
+ err := txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 1 , 2 )), nil , TxInfo {SenderID : peerID })
575
+ require .Error (t , err )
576
+ require .Contains (t , err .Error (), "mempool pending set is full" )
577
+ }
578
+
579
+ func TestTxMempool_EVMEviction (t * testing.T ) {
580
+ ctx , cancel := context .WithCancel (context .Background ())
581
+ defer cancel ()
582
+
583
+ client := abciclient .NewLocalClient (log .NewNopLogger (), & application {Application : kvstore .NewApplication ()})
584
+ if err := client .Start (ctx ); err != nil {
585
+ t .Fatal (err )
586
+ }
587
+ t .Cleanup (client .Wait )
588
+
589
+ txmp := setup (t , client , 100 )
590
+ txmp .config .Size = 1
591
+ peerID := uint16 (1 )
592
+
593
+ address1 := "0xeD23B3A9DE15e92B9ef9540E587B3661E15A12fA"
594
+ address2 := "0xfD23B3A9DE15e92B9ef9540E587B3661E15A12fA"
595
+
596
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 1 , 0 )), nil , TxInfo {SenderID : peerID }))
597
+ // this should evict the previous tx
598
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 2 , 0 )), nil , TxInfo {SenderID : peerID }))
599
+ require .Equal (t , 1 , txmp .priorityIndex .NumTxs ())
600
+ require .Equal (t , int64 (2 ), txmp .priorityIndex .txs [0 ].priority )
601
+
602
+ txmp .config .Size = 2
603
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address1 , 3 , 1 )), nil , TxInfo {SenderID : peerID }))
604
+ require .Equal (t , 0 , txmp .pendingTxs .Size ())
605
+ require .Equal (t , 2 , txmp .priorityIndex .NumTxs ())
606
+ // this would evict the tx with priority 2 and cause the tx with priority 3 to go pending
607
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address2 , 4 , 0 )), nil , TxInfo {SenderID : peerID }))
608
+ time .Sleep (1 * time .Second ) // reenqueue is async
609
+ require .Equal (t , 1 , txmp .priorityIndex .NumTxs ())
610
+ tx := txmp .priorityIndex .txs [0 ]
611
+ require .Equal (t , 1 , txmp .pendingTxs .Size ())
612
+
613
+ require .NoError (t , txmp .CheckTx (ctx , []byte (fmt .Sprintf ("evm-sender=%s=%d=%d" , address2 , 5 , 1 )), nil , TxInfo {SenderID : peerID }))
614
+ require .Equal (t , 2 , txmp .priorityIndex .NumTxs ())
615
+ txmp .removeTx (tx , true , false )
616
+ // should not reenqueue
617
+ require .Equal (t , 1 , txmp .priorityIndex .NumTxs ())
618
+ time .Sleep (1 * time .Second ) // pendingTxs should still be one even after sleeping for a sec
619
+ require .Equal (t , 1 , txmp .pendingTxs .Size ())
620
+ }
621
+
507
622
func TestTxMempool_CheckTxSamePeer (t * testing.T ) {
508
623
ctx , cancel := context .WithCancel (context .Background ())
509
624
defer cancel ()
0 commit comments