github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/mempool/mem_pool_test.go (about)

     1  package mempool
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/holiman/uint256"
    12  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    13  	"github.com/nspcc-dev/neo-go/pkg/network/payload"
    14  	"github.com/nspcc-dev/neo-go/pkg/util"
    15  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  type FeerStub struct {
    21  	feePerByte  int64
    22  	p2pSigExt   bool
    23  	blockHeight uint32
    24  	balance     int64
    25  }
    26  
    27  func (fs *FeerStub) GetBaseExecFee() int64 {
    28  	return 30
    29  }
    30  
    31  func (fs *FeerStub) FeePerByte() int64 {
    32  	return fs.feePerByte
    33  }
    34  
    35  func (fs *FeerStub) BlockHeight() uint32 {
    36  	return fs.blockHeight
    37  }
    38  
    39  func (fs *FeerStub) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
    40  	return big.NewInt(fs.balance)
    41  }
    42  
    43  func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
    44  	mp := New(10, 0, false, nil)
    45  	tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
    46  	tx.Nonce = 0
    47  	tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
    48  	_, ok := mp.TryGetValue(tx.Hash())
    49  	require.Equal(t, false, ok)
    50  	require.NoError(t, mp.Add(tx, fs))
    51  	// Re-adding should fail.
    52  	require.Error(t, mp.Add(tx, fs))
    53  	tx2, ok := mp.TryGetValue(tx.Hash())
    54  	require.Equal(t, true, ok)
    55  	require.Equal(t, tx, tx2)
    56  	mp.Remove(tx.Hash(), fs)
    57  	_, ok = mp.TryGetValue(tx.Hash())
    58  	require.Equal(t, false, ok)
    59  	// Make sure nothing left in the mempool after removal.
    60  	assert.Equal(t, 0, len(mp.verifiedMap))
    61  	assert.Equal(t, 0, len(mp.verifiedTxes))
    62  }
    63  
    64  func TestMemPoolRemoveStale(t *testing.T) {
    65  	mp := New(5, 0, false, nil)
    66  	txs := make([]*transaction.Transaction, 5)
    67  	for i := range txs {
    68  		txs[i] = transaction.New([]byte{byte(opcode.PUSH1)}, 0)
    69  		txs[i].Nonce = uint32(i)
    70  		txs[i].Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
    71  		require.NoError(t, mp.Add(txs[i], &FeerStub{blockHeight: uint32(i)}))
    72  	}
    73  
    74  	staleTxs := make(chan *transaction.Transaction, 5)
    75  	f := func(tx *transaction.Transaction, _ any) {
    76  		staleTxs <- tx
    77  	}
    78  	mp.SetResendThreshold(5, f)
    79  
    80  	isValid := func(tx *transaction.Transaction) bool {
    81  		return tx.Nonce%2 == 0
    82  	}
    83  
    84  	mp.RemoveStale(isValid, &FeerStub{blockHeight: 5}) // 0 + 5
    85  	require.Eventually(t, func() bool { return len(staleTxs) == 1 }, time.Second, time.Millisecond*100)
    86  	require.Equal(t, txs[0], <-staleTxs)
    87  
    88  	mp.RemoveStale(isValid, &FeerStub{blockHeight: 7}) // 2 + 5
    89  	require.Eventually(t, func() bool { return len(staleTxs) == 1 }, time.Second, time.Millisecond*100)
    90  	require.Equal(t, txs[2], <-staleTxs)
    91  
    92  	mp.RemoveStale(isValid, &FeerStub{blockHeight: 10}) // 0 + 2 * 5
    93  	require.Eventually(t, func() bool { return len(staleTxs) == 1 }, time.Second, time.Millisecond*100)
    94  	require.Equal(t, txs[0], <-staleTxs)
    95  
    96  	mp.RemoveStale(isValid, &FeerStub{blockHeight: 15}) // 0 + 3 * 5
    97  
    98  	// tx[2] should appear, so it is also checked that tx[0] wasn't sent on height 15.
    99  	mp.RemoveStale(isValid, &FeerStub{blockHeight: 22}) // 2 + 4 * 5
   100  	require.Eventually(t, func() bool { return len(staleTxs) == 1 }, time.Second, time.Millisecond*100)
   101  	require.Equal(t, txs[2], <-staleTxs)
   102  
   103  	// panic if something is sent after this.
   104  	close(staleTxs)
   105  	require.Len(t, staleTxs, 0)
   106  }
   107  
   108  func TestMemPoolAddRemove(t *testing.T) {
   109  	var fs = &FeerStub{}
   110  	testMemPoolAddRemoveWithFeer(t, fs)
   111  }
   112  
   113  func TestOverCapacity(t *testing.T) {
   114  	var fs = &FeerStub{balance: 10000000}
   115  	const mempoolSize = 10
   116  	mp := New(mempoolSize, 0, false, nil)
   117  
   118  	for i := 0; i < mempoolSize; i++ {
   119  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   120  		tx.Nonce = uint32(i)
   121  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   122  		require.NoError(t, mp.Add(tx, fs))
   123  	}
   124  	txcnt := uint32(mempoolSize)
   125  	require.Equal(t, mempoolSize, mp.Count())
   126  	require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   127  
   128  	bigScript := make([]byte, 64)
   129  	bigScript[0] = byte(opcode.PUSH1)
   130  	bigScript[1] = byte(opcode.RET)
   131  	// Fees are also prioritized.
   132  	for i := 0; i < mempoolSize; i++ {
   133  		tx := transaction.New(bigScript, 0)
   134  		tx.NetworkFee = 10000
   135  		tx.Nonce = txcnt
   136  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   137  		txcnt++
   138  		// size is ~90, networkFee is 10000 => feePerByte is 119
   139  		require.NoError(t, mp.Add(tx, fs))
   140  		require.Equal(t, mempoolSize, mp.Count())
   141  		require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   142  	}
   143  	// Less prioritized txes are not allowed anymore.
   144  	tx := transaction.New(bigScript, 0)
   145  	tx.NetworkFee = 100
   146  	tx.Nonce = txcnt
   147  	tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   148  	txcnt++
   149  	require.Error(t, mp.Add(tx, fs))
   150  	require.Equal(t, mempoolSize, mp.Count())
   151  	require.Equal(t, mempoolSize, len(mp.verifiedMap))
   152  	require.Equal(t, mempoolSize, len(mp.verifiedTxes))
   153  	require.False(t, mp.containsKey(tx.Hash()))
   154  	require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   155  
   156  	// Low net fee, but higher per-byte fee is still a better combination.
   157  	tx = transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   158  	tx.Nonce = txcnt
   159  	tx.NetworkFee = 7000
   160  	tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   161  	txcnt++
   162  	// size is ~51 (small script), networkFee is 7000 (<10000)
   163  	// => feePerByte is 137 (>119)
   164  	require.NoError(t, mp.Add(tx, fs))
   165  	require.Equal(t, mempoolSize, mp.Count())
   166  	require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   167  
   168  	// High priority always wins over low priority.
   169  	for i := 0; i < mempoolSize; i++ {
   170  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   171  		tx.NetworkFee = 8000
   172  		tx.Nonce = txcnt
   173  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   174  		txcnt++
   175  		require.NoError(t, mp.Add(tx, fs))
   176  		require.Equal(t, mempoolSize, mp.Count())
   177  		require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   178  	}
   179  	// Good luck with low priority now.
   180  	tx = transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   181  	tx.Nonce = txcnt
   182  	tx.NetworkFee = 7000
   183  	tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   184  	require.Error(t, mp.Add(tx, fs))
   185  	require.Equal(t, mempoolSize, mp.Count())
   186  	require.Equal(t, true, sort.IsSorted(sort.Reverse(mp.verifiedTxes)))
   187  }
   188  
   189  func TestGetVerified(t *testing.T) {
   190  	var fs = &FeerStub{}
   191  	const mempoolSize = 10
   192  	mp := New(mempoolSize, 0, false, nil)
   193  
   194  	txes := make([]*transaction.Transaction, 0, mempoolSize)
   195  	for i := 0; i < mempoolSize; i++ {
   196  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   197  		tx.Nonce = uint32(i)
   198  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   199  		txes = append(txes, tx)
   200  		require.NoError(t, mp.Add(tx, fs))
   201  	}
   202  	require.Equal(t, mempoolSize, mp.Count())
   203  	verTxes := mp.GetVerifiedTransactions()
   204  	require.Equal(t, mempoolSize, len(verTxes))
   205  	require.ElementsMatch(t, txes, verTxes)
   206  	for _, tx := range txes {
   207  		mp.Remove(tx.Hash(), fs)
   208  	}
   209  	verTxes = mp.GetVerifiedTransactions()
   210  	require.Equal(t, 0, len(verTxes))
   211  }
   212  
   213  func TestRemoveStale(t *testing.T) {
   214  	var fs = &FeerStub{}
   215  	const mempoolSize = 10
   216  	mp := New(mempoolSize, 0, false, nil)
   217  
   218  	txes1 := make([]*transaction.Transaction, 0, mempoolSize/2)
   219  	txes2 := make([]*transaction.Transaction, 0, mempoolSize/2)
   220  	for i := 0; i < mempoolSize; i++ {
   221  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   222  		tx.Nonce = uint32(i)
   223  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   224  		if i%2 == 0 {
   225  			txes1 = append(txes1, tx)
   226  		} else {
   227  			txes2 = append(txes2, tx)
   228  		}
   229  		require.NoError(t, mp.Add(tx, fs))
   230  	}
   231  	require.Equal(t, mempoolSize, mp.Count())
   232  	mp.RemoveStale(func(t *transaction.Transaction) bool {
   233  		for _, tx := range txes2 {
   234  			if tx == t {
   235  				return true
   236  			}
   237  		}
   238  		return false
   239  	}, &FeerStub{})
   240  	require.Equal(t, mempoolSize/2, mp.Count())
   241  	verTxes := mp.GetVerifiedTransactions()
   242  	for _, txf := range verTxes {
   243  		require.NotContains(t, txes1, txf)
   244  		require.Contains(t, txes2, txf)
   245  	}
   246  }
   247  
   248  func TestMemPoolFees(t *testing.T) {
   249  	mp := New(10, 0, false, nil)
   250  	fs := &FeerStub{balance: 10000000}
   251  	sender0 := util.Uint160{1, 2, 3}
   252  	tx0 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   253  	tx0.NetworkFee = fs.balance + 1
   254  	tx0.Signers = []transaction.Signer{{Account: sender0}}
   255  	// insufficient funds to add transaction, and balance shouldn't be stored
   256  	require.Equal(t, false, mp.Verify(tx0, fs))
   257  	require.Error(t, mp.Add(tx0, fs))
   258  	require.Equal(t, 0, len(mp.fees))
   259  
   260  	balancePart := new(big.Int).Div(big.NewInt(fs.balance), big.NewInt(4))
   261  	// no problems with adding another transaction with lower fee
   262  	tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   263  	tx1.NetworkFee = balancePart.Int64()
   264  	tx1.Signers = []transaction.Signer{{Account: sender0}}
   265  	require.NoError(t, mp.Add(tx1, fs))
   266  	require.Equal(t, 1, len(mp.fees))
   267  	require.Equal(t, utilityBalanceAndFees{
   268  		balance: *uint256.NewInt(uint64(fs.balance)),
   269  		feeSum:  *uint256.NewInt(uint64(tx1.NetworkFee)),
   270  	}, mp.fees[sender0])
   271  
   272  	// balance shouldn't change after adding one more transaction
   273  	tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   274  	tx2.NetworkFee = new(big.Int).Sub(big.NewInt(fs.balance), balancePart).Int64()
   275  	tx2.Signers = []transaction.Signer{{Account: sender0}}
   276  	require.NoError(t, mp.Add(tx2, fs))
   277  	require.Equal(t, 2, len(mp.verifiedTxes))
   278  	require.Equal(t, 1, len(mp.fees))
   279  	require.Equal(t, utilityBalanceAndFees{
   280  		balance: *uint256.NewInt(uint64(fs.balance)),
   281  		feeSum:  *uint256.NewInt(uint64(fs.balance)),
   282  	}, mp.fees[sender0])
   283  
   284  	// can't add more transactions as we don't have enough GAS
   285  	tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   286  	tx3.NetworkFee = 1
   287  	tx3.Signers = []transaction.Signer{{Account: sender0}}
   288  	require.Equal(t, false, mp.Verify(tx3, fs))
   289  	require.Error(t, mp.Add(tx3, fs))
   290  	require.Equal(t, 1, len(mp.fees))
   291  	require.Equal(t, utilityBalanceAndFees{
   292  		balance: *uint256.NewInt(uint64(fs.balance)),
   293  		feeSum:  *uint256.NewInt(uint64(fs.balance)),
   294  	}, mp.fees[sender0])
   295  
   296  	// check whether sender's fee updates correctly
   297  	mp.RemoveStale(func(t *transaction.Transaction) bool {
   298  		return t == tx2
   299  	}, fs)
   300  	require.Equal(t, 1, len(mp.fees))
   301  	require.Equal(t, utilityBalanceAndFees{
   302  		balance: *uint256.NewInt(uint64(fs.balance)),
   303  		feeSum:  *uint256.NewInt(uint64(tx2.NetworkFee)),
   304  	}, mp.fees[sender0])
   305  
   306  	// there should be nothing left
   307  	mp.RemoveStale(func(t *transaction.Transaction) bool {
   308  		return t == tx3
   309  	}, fs)
   310  	require.Equal(t, 0, len(mp.fees))
   311  }
   312  
   313  func TestMempoolItemsOrder(t *testing.T) {
   314  	sender0 := util.Uint160{1, 2, 3}
   315  	balance := big.NewInt(10000000)
   316  
   317  	tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   318  	tx1.NetworkFee = new(big.Int).Div(balance, big.NewInt(8)).Int64()
   319  	tx1.Signers = []transaction.Signer{{Account: sender0}}
   320  	tx1.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}
   321  	item1 := item{txn: tx1}
   322  
   323  	tx2 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   324  	tx2.NetworkFee = new(big.Int).Div(balance, big.NewInt(16)).Int64()
   325  	tx2.Signers = []transaction.Signer{{Account: sender0}}
   326  	tx2.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}
   327  	item2 := item{txn: tx2}
   328  
   329  	tx3 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   330  	tx3.NetworkFee = new(big.Int).Div(balance, big.NewInt(2)).Int64()
   331  	tx3.Signers = []transaction.Signer{{Account: sender0}}
   332  	item3 := item{txn: tx3}
   333  
   334  	tx4 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   335  	tx4.NetworkFee = new(big.Int).Div(balance, big.NewInt(4)).Int64()
   336  	tx4.Signers = []transaction.Signer{{Account: sender0}}
   337  	item4 := item{txn: tx4}
   338  
   339  	require.True(t, item1.CompareTo(item2) > 0)
   340  	require.True(t, item2.CompareTo(item1) < 0)
   341  	require.True(t, item1.CompareTo(item3) > 0)
   342  	require.True(t, item3.CompareTo(item1) < 0)
   343  	require.True(t, item1.CompareTo(item4) > 0)
   344  	require.True(t, item4.CompareTo(item1) < 0)
   345  	require.True(t, item2.CompareTo(item3) > 0)
   346  	require.True(t, item3.CompareTo(item2) < 0)
   347  	require.True(t, item2.CompareTo(item4) > 0)
   348  	require.True(t, item4.CompareTo(item2) < 0)
   349  	require.True(t, item3.CompareTo(item4) > 0)
   350  	require.True(t, item4.CompareTo(item3) < 0)
   351  }
   352  
   353  func TestMempoolAddRemoveOracleResponse(t *testing.T) {
   354  	mp := New(3, 0, false, nil)
   355  	nonce := uint32(0)
   356  	fs := &FeerStub{balance: 10000}
   357  	newTx := func(netFee int64, id uint64) *transaction.Transaction {
   358  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   359  		tx.NetworkFee = netFee
   360  		tx.Nonce = nonce
   361  		nonce++
   362  		tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}}
   363  		tx.Attributes = []transaction.Attribute{{
   364  			Type:  transaction.OracleResponseT,
   365  			Value: &transaction.OracleResponse{ID: id},
   366  		}}
   367  		// sanity check
   368  		_, ok := mp.TryGetValue(tx.Hash())
   369  		require.False(t, ok)
   370  		return tx
   371  	}
   372  
   373  	tx1 := newTx(10, 1)
   374  	require.NoError(t, mp.Add(tx1, fs))
   375  
   376  	// smaller network fee
   377  	tx2 := newTx(5, 1)
   378  	err := mp.Add(tx2, fs)
   379  	require.ErrorIs(t, err, ErrOracleResponse)
   380  
   381  	// ok if old tx is removed
   382  	mp.Remove(tx1.Hash(), fs)
   383  	require.NoError(t, mp.Add(tx2, fs))
   384  
   385  	// higher network fee
   386  	tx3 := newTx(6, 1)
   387  	require.NoError(t, mp.Add(tx3, fs))
   388  	_, ok := mp.TryGetValue(tx2.Hash())
   389  	require.False(t, ok)
   390  	_, ok = mp.TryGetValue(tx3.Hash())
   391  	require.True(t, ok)
   392  
   393  	// another oracle response ID
   394  	tx4 := newTx(4, 2)
   395  	require.NoError(t, mp.Add(tx4, fs))
   396  
   397  	mp.RemoveStale(func(tx *transaction.Transaction) bool {
   398  		return tx.Hash() != tx4.Hash()
   399  	}, fs)
   400  
   401  	// check that oracle id was removed.
   402  	tx5 := newTx(3, 2)
   403  	require.NoError(t, mp.Add(tx5, fs))
   404  
   405  	// another oracle response ID with high net fee
   406  	tx6 := newTx(6, 3)
   407  	require.NoError(t, mp.Add(tx6, fs))
   408  	// check respIds
   409  	for _, i := range []uint64{1, 2, 3} {
   410  		_, ok := mp.oracleResp[i]
   411  		require.True(t, ok)
   412  	}
   413  	// reach capacity, check that response ID is removed together with tx5
   414  	tx7 := newTx(6, 4)
   415  	require.NoError(t, mp.Add(tx7, fs))
   416  	for _, i := range []uint64{1, 4, 3} {
   417  		_, ok := mp.oracleResp[i]
   418  		require.True(t, ok)
   419  	}
   420  }
   421  
   422  func TestMempoolAddRemoveConflicts(t *testing.T) {
   423  	var (
   424  		capacity        = 6
   425  		mp              = New(capacity, 0, false, nil)
   426  		sender          = transaction.Signer{Account: util.Uint160{1, 2, 3}}
   427  		maliciousSender = transaction.Signer{Account: util.Uint160{4, 5, 6}}
   428  	)
   429  
   430  	var (
   431  		fs           = &FeerStub{p2pSigExt: true, balance: 100000}
   432  		nonce uint32 = 1
   433  	)
   434  	getTx := func(netFee int64, sender transaction.Signer, hashes ...util.Uint256) *transaction.Transaction {
   435  		tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
   436  		tx.NetworkFee = netFee
   437  		tx.Nonce = nonce
   438  		nonce++
   439  		tx.Signers = []transaction.Signer{sender}
   440  		tx.Attributes = make([]transaction.Attribute, len(hashes))
   441  		for i, h := range hashes {
   442  			tx.Attributes[i] = transaction.Attribute{
   443  				Type: transaction.ConflictsT,
   444  				Value: &transaction.Conflicts{
   445  					Hash: h,
   446  				},
   447  			}
   448  		}
   449  		_, ok := mp.TryGetValue(tx.Hash())
   450  		require.Equal(t, false, ok)
   451  		return tx
   452  	}
   453  	getConflictsTx := func(netFee int64, hashes ...util.Uint256) *transaction.Transaction {
   454  		return getTx(netFee, sender, hashes...)
   455  	}
   456  	getMaliciousTx := func(netFee int64, hashes ...util.Uint256) *transaction.Transaction {
   457  		return getTx(netFee, maliciousSender, hashes...)
   458  	}
   459  
   460  	// tx1 in mempool and does not conflicts with anyone
   461  	smallNetFee := int64(3)
   462  	tx1 := getConflictsTx(smallNetFee)
   463  	require.NoError(t, mp.Add(tx1, fs))
   464  
   465  	// tx2 conflicts with tx1 and has smaller netfee (Step 2, negative)
   466  	tx2 := getConflictsTx(smallNetFee-1, tx1.Hash())
   467  	require.ErrorIs(t, mp.Add(tx2, fs), ErrConflictsAttribute)
   468  
   469  	// tx3 conflicts with mempooled tx1 and has larger netfee => tx1 should be replaced by tx3 (Step 2, positive)
   470  	tx3 := getConflictsTx(smallNetFee+1, tx1.Hash())
   471  	require.NoError(t, mp.Add(tx3, fs))
   472  	assert.Equal(t, 1, mp.Count())
   473  	assert.Equal(t, 1, len(mp.conflicts))
   474  	assert.Equal(t, []util.Uint256{tx3.Hash()}, mp.conflicts[tx1.Hash()])
   475  
   476  	// tx1 still does not conflicts with anyone, but tx3 is mempooled, conflicts with tx1
   477  	// and has larger netfee => tx1 shouldn't be added again (Step 1, negative)
   478  	require.ErrorIs(t, mp.Add(tx1, fs), ErrConflictsAttribute)
   479  
   480  	// tx2 can now safely be added because conflicting tx1 is not in mempool => we
   481  	// cannot check that tx2 is signed by tx1.Sender
   482  	require.NoError(t, mp.Add(tx2, fs))
   483  	assert.Equal(t, 1, len(mp.conflicts))
   484  	assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()])
   485  
   486  	// mempooled tx4 conflicts with tx5, but tx4 has smaller netfee => tx4 should be replaced by tx5 (Step 1, positive)
   487  	tx5 := getConflictsTx(smallNetFee + 1)
   488  	tx4 := getConflictsTx(smallNetFee, tx5.Hash())
   489  	require.NoError(t, mp.Add(tx4, fs)) // unverified
   490  	assert.Equal(t, 2, len(mp.conflicts))
   491  	assert.Equal(t, []util.Uint256{tx4.Hash()}, mp.conflicts[tx5.Hash()])
   492  	require.NoError(t, mp.Add(tx5, fs))
   493  	// tx5 does not conflict with anyone
   494  	assert.Equal(t, 1, len(mp.conflicts))
   495  
   496  	// multiple conflicts in attributes of single transaction
   497  	tx6 := getConflictsTx(smallNetFee)
   498  	tx7 := getConflictsTx(smallNetFee)
   499  	tx8 := getConflictsTx(smallNetFee)
   500  	// need small network fee later
   501  	tx9 := getConflictsTx(smallNetFee-2, tx6.Hash(), tx7.Hash(), tx8.Hash())
   502  	require.NoError(t, mp.Add(tx9, fs))
   503  	assert.Equal(t, 4, len(mp.conflicts))
   504  	assert.Equal(t, []util.Uint256{tx9.Hash()}, mp.conflicts[tx6.Hash()])
   505  	assert.Equal(t, []util.Uint256{tx9.Hash()}, mp.conflicts[tx7.Hash()])
   506  	assert.Equal(t, []util.Uint256{tx9.Hash()}, mp.conflicts[tx8.Hash()])
   507  	assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()])
   508  
   509  	// multiple conflicts in attributes of multiple transactions
   510  	tx10 := getConflictsTx(smallNetFee, tx6.Hash())
   511  	tx11 := getConflictsTx(smallNetFee, tx6.Hash())
   512  	require.NoError(t, mp.Add(tx10, fs)) // unverified, because tx6 is not in the pool
   513  	require.NoError(t, mp.Add(tx11, fs)) // unverified, because tx6 is not in the pool
   514  	assert.Equal(t, 4, len(mp.conflicts))
   515  	assert.Equal(t, []util.Uint256{tx9.Hash(), tx10.Hash(), tx11.Hash()}, mp.conflicts[tx6.Hash()])
   516  	assert.Equal(t, []util.Uint256{tx9.Hash()}, mp.conflicts[tx7.Hash()])
   517  	assert.Equal(t, []util.Uint256{tx9.Hash()}, mp.conflicts[tx8.Hash()])
   518  	assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()])
   519  
   520  	// reach capacity, remove less prioritised tx9 with its multiple conflicts
   521  	require.Equal(t, capacity, len(mp.verifiedTxes))
   522  	tx12 := getConflictsTx(smallNetFee + 2)
   523  	require.NoError(t, mp.Add(tx12, fs))
   524  	assert.Equal(t, 2, len(mp.conflicts))
   525  	assert.Equal(t, []util.Uint256{tx10.Hash(), tx11.Hash()}, mp.conflicts[tx6.Hash()])
   526  	assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()])
   527  
   528  	// manually remove tx11 with its single conflict
   529  	mp.Remove(tx11.Hash(), fs)
   530  	assert.Equal(t, 2, len(mp.conflicts))
   531  	assert.Equal(t, []util.Uint256{tx10.Hash()}, mp.conflicts[tx6.Hash()])
   532  
   533  	// manually remove last tx which conflicts with tx6 => mp.conflicts[tx6] should also be deleted
   534  	mp.Remove(tx10.Hash(), fs)
   535  	assert.Equal(t, 1, len(mp.conflicts))
   536  	assert.Equal(t, []util.Uint256{tx3.Hash(), tx2.Hash()}, mp.conflicts[tx1.Hash()])
   537  
   538  	// tx13 conflicts with tx2, but is not signed by tx2.Sender
   539  	tx13 := getMaliciousTx(smallNetFee, tx2.Hash())
   540  	_, ok := mp.TryGetValue(tx13.Hash())
   541  	require.Equal(t, false, ok)
   542  	require.ErrorIs(t, mp.Add(tx13, fs), ErrConflictsAttribute)
   543  
   544  	// tx15 conflicts with tx14, but added firstly and has the same network fee => tx14 must not be added.
   545  	tx14 := getConflictsTx(smallNetFee)
   546  	tx15 := getConflictsTx(smallNetFee, tx14.Hash())
   547  	require.NoError(t, mp.Add(tx15, fs))
   548  	err := mp.Add(tx14, fs)
   549  	require.Error(t, err)
   550  
   551  	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("conflicting transactions have bigger or equal network fee: %d vs %d", smallNetFee, smallNetFee)))
   552  
   553  	check := func(t *testing.T, mainFee int64, fail bool) {
   554  		// Clear mempool.
   555  		mp.RemoveStale(func(t *transaction.Transaction) bool {
   556  			return false
   557  		}, fs)
   558  
   559  		// mempooled tx17, tx18, tx19 conflict with tx16
   560  		tx16 := getConflictsTx(mainFee)
   561  		tx17 := getConflictsTx(smallNetFee, tx16.Hash())
   562  		tx18 := getConflictsTx(smallNetFee, tx16.Hash())
   563  		tx19 := getMaliciousTx(smallNetFee, tx16.Hash()) // malicious, thus, doesn't take into account during fee evaluation
   564  		require.NoError(t, mp.Add(tx17, fs))
   565  		require.NoError(t, mp.Add(tx18, fs))
   566  		require.NoError(t, mp.Add(tx19, fs))
   567  		if fail {
   568  			require.Error(t, mp.Add(tx16, fs))
   569  			_, ok = mp.TryGetValue(tx17.Hash())
   570  			require.True(t, ok)
   571  			_, ok = mp.TryGetValue(tx18.Hash())
   572  			require.True(t, ok)
   573  			_, ok = mp.TryGetValue(tx19.Hash())
   574  			require.True(t, ok)
   575  		} else {
   576  			require.NoError(t, mp.Add(tx16, fs))
   577  			_, ok = mp.TryGetValue(tx17.Hash())
   578  			require.False(t, ok)
   579  			_, ok = mp.TryGetValue(tx18.Hash())
   580  			require.False(t, ok)
   581  			_, ok = mp.TryGetValue(tx19.Hash())
   582  			require.False(t, ok)
   583  		}
   584  	}
   585  	check(t, smallNetFee*2, true)
   586  	check(t, smallNetFee*2+1, false)
   587  
   588  	check = func(t *testing.T, mainFee int64, fail bool) {
   589  		// Clear mempool.
   590  		mp.RemoveStale(func(t *transaction.Transaction) bool {
   591  			return false
   592  		}, fs)
   593  
   594  		// mempooled tx20, tx21, tx22 don't conflict with anyone, but tx23 conflicts with them
   595  		tx20 := getConflictsTx(smallNetFee)
   596  		tx21 := getConflictsTx(smallNetFee)
   597  		tx22 := getConflictsTx(smallNetFee)
   598  		tx23 := getConflictsTx(mainFee, tx20.Hash(), tx21.Hash(), tx22.Hash())
   599  		require.NoError(t, mp.Add(tx20, fs))
   600  		require.NoError(t, mp.Add(tx21, fs))
   601  		require.NoError(t, mp.Add(tx22, fs))
   602  		if fail {
   603  			require.Error(t, mp.Add(tx23, fs))
   604  			_, ok = mp.TryGetData(tx20.Hash())
   605  			require.True(t, ok)
   606  			_, ok = mp.TryGetData(tx21.Hash())
   607  			require.True(t, ok)
   608  			_, ok = mp.TryGetData(tx22.Hash())
   609  			require.True(t, ok)
   610  		} else {
   611  			require.NoError(t, mp.Add(tx23, fs))
   612  			_, ok = mp.TryGetData(tx20.Hash())
   613  			require.False(t, ok)
   614  			_, ok = mp.TryGetData(tx21.Hash())
   615  			require.False(t, ok)
   616  			_, ok = mp.TryGetData(tx22.Hash())
   617  			require.False(t, ok)
   618  		}
   619  	}
   620  	check(t, smallNetFee*3, true)
   621  	check(t, smallNetFee*3+1, false)
   622  }
   623  
   624  func TestMempoolAddWithDataGetData(t *testing.T) {
   625  	var (
   626  		smallNetFee int64 = 3
   627  		nonce       uint32
   628  	)
   629  	fs := &FeerStub{
   630  		feePerByte:  0,
   631  		p2pSigExt:   true,
   632  		blockHeight: 5,
   633  		balance:     100,
   634  	}
   635  	mp := New(10, 1, false, nil)
   636  
   637  	// bad, insufficient deposit
   638  	r1 := &payload.P2PNotaryRequest{
   639  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   640  		FallbackTransaction: mkTwoSignersTx(fs.balance+1, &nonce),
   641  	}
   642  	require.ErrorIs(t, mp.Add(r1.FallbackTransaction, fs, r1), ErrInsufficientFunds)
   643  
   644  	// good
   645  	r2 := &payload.P2PNotaryRequest{
   646  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   647  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   648  	}
   649  	require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
   650  	require.True(t, mp.ContainsKey(r2.FallbackTransaction.Hash()))
   651  	data, ok := mp.TryGetData(r2.FallbackTransaction.Hash())
   652  	require.True(t, ok)
   653  	require.Equal(t, r2, data)
   654  
   655  	// bad, already in pool
   656  	require.ErrorIs(t, mp.Add(r2.FallbackTransaction, fs, r2), ErrDup)
   657  
   658  	// good, higher priority than r2. The resulting mp.verifiedTxes: [r3, r2]
   659  	r3 := &payload.P2PNotaryRequest{
   660  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   661  		FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
   662  	}
   663  	require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
   664  	require.True(t, mp.ContainsKey(r3.FallbackTransaction.Hash()))
   665  	data, ok = mp.TryGetData(r3.FallbackTransaction.Hash())
   666  	require.True(t, ok)
   667  	require.Equal(t, r3, data)
   668  
   669  	// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4]
   670  	r4 := &payload.P2PNotaryRequest{
   671  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   672  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   673  	}
   674  	require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
   675  	require.True(t, mp.ContainsKey(r4.FallbackTransaction.Hash()))
   676  	data, ok = mp.TryGetData(r4.FallbackTransaction.Hash())
   677  	require.True(t, ok)
   678  	require.Equal(t, r4, data)
   679  
   680  	// good, same priority as r2. The resulting mp.verifiedTxes: [r3, r2, r4, r5]
   681  	r5 := &payload.P2PNotaryRequest{
   682  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   683  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   684  	}
   685  	require.NoError(t, mp.Add(r5.FallbackTransaction, fs, r5))
   686  	require.True(t, mp.ContainsKey(r5.FallbackTransaction.Hash()))
   687  	data, ok = mp.TryGetData(r5.FallbackTransaction.Hash())
   688  	require.True(t, ok)
   689  	require.Equal(t, r5, data)
   690  
   691  	// and both r2's and r4's data should still be reachable
   692  	data, ok = mp.TryGetData(r2.FallbackTransaction.Hash())
   693  	require.True(t, ok)
   694  	require.Equal(t, r2, data)
   695  	data, ok = mp.TryGetData(r4.FallbackTransaction.Hash())
   696  	require.True(t, ok)
   697  	require.Equal(t, r4, data)
   698  
   699  	// should fail to get unexisting data
   700  	_, ok = mp.TryGetData(util.Uint256{0, 0, 0})
   701  	require.False(t, ok)
   702  
   703  	// but getting nil data is OK. The resulting mp.verifiedTxes: [r3, r2, r4, r5, r6]
   704  	r6 := mkTwoSignersTx(smallNetFee, &nonce)
   705  	require.NoError(t, mp.Add(r6, fs, nil))
   706  	require.True(t, mp.ContainsKey(r6.Hash()))
   707  	data, ok = mp.TryGetData(r6.Hash())
   708  	require.True(t, ok)
   709  	require.Nil(t, data)
   710  
   711  	// getting data: item is in verifiedMap, but not in verifiedTxes
   712  	r7 := &payload.P2PNotaryRequest{
   713  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   714  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   715  	}
   716  	require.NoError(t, mp.Add(r7.FallbackTransaction, fs, r4))
   717  	require.True(t, mp.ContainsKey(r7.FallbackTransaction.Hash()))
   718  	r8 := &payload.P2PNotaryRequest{
   719  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   720  		FallbackTransaction: mkTwoSignersTx(smallNetFee-1, &nonce),
   721  	}
   722  	require.NoError(t, mp.Add(r8.FallbackTransaction, fs, r4))
   723  	require.True(t, mp.ContainsKey(r8.FallbackTransaction.Hash()))
   724  	mp.verifiedTxes = append(mp.verifiedTxes[:len(mp.verifiedTxes)-2], mp.verifiedTxes[len(mp.verifiedTxes)-1])
   725  	_, ok = mp.TryGetData(r7.FallbackTransaction.Hash())
   726  	require.False(t, ok)
   727  }
   728  
   729  func mkTwoSignersTx(netFee int64, nonce *uint32) *transaction.Transaction {
   730  	tx := transaction.New([]byte{byte(opcode.RET)}, 0)
   731  	tx.Signers = []transaction.Signer{{}, {}}
   732  	tx.NetworkFee = netFee
   733  	*nonce++
   734  	tx.Nonce = *nonce
   735  	return tx
   736  }
   737  
   738  func TestMempoolIterateVerifiedTransactions(t *testing.T) {
   739  	var (
   740  		smallNetFee        int64 = 3
   741  		nonce              uint32
   742  		r1, r2, r3, r4, r5 *payload.P2PNotaryRequest
   743  	)
   744  	fs := &FeerStub{
   745  		feePerByte:  0,
   746  		p2pSigExt:   true,
   747  		blockHeight: 5,
   748  		balance:     100,
   749  	}
   750  	mp := New(10, 1, false, nil)
   751  
   752  	checkRequestsOrder := func(orderedRequests []*payload.P2PNotaryRequest) {
   753  		var pooledRequests []*payload.P2PNotaryRequest
   754  		mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
   755  			d := data.(*payload.P2PNotaryRequest)
   756  			pooledRequests = append(pooledRequests, d)
   757  			return true
   758  		})
   759  		require.Equal(t, orderedRequests, pooledRequests)
   760  	}
   761  
   762  	r1 = &payload.P2PNotaryRequest{
   763  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   764  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   765  	}
   766  	require.NoError(t, mp.Add(r1.FallbackTransaction, fs, r1))
   767  	checkRequestsOrder([]*payload.P2PNotaryRequest{r1})
   768  
   769  	// r2 has higher priority than r1. The resulting mp.verifiedTxes: [r2, r1]
   770  	r2 = &payload.P2PNotaryRequest{
   771  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   772  		FallbackTransaction: mkTwoSignersTx(smallNetFee+1, &nonce),
   773  	}
   774  	require.NoError(t, mp.Add(r2.FallbackTransaction, fs, r2))
   775  	checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1})
   776  
   777  	// r3 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3]
   778  	r3 = &payload.P2PNotaryRequest{
   779  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   780  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   781  	}
   782  	require.NoError(t, mp.Add(r3.FallbackTransaction, fs, r3))
   783  	checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3})
   784  
   785  	// r4 has the same priority as r1. The resulting mp.verifiedTxes: [r2, r1, r3, r4]
   786  	r4 = &payload.P2PNotaryRequest{
   787  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   788  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   789  	}
   790  	require.NoError(t, mp.Add(r4.FallbackTransaction, fs, r4))
   791  	checkRequestsOrder([]*payload.P2PNotaryRequest{r2, r1, r3, r4})
   792  
   793  	checkPooledRequest := func(t *testing.T, r *payload.P2PNotaryRequest, isPooled bool) {
   794  		cont := true
   795  		notaryRequest := &payload.P2PNotaryRequest{}
   796  		mp.IterateVerifiedTransactions(func(tx *transaction.Transaction, data any) bool {
   797  			if data != nil {
   798  				notaryRequest = data.(*payload.P2PNotaryRequest)
   799  				if notaryRequest.MainTransaction.Hash() == r.MainTransaction.Hash() {
   800  					cont = false
   801  				}
   802  			}
   803  			return cont
   804  		})
   805  
   806  		if isPooled {
   807  			require.Equal(t, false, cont)
   808  			require.Equal(t, r, notaryRequest)
   809  		} else {
   810  			require.Equal(t, true, cont)
   811  		}
   812  	}
   813  	checkPooledRequest(t, r1, true)
   814  	checkPooledRequest(t, r2, true)
   815  	checkPooledRequest(t, r3, true)
   816  	checkPooledRequest(t, r4, true)
   817  
   818  	r5 = &payload.P2PNotaryRequest{
   819  		MainTransaction:     mkTwoSignersTx(0, &nonce),
   820  		FallbackTransaction: mkTwoSignersTx(smallNetFee, &nonce),
   821  	}
   822  	checkPooledRequest(t, r5, false)
   823  }