2020-01-15 07:52:59 +00:00
package mempool
2019-10-24 09:29:55 +00:00
import (
2023-05-31 16:22:04 +00:00
"fmt"
2020-07-09 09:57:24 +00:00
"math/big"
2024-08-26 17:28:35 +00:00
"slices"
2023-05-31 16:22:04 +00:00
"strings"
2019-10-24 09:29:55 +00:00
"testing"
2020-11-11 12:49:51 +00:00
"time"
2019-10-24 09:29:55 +00:00
2021-11-30 15:42:22 +00:00
"github.com/holiman/uint256"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
2020-11-27 10:55:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/network/payload"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2020-06-05 09:17:16 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
2019-10-24 09:29:55 +00:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type FeerStub struct {
2020-11-11 12:49:51 +00:00
feePerByte int64
p2pSigExt bool
blockHeight uint32
2020-11-27 10:55:48 +00:00
balance int64
2019-10-24 09:29:55 +00:00
}
2020-12-11 12:22:49 +00:00
func ( fs * FeerStub ) GetBaseExecFee ( ) int64 {
return 30
}
2020-06-23 14:15:35 +00:00
func ( fs * FeerStub ) FeePerByte ( ) int64 {
2020-05-08 17:54:24 +00:00
return fs . feePerByte
2019-10-24 09:29:55 +00:00
}
2020-09-09 12:32:31 +00:00
func ( fs * FeerStub ) BlockHeight ( ) uint32 {
2020-11-11 12:49:51 +00:00
return fs . blockHeight
2020-09-09 12:32:31 +00:00
}
2020-07-09 09:57:24 +00:00
func ( fs * FeerStub ) GetUtilityTokenBalance ( uint160 util . Uint160 ) * big . Int {
2020-11-27 10:55:48 +00:00
return big . NewInt ( fs . balance )
2020-05-18 08:20:41 +00:00
}
2019-10-24 09:29:55 +00:00
func testMemPoolAddRemoveWithFeer ( t * testing . T , fs Feer ) {
2023-04-13 11:03:02 +00:00
mp := New ( 10 , 0 , false , nil )
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = 0
2020-07-29 16:57:38 +00:00
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { 1 , 2 , 3 } } }
2020-06-05 16:01:10 +00:00
_ , ok := mp . TryGetValue ( tx . Hash ( ) )
2019-10-24 09:29:55 +00:00
require . Equal ( t , false , ok )
2020-02-05 11:24:36 +00:00
require . NoError ( t , mp . Add ( tx , fs ) )
2019-10-24 09:29:55 +00:00
// Re-adding should fail.
2020-02-05 11:24:36 +00:00
require . Error ( t , mp . Add ( tx , fs ) )
2020-06-05 16:01:10 +00:00
tx2 , ok := mp . TryGetValue ( tx . Hash ( ) )
2019-10-24 09:29:55 +00:00
require . Equal ( t , true , ok )
require . Equal ( t , tx , tx2 )
2024-07-30 14:36:57 +00:00
mp . Remove ( tx . Hash ( ) )
2020-06-05 16:01:10 +00:00
_ , ok = mp . TryGetValue ( tx . Hash ( ) )
2019-10-24 09:29:55 +00:00
require . Equal ( t , false , ok )
// Make sure nothing left in the mempool after removal.
2020-02-05 14:13:35 +00:00
assert . Equal ( t , 0 , len ( mp . verifiedMap ) )
assert . Equal ( t , 0 , len ( mp . verifiedTxes ) )
2019-10-24 09:29:55 +00:00
}
2020-11-11 12:49:51 +00:00
func TestMemPoolRemoveStale ( t * testing . T ) {
2023-04-13 11:03:02 +00:00
mp := New ( 5 , 0 , false , nil )
2020-11-11 12:49:51 +00:00
txs := make ( [ ] * transaction . Transaction , 5 )
for i := range txs {
2021-03-25 16:18:01 +00:00
txs [ i ] = transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-11-11 12:49:51 +00:00
txs [ i ] . Nonce = uint32 ( i )
txs [ i ] . Signers = [ ] transaction . Signer { { Account : util . Uint160 { 1 , 2 , 3 } } }
require . NoError ( t , mp . Add ( txs [ i ] , & FeerStub { blockHeight : uint32 ( i ) } ) )
}
staleTxs := make ( chan * transaction . Transaction , 5 )
2023-04-03 10:34:24 +00:00
f := func ( tx * transaction . Transaction , _ any ) {
2020-11-11 12:49:51 +00:00
staleTxs <- tx
}
mp . SetResendThreshold ( 5 , f )
isValid := func ( tx * transaction . Transaction ) bool {
return tx . Nonce % 2 == 0
}
mp . RemoveStale ( isValid , & FeerStub { blockHeight : 5 } ) // 0 + 5
require . Eventually ( t , func ( ) bool { return len ( staleTxs ) == 1 } , time . Second , time . Millisecond * 100 )
require . Equal ( t , txs [ 0 ] , <- staleTxs )
mp . RemoveStale ( isValid , & FeerStub { blockHeight : 7 } ) // 2 + 5
require . Eventually ( t , func ( ) bool { return len ( staleTxs ) == 1 } , time . Second , time . Millisecond * 100 )
require . Equal ( t , txs [ 2 ] , <- staleTxs )
mp . RemoveStale ( isValid , & FeerStub { blockHeight : 10 } ) // 0 + 2 * 5
require . Eventually ( t , func ( ) bool { return len ( staleTxs ) == 1 } , time . Second , time . Millisecond * 100 )
require . Equal ( t , txs [ 0 ] , <- staleTxs )
mp . RemoveStale ( isValid , & FeerStub { blockHeight : 15 } ) // 0 + 3 * 5
// tx[2] should appear, so it is also checked that tx[0] wasn't sent on height 15.
mp . RemoveStale ( isValid , & FeerStub { blockHeight : 22 } ) // 2 + 4 * 5
require . Eventually ( t , func ( ) bool { return len ( staleTxs ) == 1 } , time . Second , time . Millisecond * 100 )
require . Equal ( t , txs [ 2 ] , <- staleTxs )
// panic if something is sent after this.
close ( staleTxs )
require . Len ( t , staleTxs , 0 )
}
2019-10-24 09:29:55 +00:00
func TestMemPoolAddRemove ( t * testing . T ) {
2020-06-18 19:32:29 +00:00
var fs = & FeerStub { }
testMemPoolAddRemoveWithFeer ( t , fs )
2019-10-24 09:29:55 +00:00
}
2019-12-13 21:03:04 +00:00
2020-02-06 14:20:38 +00:00
func TestOverCapacity ( t * testing . T ) {
2020-11-27 10:55:48 +00:00
var fs = & FeerStub { balance : 10000000 }
2024-07-30 15:11:05 +00:00
var acc = util . Uint160 { 1 , 2 , 3 }
2020-02-06 14:20:38 +00:00
const mempoolSize = 10
2023-04-13 11:03:02 +00:00
mp := New ( mempoolSize , 0 , false , nil )
2020-02-06 14:20:38 +00:00
2024-08-26 17:28:35 +00:00
var checkPoolIsSorted = func ( ) {
2024-08-26 17:42:09 +00:00
require . True ( t , slices . IsSortedFunc ( mp . verifiedTxes , func ( a , b item ) int { return - a . Compare ( b ) } ) )
2024-08-26 17:28:35 +00:00
}
2024-08-30 18:41:02 +00:00
for i := range mempoolSize {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = uint32 ( i )
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
require . NoError ( t , mp . Add ( tx , fs ) )
}
txcnt := uint32 ( mempoolSize )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2024-07-30 15:11:05 +00:00
require . Equal ( t , * uint256 . NewInt ( 0 ) , mp . fees [ acc ] . feeSum )
2020-02-06 14:20:38 +00:00
2020-08-11 17:56:39 +00:00
bigScript := make ( [ ] byte , 64 )
bigScript [ 0 ] = byte ( opcode . PUSH1 )
bigScript [ 1 ] = byte ( opcode . RET )
2020-02-06 14:20:38 +00:00
// Fees are also prioritized.
2024-08-30 18:41:02 +00:00
for range mempoolSize {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( bigScript , 0 )
2020-06-23 14:15:35 +00:00
tx . NetworkFee = 10000
2020-04-22 17:42:38 +00:00
tx . Nonce = txcnt
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
txcnt ++
2020-08-11 17:56:39 +00:00
// size is ~90, networkFee is 10000 => feePerByte is 119
2020-02-06 14:20:38 +00:00
require . NoError ( t , mp . Add ( tx , fs ) )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2020-02-06 14:20:38 +00:00
}
2024-07-30 15:11:05 +00:00
require . Equal ( t , * uint256 . NewInt ( 10 * 10000 ) , mp . fees [ acc ] . feeSum )
2020-02-06 14:20:38 +00:00
// Less prioritized txes are not allowed anymore.
2021-03-25 16:18:01 +00:00
tx := transaction . New ( bigScript , 0 )
2020-06-23 14:15:35 +00:00
tx . NetworkFee = 100
2020-04-22 17:42:38 +00:00
tx . Nonce = txcnt
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
txcnt ++
require . Error ( t , mp . Add ( tx , fs ) )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2020-12-16 10:14:30 +00:00
require . Equal ( t , mempoolSize , len ( mp . verifiedMap ) )
require . Equal ( t , mempoolSize , len ( mp . verifiedTxes ) )
require . False ( t , mp . containsKey ( tx . Hash ( ) ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2024-07-30 15:11:05 +00:00
require . Equal ( t , * uint256 . NewInt ( 100000 ) , mp . fees [ acc ] . feeSum )
2020-02-06 14:20:38 +00:00
// Low net fee, but higher per-byte fee is still a better combination.
2021-03-25 16:18:01 +00:00
tx = transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = txcnt
2020-06-23 14:15:35 +00:00
tx . NetworkFee = 7000
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
txcnt ++
2020-08-11 17:56:39 +00:00
// size is ~51 (small script), networkFee is 7000 (<10000)
2020-06-23 14:15:35 +00:00
// => feePerByte is 137 (>119)
2020-02-06 14:20:38 +00:00
require . NoError ( t , mp . Add ( tx , fs ) )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2024-07-30 15:11:05 +00:00
require . Equal ( t , * uint256 . NewInt ( 9 * 10000 + 7000 ) , mp . fees [ acc ] . feeSum )
2020-02-06 14:20:38 +00:00
// High priority always wins over low priority.
2024-08-30 18:41:02 +00:00
for range mempoolSize {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-06-23 14:15:35 +00:00
tx . NetworkFee = 8000
2020-04-22 17:42:38 +00:00
tx . Nonce = txcnt
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
txcnt ++
require . NoError ( t , mp . Add ( tx , fs ) )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2020-02-06 14:20:38 +00:00
}
2024-07-30 15:11:05 +00:00
require . Equal ( t , * uint256 . NewInt ( 10 * 8000 ) , mp . fees [ acc ] . feeSum )
2020-02-06 14:20:38 +00:00
// Good luck with low priority now.
2021-03-25 16:18:01 +00:00
tx = transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = txcnt
2020-06-23 14:15:35 +00:00
tx . NetworkFee = 7000
2024-07-30 15:11:05 +00:00
tx . Signers = [ ] transaction . Signer { { Account : acc } }
2020-02-06 14:20:38 +00:00
require . Error ( t , mp . Add ( tx , fs ) )
require . Equal ( t , mempoolSize , mp . Count ( ) )
2024-08-26 17:28:35 +00:00
checkPoolIsSorted ( )
2020-02-06 14:20:38 +00:00
}
func TestGetVerified ( t * testing . T ) {
2020-06-18 19:32:29 +00:00
var fs = & FeerStub { }
2020-02-06 14:20:38 +00:00
const mempoolSize = 10
2023-04-13 11:03:02 +00:00
mp := New ( mempoolSize , 0 , false , nil )
2020-02-06 14:20:38 +00:00
txes := make ( [ ] * transaction . Transaction , 0 , mempoolSize )
2024-08-30 18:41:02 +00:00
for i := range mempoolSize {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = uint32 ( i )
2020-07-29 16:57:38 +00:00
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { 1 , 2 , 3 } } }
2020-02-06 14:20:38 +00:00
txes = append ( txes , tx )
require . NoError ( t , mp . Add ( tx , fs ) )
}
require . Equal ( t , mempoolSize , mp . Count ( ) )
verTxes := mp . GetVerifiedTransactions ( )
require . Equal ( t , mempoolSize , len ( verTxes ) )
2020-06-05 16:01:10 +00:00
require . ElementsMatch ( t , txes , verTxes )
2020-02-06 14:20:38 +00:00
for _ , tx := range txes {
2024-07-30 14:36:57 +00:00
mp . Remove ( tx . Hash ( ) )
2020-02-06 14:20:38 +00:00
}
verTxes = mp . GetVerifiedTransactions ( )
require . Equal ( t , 0 , len ( verTxes ) )
}
func TestRemoveStale ( t * testing . T ) {
2020-06-18 19:32:29 +00:00
var fs = & FeerStub { }
2020-02-06 14:20:38 +00:00
const mempoolSize = 10
2023-04-13 11:03:02 +00:00
mp := New ( mempoolSize , 0 , false , nil )
2020-02-06 14:20:38 +00:00
txes1 := make ( [ ] * transaction . Transaction , 0 , mempoolSize / 2 )
txes2 := make ( [ ] * transaction . Transaction , 0 , mempoolSize / 2 )
2024-08-30 18:41:02 +00:00
for i := range mempoolSize {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-22 17:42:38 +00:00
tx . Nonce = uint32 ( i )
2020-07-29 16:57:38 +00:00
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { 1 , 2 , 3 } } }
2020-02-06 14:20:38 +00:00
if i % 2 == 0 {
txes1 = append ( txes1 , tx )
} else {
txes2 = append ( txes2 , tx )
}
require . NoError ( t , mp . Add ( tx , fs ) )
}
require . Equal ( t , mempoolSize , mp . Count ( ) )
mp . RemoveStale ( func ( t * transaction . Transaction ) bool {
for _ , tx := range txes2 {
if tx == t {
return true
}
}
return false
2020-05-18 08:20:41 +00:00
} , & FeerStub { } )
2020-02-06 14:20:38 +00:00
require . Equal ( t , mempoolSize / 2 , mp . Count ( ) )
verTxes := mp . GetVerifiedTransactions ( )
2020-02-18 15:56:41 +00:00
for _ , txf := range verTxes {
2020-06-05 16:01:10 +00:00
require . NotContains ( t , txes1 , txf )
require . Contains ( t , txes2 , txf )
2020-01-15 07:52:59 +00:00
}
}
2020-05-18 08:20:41 +00:00
func TestMemPoolFees ( t * testing . T ) {
2023-04-13 11:03:02 +00:00
mp := New ( 10 , 0 , false , nil )
2020-11-27 10:55:48 +00:00
fs := & FeerStub { balance : 10000000 }
2020-05-18 08:20:41 +00:00
sender0 := util . Uint160 { 1 , 2 , 3 }
2021-03-25 16:18:01 +00:00
tx0 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-11-27 10:55:48 +00:00
tx0 . NetworkFee = fs . balance + 1
2020-07-29 16:57:38 +00:00
tx0 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-07-09 16:23:41 +00:00
// insufficient funds to add transaction, and balance shouldn't be stored
2020-11-27 10:55:48 +00:00
require . Equal ( t , false , mp . Verify ( tx0 , fs ) )
require . Error ( t , mp . Add ( tx0 , fs ) )
2020-07-09 16:23:41 +00:00
require . Equal ( t , 0 , len ( mp . fees ) )
2020-05-18 08:20:41 +00:00
2020-11-27 10:55:48 +00:00
balancePart := new ( big . Int ) . Div ( big . NewInt ( fs . balance ) , big . NewInt ( 4 ) )
2020-05-18 08:20:41 +00:00
// no problems with adding another transaction with lower fee
2021-03-25 16:18:01 +00:00
tx1 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-07-09 09:57:24 +00:00
tx1 . NetworkFee = balancePart . Int64 ( )
2020-07-29 16:57:38 +00:00
tx1 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-11-27 10:55:48 +00:00
require . NoError ( t , mp . Add ( tx1 , fs ) )
2020-05-18 08:20:41 +00:00
require . Equal ( t , 1 , len ( mp . fees ) )
require . Equal ( t , utilityBalanceAndFees {
2021-11-30 15:42:22 +00:00
balance : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
feeSum : * uint256 . NewInt ( uint64 ( tx1 . NetworkFee ) ) ,
2020-05-18 08:20:41 +00:00
} , mp . fees [ sender0 ] )
// balance shouldn't change after adding one more transaction
2021-03-25 16:18:01 +00:00
tx2 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-11-27 10:55:48 +00:00
tx2 . NetworkFee = new ( big . Int ) . Sub ( big . NewInt ( fs . balance ) , balancePart ) . Int64 ( )
2020-07-29 16:57:38 +00:00
tx2 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-11-27 10:55:48 +00:00
require . NoError ( t , mp . Add ( tx2 , fs ) )
2020-05-18 08:20:41 +00:00
require . Equal ( t , 2 , len ( mp . verifiedTxes ) )
require . Equal ( t , 1 , len ( mp . fees ) )
require . Equal ( t , utilityBalanceAndFees {
2021-11-30 15:42:22 +00:00
balance : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
feeSum : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
2020-05-18 08:20:41 +00:00
} , mp . fees [ sender0 ] )
// can't add more transactions as we don't have enough GAS
2021-03-25 16:18:01 +00:00
tx3 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-06-23 14:15:35 +00:00
tx3 . NetworkFee = 1
2020-07-29 16:57:38 +00:00
tx3 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-11-27 10:55:48 +00:00
require . Equal ( t , false , mp . Verify ( tx3 , fs ) )
require . Error ( t , mp . Add ( tx3 , fs ) )
2020-05-18 08:20:41 +00:00
require . Equal ( t , 1 , len ( mp . fees ) )
require . Equal ( t , utilityBalanceAndFees {
2021-11-30 15:42:22 +00:00
balance : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
feeSum : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
2020-05-18 08:20:41 +00:00
} , mp . fees [ sender0 ] )
// check whether sender's fee updates correctly
mp . RemoveStale ( func ( t * transaction . Transaction ) bool {
2021-05-12 15:40:41 +00:00
return t == tx2
2020-11-27 10:55:48 +00:00
} , fs )
2020-05-18 08:20:41 +00:00
require . Equal ( t , 1 , len ( mp . fees ) )
require . Equal ( t , utilityBalanceAndFees {
2021-11-30 15:42:22 +00:00
balance : * uint256 . NewInt ( uint64 ( fs . balance ) ) ,
feeSum : * uint256 . NewInt ( uint64 ( tx2 . NetworkFee ) ) ,
2020-05-18 08:20:41 +00:00
} , mp . fees [ sender0 ] )
// there should be nothing left
mp . RemoveStale ( func ( t * transaction . Transaction ) bool {
2021-05-12 15:40:41 +00:00
return t == tx3
2020-11-27 10:55:48 +00:00
} , fs )
2020-05-18 08:20:41 +00:00
require . Equal ( t , 0 , len ( mp . fees ) )
}
2020-08-19 13:41:32 +00:00
func TestMempoolItemsOrder ( t * testing . T ) {
sender0 := util . Uint160 { 1 , 2 , 3 }
2020-11-27 10:55:48 +00:00
balance := big . NewInt ( 10000000 )
2020-08-19 13:41:32 +00:00
2021-03-25 16:18:01 +00:00
tx1 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 13:41:32 +00:00
tx1 . NetworkFee = new ( big . Int ) . Div ( balance , big . NewInt ( 8 ) ) . Int64 ( )
tx1 . Signers = [ ] transaction . Signer { { Account : sender0 } }
tx1 . Attributes = [ ] transaction . Attribute { { Type : transaction . HighPriority } }
2020-08-29 19:20:56 +00:00
item1 := item { txn : tx1 }
2020-08-19 13:41:32 +00:00
2021-03-25 16:18:01 +00:00
tx2 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 13:41:32 +00:00
tx2 . NetworkFee = new ( big . Int ) . Div ( balance , big . NewInt ( 16 ) ) . Int64 ( )
tx2 . Signers = [ ] transaction . Signer { { Account : sender0 } }
tx2 . Attributes = [ ] transaction . Attribute { { Type : transaction . HighPriority } }
2020-08-29 19:20:56 +00:00
item2 := item { txn : tx2 }
2020-08-19 13:41:32 +00:00
2021-03-25 16:18:01 +00:00
tx3 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 13:41:32 +00:00
tx3 . NetworkFee = new ( big . Int ) . Div ( balance , big . NewInt ( 2 ) ) . Int64 ( )
tx3 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-08-29 19:20:56 +00:00
item3 := item { txn : tx3 }
2020-08-19 13:41:32 +00:00
2021-03-25 16:18:01 +00:00
tx4 := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 13:41:32 +00:00
tx4 . NetworkFee = new ( big . Int ) . Div ( balance , big . NewInt ( 4 ) ) . Int64 ( )
tx4 . Signers = [ ] transaction . Signer { { Account : sender0 } }
2020-08-29 19:20:56 +00:00
item4 := item { txn : tx4 }
2020-08-19 13:41:32 +00:00
2024-08-26 17:42:09 +00:00
require . True ( t , item1 . Compare ( item2 ) > 0 )
require . True ( t , item2 . Compare ( item1 ) < 0 )
require . True ( t , item1 . Compare ( item3 ) > 0 )
require . True ( t , item3 . Compare ( item1 ) < 0 )
require . True ( t , item1 . Compare ( item4 ) > 0 )
require . True ( t , item4 . Compare ( item1 ) < 0 )
require . True ( t , item2 . Compare ( item3 ) > 0 )
require . True ( t , item3 . Compare ( item2 ) < 0 )
require . True ( t , item2 . Compare ( item4 ) > 0 )
require . True ( t , item4 . Compare ( item2 ) < 0 )
require . True ( t , item3 . Compare ( item4 ) > 0 )
require . True ( t , item4 . Compare ( item3 ) < 0 )
2020-08-19 13:41:32 +00:00
}
2020-10-15 11:45:29 +00:00
2020-11-30 09:48:18 +00:00
func TestMempoolAddRemoveOracleResponse ( t * testing . T ) {
2023-04-13 11:03:02 +00:00
mp := New ( 3 , 0 , false , nil )
2020-11-30 09:48:18 +00:00
nonce := uint32 ( 0 )
2020-11-27 10:55:48 +00:00
fs := & FeerStub { balance : 10000 }
2020-11-30 09:48:18 +00:00
newTx := func ( netFee int64 , id uint64 ) * transaction . Transaction {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-11-30 09:48:18 +00:00
tx . NetworkFee = netFee
tx . Nonce = nonce
nonce ++
tx . Signers = [ ] transaction . Signer { { Account : util . Uint160 { 1 , 2 , 3 } } }
tx . Attributes = [ ] transaction . Attribute { {
Type : transaction . OracleResponseT ,
Value : & transaction . OracleResponse { ID : id } ,
} }
// sanity check
_ , ok := mp . TryGetValue ( tx . Hash ( ) )
require . False ( t , ok )
return tx
}
tx1 := newTx ( 10 , 1 )
require . NoError ( t , mp . Add ( tx1 , fs ) )
// smaller network fee
tx2 := newTx ( 5 , 1 )
err := mp . Add ( tx2 , fs )
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , err , ErrOracleResponse )
2020-11-30 09:48:18 +00:00
// ok if old tx is removed
2024-07-30 14:36:57 +00:00
mp . Remove ( tx1 . Hash ( ) )
2020-11-30 09:48:18 +00:00
require . NoError ( t , mp . Add ( tx2 , fs ) )
// higher network fee
tx3 := newTx ( 6 , 1 )
require . NoError ( t , mp . Add ( tx3 , fs ) )
_ , ok := mp . TryGetValue ( tx2 . Hash ( ) )
require . False ( t , ok )
_ , ok = mp . TryGetValue ( tx3 . Hash ( ) )
require . True ( t , ok )
// another oracle response ID
tx4 := newTx ( 4 , 2 )
require . NoError ( t , mp . Add ( tx4 , fs ) )
mp . RemoveStale ( func ( tx * transaction . Transaction ) bool {
return tx . Hash ( ) != tx4 . Hash ( )
} , fs )
// check that oracle id was removed.
tx5 := newTx ( 3 , 2 )
require . NoError ( t , mp . Add ( tx5 , fs ) )
2021-01-15 13:12:27 +00:00
// another oracle response ID with high net fee
tx6 := newTx ( 6 , 3 )
require . NoError ( t , mp . Add ( tx6 , fs ) )
// check respIds
for _ , i := range [ ] uint64 { 1 , 2 , 3 } {
_ , ok := mp . oracleResp [ i ]
require . True ( t , ok )
}
// reach capacity, check that response ID is removed together with tx5
tx7 := newTx ( 6 , 4 )
require . NoError ( t , mp . Add ( tx7 , fs ) )
for _ , i := range [ ] uint64 { 1 , 4 , 3 } {
_ , ok := mp . oracleResp [ i ]
require . True ( t , ok )
}
2020-11-30 09:48:18 +00:00
}
2020-10-15 11:45:29 +00:00
func TestMempoolAddRemoveConflicts ( t * testing . T ) {
2023-05-31 16:22:04 +00:00
var (
capacity = 6
mp = New ( capacity , 0 , false , nil )
sender = transaction . Signer { Account : util . Uint160 { 1 , 2 , 3 } }
maliciousSender = transaction . Signer { Account : util . Uint160 { 4 , 5 , 6 } }
)
2020-11-12 12:05:01 +00:00
var (
2020-11-27 10:55:48 +00:00
fs = & FeerStub { p2pSigExt : true , balance : 100000 }
2020-11-12 12:05:01 +00:00
nonce uint32 = 1
)
2023-05-31 16:22:04 +00:00
getTx := func ( netFee int64 , sender transaction . Signer , hashes ... util . Uint256 ) * transaction . Transaction {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-10-15 11:45:29 +00:00
tx . NetworkFee = netFee
2020-11-12 12:05:01 +00:00
tx . Nonce = nonce
nonce ++
2023-05-31 16:22:04 +00:00
tx . Signers = [ ] transaction . Signer { sender }
2020-10-15 11:45:29 +00:00
tx . Attributes = make ( [ ] transaction . Attribute , len ( hashes ) )
for i , h := range hashes {
tx . Attributes [ i ] = transaction . Attribute {
Type : transaction . ConflictsT ,
Value : & transaction . Conflicts {
Hash : h ,
} ,
}
}
_ , ok := mp . TryGetValue ( tx . Hash ( ) )
require . Equal ( t , false , ok )
return tx
}
2023-05-31 16:22:04 +00:00
getConflictsTx := func ( netFee int64 , hashes ... util . Uint256 ) * transaction . Transaction {
return getTx ( netFee , sender , hashes ... )
}
getMaliciousTx := func ( netFee int64 , hashes ... util . Uint256 ) * transaction . Transaction {
return getTx ( netFee , maliciousSender , hashes ... )
}
2020-10-15 11:45:29 +00:00
// tx1 in mempool and does not conflicts with anyone
smallNetFee := int64 ( 3 )
tx1 := getConflictsTx ( smallNetFee )
require . NoError ( t , mp . Add ( tx1 , fs ) )
// tx2 conflicts with tx1 and has smaller netfee (Step 2, negative)
tx2 := getConflictsTx ( smallNetFee - 1 , tx1 . Hash ( ) )
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , mp . Add ( tx2 , fs ) , ErrConflictsAttribute )
2020-10-15 11:45:29 +00:00
// tx3 conflicts with mempooled tx1 and has larger netfee => tx1 should be replaced by tx3 (Step 2, positive)
tx3 := getConflictsTx ( smallNetFee + 1 , tx1 . Hash ( ) )
require . NoError ( t , mp . Add ( tx3 , fs ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 1 , mp . Count ( ) )
assert . Equal ( t , 1 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// tx1 still does not conflicts with anyone, but tx3 is mempooled, conflicts with tx1
// and has larger netfee => tx1 shouldn't be added again (Step 1, negative)
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , mp . Add ( tx1 , fs ) , ErrConflictsAttribute )
2020-10-15 11:45:29 +00:00
// tx2 can now safely be added because conflicting tx1 is not in mempool => we
// cannot check that tx2 is signed by tx1.Sender
require . NoError ( t , mp . Add ( tx2 , fs ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 1 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) , tx2 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// mempooled tx4 conflicts with tx5, but tx4 has smaller netfee => tx4 should be replaced by tx5 (Step 1, positive)
tx5 := getConflictsTx ( smallNetFee + 1 )
tx4 := getConflictsTx ( smallNetFee , tx5 . Hash ( ) )
require . NoError ( t , mp . Add ( tx4 , fs ) ) // unverified
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 2 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx4 . Hash ( ) } , mp . conflicts [ tx5 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
require . NoError ( t , mp . Add ( tx5 , fs ) )
// tx5 does not conflict with anyone
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 1 , len ( mp . conflicts ) )
2020-10-15 11:45:29 +00:00
// multiple conflicts in attributes of single transaction
tx6 := getConflictsTx ( smallNetFee )
tx7 := getConflictsTx ( smallNetFee )
tx8 := getConflictsTx ( smallNetFee )
// need small network fee later
tx9 := getConflictsTx ( smallNetFee - 2 , tx6 . Hash ( ) , tx7 . Hash ( ) , tx8 . Hash ( ) )
require . NoError ( t , mp . Add ( tx9 , fs ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 4 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) } , mp . conflicts [ tx6 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) } , mp . conflicts [ tx7 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) } , mp . conflicts [ tx8 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) , tx2 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// multiple conflicts in attributes of multiple transactions
tx10 := getConflictsTx ( smallNetFee , tx6 . Hash ( ) )
tx11 := getConflictsTx ( smallNetFee , tx6 . Hash ( ) )
require . NoError ( t , mp . Add ( tx10 , fs ) ) // unverified, because tx6 is not in the pool
require . NoError ( t , mp . Add ( tx11 , fs ) ) // unverified, because tx6 is not in the pool
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 4 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) , tx10 . Hash ( ) , tx11 . Hash ( ) } , mp . conflicts [ tx6 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) } , mp . conflicts [ tx7 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx9 . Hash ( ) } , mp . conflicts [ tx8 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) , tx2 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// reach capacity, remove less prioritised tx9 with its multiple conflicts
require . Equal ( t , capacity , len ( mp . verifiedTxes ) )
tx12 := getConflictsTx ( smallNetFee + 2 )
require . NoError ( t , mp . Add ( tx12 , fs ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 2 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx10 . Hash ( ) , tx11 . Hash ( ) } , mp . conflicts [ tx6 . Hash ( ) ] )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) , tx2 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// manually remove tx11 with its single conflict
2024-07-30 14:36:57 +00:00
mp . Remove ( tx11 . Hash ( ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 2 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx10 . Hash ( ) } , mp . conflicts [ tx6 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// manually remove last tx which conflicts with tx6 => mp.conflicts[tx6] should also be deleted
2024-07-30 14:36:57 +00:00
mp . Remove ( tx10 . Hash ( ) )
2020-11-12 12:14:32 +00:00
assert . Equal ( t , 1 , len ( mp . conflicts ) )
assert . Equal ( t , [ ] util . Uint256 { tx3 . Hash ( ) , tx2 . Hash ( ) } , mp . conflicts [ tx1 . Hash ( ) ] )
2020-10-15 11:45:29 +00:00
// tx13 conflicts with tx2, but is not signed by tx2.Sender
2023-05-31 16:22:04 +00:00
tx13 := getMaliciousTx ( smallNetFee , tx2 . Hash ( ) )
2020-10-15 11:45:29 +00:00
_ , ok := mp . TryGetValue ( tx13 . Hash ( ) )
require . Equal ( t , false , ok )
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , mp . Add ( tx13 , fs ) , ErrConflictsAttribute )
2023-05-26 17:19:21 +00:00
2023-05-31 16:22:04 +00:00
// tx15 conflicts with tx14, but added firstly and has the same network fee => tx14 must not be added.
2023-05-26 17:19:21 +00:00
tx14 := getConflictsTx ( smallNetFee )
tx15 := getConflictsTx ( smallNetFee , tx14 . Hash ( ) )
require . NoError ( t , mp . Add ( tx15 , fs ) )
2023-05-31 16:22:04 +00:00
err := mp . Add ( tx14 , fs )
require . Error ( t , err )
require . True ( t , strings . Contains ( err . Error ( ) , fmt . Sprintf ( "conflicting transactions have bigger or equal network fee: %d vs %d" , smallNetFee , smallNetFee ) ) )
check := func ( t * testing . T , mainFee int64 , fail bool ) {
// Clear mempool.
mp . RemoveStale ( func ( t * transaction . Transaction ) bool {
return false
} , fs )
// mempooled tx17, tx18, tx19 conflict with tx16
tx16 := getConflictsTx ( mainFee )
tx17 := getConflictsTx ( smallNetFee , tx16 . Hash ( ) )
tx18 := getConflictsTx ( smallNetFee , tx16 . Hash ( ) )
tx19 := getMaliciousTx ( smallNetFee , tx16 . Hash ( ) ) // malicious, thus, doesn't take into account during fee evaluation
require . NoError ( t , mp . Add ( tx17 , fs ) )
require . NoError ( t , mp . Add ( tx18 , fs ) )
require . NoError ( t , mp . Add ( tx19 , fs ) )
if fail {
require . Error ( t , mp . Add ( tx16 , fs ) )
_ , ok = mp . TryGetValue ( tx17 . Hash ( ) )
require . True ( t , ok )
_ , ok = mp . TryGetValue ( tx18 . Hash ( ) )
require . True ( t , ok )
_ , ok = mp . TryGetValue ( tx19 . Hash ( ) )
require . True ( t , ok )
} else {
require . NoError ( t , mp . Add ( tx16 , fs ) )
_ , ok = mp . TryGetValue ( tx17 . Hash ( ) )
require . False ( t , ok )
_ , ok = mp . TryGetValue ( tx18 . Hash ( ) )
require . False ( t , ok )
_ , ok = mp . TryGetValue ( tx19 . Hash ( ) )
require . False ( t , ok )
}
}
check ( t , smallNetFee * 2 , true )
check ( t , smallNetFee * 2 + 1 , false )
check = func ( t * testing . T , mainFee int64 , fail bool ) {
// Clear mempool.
mp . RemoveStale ( func ( t * transaction . Transaction ) bool {
return false
} , fs )
// mempooled tx20, tx21, tx22 don't conflict with anyone, but tx23 conflicts with them
tx20 := getConflictsTx ( smallNetFee )
tx21 := getConflictsTx ( smallNetFee )
tx22 := getConflictsTx ( smallNetFee )
tx23 := getConflictsTx ( mainFee , tx20 . Hash ( ) , tx21 . Hash ( ) , tx22 . Hash ( ) )
require . NoError ( t , mp . Add ( tx20 , fs ) )
require . NoError ( t , mp . Add ( tx21 , fs ) )
require . NoError ( t , mp . Add ( tx22 , fs ) )
if fail {
require . Error ( t , mp . Add ( tx23 , fs ) )
_ , ok = mp . TryGetData ( tx20 . Hash ( ) )
require . True ( t , ok )
_ , ok = mp . TryGetData ( tx21 . Hash ( ) )
require . True ( t , ok )
_ , ok = mp . TryGetData ( tx22 . Hash ( ) )
require . True ( t , ok )
} else {
require . NoError ( t , mp . Add ( tx23 , fs ) )
_ , ok = mp . TryGetData ( tx20 . Hash ( ) )
require . False ( t , ok )
_ , ok = mp . TryGetData ( tx21 . Hash ( ) )
require . False ( t , ok )
_ , ok = mp . TryGetData ( tx22 . Hash ( ) )
require . False ( t , ok )
}
}
check ( t , smallNetFee * 3 , true )
check ( t , smallNetFee * 3 + 1 , false )
2020-10-15 11:45:29 +00:00
}
2020-11-27 10:55:48 +00:00
func TestMempoolAddWithDataGetData ( t * testing . T ) {
var (
smallNetFee int64 = 3
nonce uint32
)
fs := & FeerStub {
feePerByte : 0 ,
p2pSigExt : true ,
blockHeight : 5 ,
balance : 100 ,
}
2023-04-13 11:03:02 +00:00
mp := New ( 10 , 1 , false , nil )
2020-11-27 10:55:48 +00:00
// bad, insufficient deposit
r1 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( fs . balance + 1 , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , mp . Add ( r1 . FallbackTransaction , fs , r1 ) , ErrInsufficientFunds )
2020-11-27 10:55:48 +00:00
// good
r2 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r2 . FallbackTransaction , fs , r2 ) )
require . True ( t , mp . ContainsKey ( r2 . FallbackTransaction . Hash ( ) ) )
data , ok := mp . TryGetData ( r2 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r2 , data )
// bad, already in pool
2023-05-04 14:03:06 +00:00
require . ErrorIs ( t , mp . Add ( r2 . FallbackTransaction , fs , r2 ) , ErrDup )
2020-11-27 10:55:48 +00:00
// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
r3 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee + 1 , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r3 . FallbackTransaction , fs , r3 ) )
require . True ( t , mp . ContainsKey ( r3 . FallbackTransaction . Hash ( ) ) )
data , ok = mp . TryGetData ( r3 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r3 , data )
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
r4 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r4 . FallbackTransaction , fs , r4 ) )
require . True ( t , mp . ContainsKey ( r4 . FallbackTransaction . Hash ( ) ) )
data , ok = mp . TryGetData ( r4 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r4 , data )
// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
r5 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r5 . FallbackTransaction , fs , r5 ) )
require . True ( t , mp . ContainsKey ( r5 . FallbackTransaction . Hash ( ) ) )
data , ok = mp . TryGetData ( r5 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r5 , data )
// and both r2's and r4's data should still be reachable
data , ok = mp . TryGetData ( r2 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r2 , data )
data , ok = mp . TryGetData ( r4 . FallbackTransaction . Hash ( ) )
require . True ( t , ok )
require . Equal ( t , r4 , data )
// should fail to get unexisting data
_ , ok = mp . TryGetData ( util . Uint256 { 0 , 0 , 0 } )
require . False ( t , ok )
// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
2023-08-22 09:24:59 +00:00
r6 := mkTwoSignersTx ( smallNetFee , & nonce )
2020-11-27 10:55:48 +00:00
require . NoError ( t , mp . Add ( r6 , fs , nil ) )
require . True ( t , mp . ContainsKey ( r6 . Hash ( ) ) )
data , ok = mp . TryGetData ( r6 . Hash ( ) )
require . True ( t , ok )
require . Nil ( t , data )
// getting data: item is in verifiedMap, but not in verifiedTxes
r7 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r7 . FallbackTransaction , fs , r4 ) )
require . True ( t , mp . ContainsKey ( r7 . FallbackTransaction . Hash ( ) ) )
r8 := & payload . P2PNotaryRequest {
2023-08-22 09:24:59 +00:00
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee - 1 , & nonce ) ,
2020-11-27 10:55:48 +00:00
}
require . NoError ( t , mp . Add ( r8 . FallbackTransaction , fs , r4 ) )
require . True ( t , mp . ContainsKey ( r8 . FallbackTransaction . Hash ( ) ) )
mp . verifiedTxes = append ( mp . verifiedTxes [ : len ( mp . verifiedTxes ) - 2 ] , mp . verifiedTxes [ len ( mp . verifiedTxes ) - 1 ] )
_ , ok = mp . TryGetData ( r7 . FallbackTransaction . Hash ( ) )
require . False ( t , ok )
}
2023-08-22 09:24:59 +00:00
func mkTwoSignersTx ( netFee int64 , nonce * uint32 ) * transaction . Transaction {
tx := transaction . New ( [ ] byte { byte ( opcode . RET ) } , 0 )
tx . Signers = [ ] transaction . Signer { { } , { } }
tx . NetworkFee = netFee
* nonce ++
tx . Nonce = * nonce
return tx
}
func TestMempoolIterateVerifiedTransactions ( t * testing . T ) {
var (
smallNetFee int64 = 3
nonce uint32
r1 , r2 , r3 , r4 , r5 * payload . P2PNotaryRequest
)
fs := & FeerStub {
feePerByte : 0 ,
p2pSigExt : true ,
blockHeight : 5 ,
balance : 100 ,
}
mp := New ( 10 , 1 , false , nil )
checkRequestsOrder := func ( orderedRequests [ ] * payload . P2PNotaryRequest ) {
var pooledRequests [ ] * payload . P2PNotaryRequest
mp . IterateVerifiedTransactions ( func ( tx * transaction . Transaction , data any ) bool {
d := data . ( * payload . P2PNotaryRequest )
pooledRequests = append ( pooledRequests , d )
return true
} )
require . Equal ( t , orderedRequests , pooledRequests )
}
r1 = & payload . P2PNotaryRequest {
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
}
require . NoError ( t , mp . Add ( r1 . FallbackTransaction , fs , r1 ) )
checkRequestsOrder ( [ ] * payload . P2PNotaryRequest { r1 } )
// r2 has higher priority than r1. The resulting mp.verifiedTxes: [r2, r1]
r2 = & payload . P2PNotaryRequest {
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee + 1 , & nonce ) ,
}
require . NoError ( t , mp . Add ( r2 . FallbackTransaction , fs , r2 ) )
checkRequestsOrder ( [ ] * payload . P2PNotaryRequest { r2 , r1 } )
// r3 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3]
r3 = & payload . P2PNotaryRequest {
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
}
require . NoError ( t , mp . Add ( r3 . FallbackTransaction , fs , r3 ) )
checkRequestsOrder ( [ ] * payload . P2PNotaryRequest { r2 , r1 , r3 } )
// r4 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3, r4]
r4 = & payload . P2PNotaryRequest {
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
}
require . NoError ( t , mp . Add ( r4 . FallbackTransaction , fs , r4 ) )
checkRequestsOrder ( [ ] * payload . P2PNotaryRequest { r2 , r1 , r3 , r4 } )
checkPooledRequest := func ( t * testing . T , r * payload . P2PNotaryRequest , isPooled bool ) {
cont := true
notaryRequest := & payload . P2PNotaryRequest { }
mp . IterateVerifiedTransactions ( func ( tx * transaction . Transaction , data any ) bool {
if data != nil {
notaryRequest = data . ( * payload . P2PNotaryRequest )
if notaryRequest . MainTransaction . Hash ( ) == r . MainTransaction . Hash ( ) {
cont = false
}
}
return cont
} )
if isPooled {
require . Equal ( t , false , cont )
require . Equal ( t , r , notaryRequest )
} else {
require . Equal ( t , true , cont )
}
}
checkPooledRequest ( t , r1 , true )
checkPooledRequest ( t , r2 , true )
checkPooledRequest ( t , r3 , true )
checkPooledRequest ( t , r4 , true )
r5 = & payload . P2PNotaryRequest {
MainTransaction : mkTwoSignersTx ( 0 , & nonce ) ,
FallbackTransaction : mkTwoSignersTx ( smallNetFee , & nonce ) ,
}
checkPooledRequest ( t , r5 , false )
}