github.com/aergoio/aergo@v1.3.1/mempool/mempool_test.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  package mempool
     6  
     7  import (
     8  	"encoding/binary"
     9  	"math/big"
    10  	"math/rand"
    11  	"os"
    12  	"sync/atomic"
    13  	"testing"
    14  
    15  	"github.com/aergoio/aergo/account/key"
    16  	"github.com/aergoio/aergo/config"
    17  	"github.com/aergoio/aergo/types"
    18  	"github.com/btcsuite/btcd/btcec"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  const (
    23  	maxAccount       = 1000
    24  	maxRecipient     = 1000
    25  	maxBlockBodySize = 10485760
    26  )
    27  
    28  var (
    29  	pool      *MemPool
    30  	accs      [maxAccount][]byte
    31  	sign      [maxAccount]*btcec.PrivateKey
    32  	recipient [maxRecipient][]byte
    33  )
    34  
    35  func _itobU32(argv uint32) []byte {
    36  	bs := make([]byte, 4)
    37  	binary.LittleEndian.PutUint32(bs, argv)
    38  	return bs
    39  }
    40  
    41  func getAccount(tx *types.Tx) string {
    42  	ab := tx.GetBody().GetAccount()
    43  	aid := types.ToAccountID(ab)
    44  	as := aid.String()
    45  	return as
    46  }
    47  
    48  func simulateBlockGen(txs ...types.Transaction) error {
    49  	lock.Lock()
    50  	inblock := make([]*types.Tx, 0)
    51  	for _, tx := range txs {
    52  		inblock = append(inblock, tx.GetTx())
    53  		acc := getAccount(tx.GetTx())
    54  		n := tx.GetBody().GetNonce()
    55  		nonce[acc] = n
    56  		_, ok := balance[acc]
    57  		if !ok {
    58  			balance[acc] = defaultBalance
    59  		}
    60  		balance[acc] -= tx.GetBody().GetAmountBigInt().Uint64()
    61  	}
    62  	lock.Unlock()
    63  	pool.removeOnBlockArrival(
    64  		&types.Block{
    65  			Body: &types.BlockBody{
    66  				Txs: inblock,
    67  			}})
    68  
    69  	//bestBlockNo++
    70  	return nil
    71  }
    72  func initTest(t *testing.T) {
    73  	serverCtx := config.NewServerContext("", "")
    74  	cfg := serverCtx.GetDefaultConfig().(*config.Config)
    75  	pool = NewMemPoolService(cfg, nil)
    76  	pool.testConfig = true
    77  	pool.BeforeStart()
    78  
    79  	for i := 0; i < maxAccount; i++ {
    80  		privkey, err := btcec.NewPrivateKey(btcec.S256())
    81  		if err != nil {
    82  			t.Fatalf("failed to init test (%s)", err)
    83  		}
    84  		//gen new address
    85  		accs[i] = key.GenerateAddress(&privkey.PublicKey)
    86  		sign[i] = privkey
    87  		recipient[i] = _itobU32(uint32(i))
    88  	}
    89  }
    90  func deinitTest() {
    91  
    92  }
    93  
    94  func sameTx(a *types.Tx, b *types.Tx) bool {
    95  	return types.ToTxID(a.Hash) == types.ToTxID(b.Hash)
    96  }
    97  func sameTxs(a []types.Transaction, b []types.Transaction) bool {
    98  	if len(a) != len(b) {
    99  		return false
   100  	}
   101  	check := false
   102  	for _, txa := range a {
   103  		check = false
   104  		for _, txb := range b {
   105  			if sameTx(txa.GetTx(), txb.GetTx()) {
   106  				check = true
   107  				break
   108  			}
   109  		}
   110  		if !check {
   111  			break
   112  		}
   113  	}
   114  	return check
   115  }
   116  func genTx(acc int, rec int, nonce uint64, amount uint64) types.Transaction {
   117  	tx := types.Tx{
   118  		Body: &types.TxBody{
   119  			Nonce:     nonce,
   120  			Account:   accs[acc],
   121  			Recipient: recipient[rec],
   122  			Amount:    new(big.Int).SetUint64(amount).Bytes(),
   123  		},
   124  	}
   125  	tx.Hash = tx.CalculateTxHash()
   126  	//key.SignTx(&tx, sign[acc])
   127  	return types.NewTransaction(&tx)
   128  }
   129  
   130  /*
   131  func TestTxSize(t *testing.T) {
   132  	initTest(t)
   133  	defer deinitTest()
   134  
   135  	var b []byte
   136  	b = make([]byte, txMaxSize)
   137  	tx := &types.Tx{
   138  		Body: &types.TxBody{
   139  			Nonce:     1,
   140  			Account:   accs[0],
   141  			Recipient: recipient[0],
   142  			Amount:    new(big.Int).SetUint64(1).Bytes(),
   143  			Payload:   b,
   144  		},
   145  	}
   146  	tx.Hash = tx.CalculateTxHash()
   147  	err := pool.put(tx)
   148  	assert.EqualError(t, err, types.ErrTxSizeExceedLimit.Error(), "wrong err")
   149  }
   150  */
   151  
   152  func TestInvalidTransaction(t *testing.T) {
   153  
   154  	initTest(t)
   155  	defer deinitTest()
   156  	err := pool.put(genTx(0, 1, 1, defaultBalance*2))
   157  	assert.EqualError(t, err, types.ErrInsufficientBalance.Error(), "wrong err")
   158  
   159  	err = pool.put(genTx(0, 1, 1, 1))
   160  	assert.NoError(t, err, "tx should be accepted")
   161  
   162  	err = pool.put(genTx(0, 1, 1, 1))
   163  	assert.EqualError(t, err, types.ErrTxAlreadyInMempool.Error(), "tx should be denied")
   164  
   165  	txs := []types.Transaction{genTx(0, 1, 1, 1)}
   166  	simulateBlockGen(txs...)
   167  
   168  	err = pool.put(genTx(0, 1, 1, 1))
   169  	assert.EqualError(t, err, types.ErrTxNonceTooLow.Error(), "tx should be denied")
   170  }
   171  
   172  /*
   173  func TestInvalidTransactions(t *testing.T) {
   174  	initTest(t)
   175  	defer deinitTest()
   176  	tx := genTx(0, 1, 1, 1)
   177  
   178  	key.SignTx(tx, sign[1])
   179  	err := pool.put(tx)
   180  	if err == nil {
   181  		t.Errorf("put invalid tx should be failed")
   182  	}
   183  
   184  	tx.Body.Sign = nil
   185  	tx.Hash = tx.CalculateTxHash()
   186  
   187  	err = pool.put(tx)
   188  	if err == nil {
   189  		t.Errorf("put invalid tx should be failed")
   190  	}
   191  }
   192  */
   193  
   194  func TestOrphanTransaction(t *testing.T) {
   195  
   196  	initTest(t)
   197  	defer deinitTest()
   198  
   199  	err := pool.put(genTx(0, 1, 1, 2))
   200  	assert.NoError(t, err, "tx should be accepted")
   201  
   202  	// tx inject order : 1 3 5 2 4 10 9 8 7 6
   203  	// non-sequential nonce should be accepted (orphan) but not counted
   204  	err = pool.put(genTx(0, 1, 3, 2))
   205  	assert.NoError(t, err, "tx should be accepted")
   206  
   207  	err = pool.put(genTx(0, 1, 5, 2))
   208  	assert.NoError(t, err, "tx should be accepted")
   209  
   210  	total, orphan := pool.Size()
   211  	assert.EqualValuesf(t, []int{total, orphan}, []int{3, 2}, "wrong mempool stat")
   212  
   213  	err = pool.put(genTx(0, 1, 2, 2))
   214  	assert.NoError(t, err, "tx should be accepted")
   215  
   216  	total, orphan = pool.Size()
   217  	assert.EqualValuesf(t, []int{total, orphan}, []int{4, 1}, "wrong mempool stat")
   218  
   219  	err = pool.put(genTx(0, 1, 4, 2))
   220  	assert.NoError(t, err, "tx should be accepted")
   221  
   222  	total, orphan = pool.Size()
   223  	assert.EqualValuesf(t, []int{total, orphan}, []int{5, 0}, "wrong mempool stat")
   224  
   225  	err = pool.put(genTx(0, 1, 10, 2))
   226  	assert.NoError(t, err, "tx should be accepted")
   227  
   228  	err = pool.put(genTx(0, 1, 9, 2))
   229  	assert.NoError(t, err, "tx should be accepted")
   230  
   231  	err = pool.put(genTx(0, 1, 8, 2))
   232  	assert.NoError(t, err, "tx should be accepted")
   233  
   234  	err = pool.put(genTx(0, 1, 7, 2))
   235  	assert.NoError(t, err, "tx should be accepted")
   236  
   237  	total, orphan = pool.Size()
   238  	assert.EqualValuesf(t, []int{total, orphan}, []int{9, 4}, "wrong mempool stat")
   239  
   240  	err = pool.put(genTx(0, 1, 6, 2))
   241  	assert.NoError(t, err, "tx should be accepted")
   242  
   243  	total, orphan = pool.Size()
   244  	assert.EqualValuesf(t, []int{total, orphan}, []int{10, 0}, "wrong mempool stat")
   245  }
   246  
   247  func TestBasics2(t *testing.T) {
   248  	initTest(t)
   249  	defer deinitTest()
   250  	txs := make([]*types.Tx, 0)
   251  
   252  	accCount := 1000
   253  	txCount := 1000
   254  	nonce := make([]uint64, txCount)
   255  	for i := 0; i < txCount; i++ {
   256  		nonce[i] = uint64(i + 1)
   257  	}
   258  
   259  	for i := 0; i < accCount; i++ {
   260  		rand.Shuffle(txCount, func(i, j int) {
   261  			nonce[i], nonce[j] = nonce[j], nonce[i]
   262  		})
   263  		for j := 0; j < txCount; j++ {
   264  			tmp := genTx(i, 0, nonce[j], uint64(i+1))
   265  			txs = append(txs, tmp.GetTx())
   266  		}
   267  	}
   268  
   269  	for _, tx := range txs {
   270  		err := pool.put(types.NewTransaction(tx))
   271  		assert.NoError(t, err, "tx should be accepted")
   272  	}
   273  
   274  	txsMempool, err := pool.get(maxBlockBodySize * 10)
   275  	assert.NoError(t, err, "get failed")
   276  	assert.Equal(t, len(txsMempool), len(txs))
   277  }
   278  
   279  // gen sequential transactions
   280  // check mempool internal states
   281  func TestBasics(t *testing.T) {
   282  	initTest(t)
   283  	defer deinitTest()
   284  	txs := make([]types.Transaction, 0)
   285  
   286  	accCount := 10
   287  	txCount := 10
   288  	nonce := make([]uint64, txCount)
   289  	for i := 0; i < txCount; i++ {
   290  		nonce[i] = uint64(i + 1)
   291  	}
   292  	for i := 0; i < accCount; i++ {
   293  		rand.Shuffle(txCount, func(i, j int) {
   294  			nonce[i], nonce[j] = nonce[j], nonce[i]
   295  		})
   296  		for j := 0; j < txCount; j++ {
   297  			tmp := genTx(i, 0, nonce[j], uint64(i+1))
   298  			txs = append(txs, tmp)
   299  		}
   300  	}
   301  
   302  	errs := pool.puts(txs...)
   303  	assert.Equal(t, len(errs), accCount*txCount, "error length is different")
   304  
   305  	for i := 0; i < len(errs); i++ {
   306  		assert.NoError(t, errs[i], "%dth tx failed", i)
   307  	}
   308  
   309  	txsMempool, err := pool.get(maxBlockBodySize)
   310  	assert.NoError(t, err, "get failed")
   311  	assert.Equal(t, len(txsMempool), len(txs))
   312  }
   313  
   314  func TestDeleteOTxs(t *testing.T) {
   315  	initTest(t)
   316  	defer deinitTest()
   317  	txs := make([]types.Transaction, 0)
   318  	for i := 0; i < 5; i++ {
   319  		tmp := genTx(0, 0, uint64(i+1), uint64(i+1))
   320  		txs = append(txs, tmp)
   321  	}
   322  	pool.puts(txs...)
   323  
   324  	total, orphan := pool.Size()
   325  	assert.EqualValuesf(t, []int{total, orphan}, []int{5, 0}, "wrong mempool stat")
   326  
   327  	txs[4] = genTx(0, 1, 5, 150)
   328  	simulateBlockGen(txs...)
   329  
   330  	total, orphan = pool.Size()
   331  	assert.EqualValuesf(t, []int{total, orphan}, []int{0, 0}, "wrong mempool stat")
   332  }
   333  
   334  // add 100 sequential txs and simulate to generate block 10time.
   335  // each block contains 10 txs
   336  func TestBasicDeleteOnBlockConnect(t *testing.T) {
   337  	initTest(t)
   338  	defer deinitTest()
   339  	txs := make([]types.Transaction, 0)
   340  
   341  	for i := 0; i < 100; i++ {
   342  		tmp := genTx(0, 0, uint64(i+1), uint64(i+1))
   343  		txs = append(txs, tmp)
   344  	}
   345  	pool.puts(txs...)
   346  
   347  	total, orphan := pool.Size()
   348  	assert.EqualValuesf(t, []int{total, orphan}, []int{100, 0}, "wrong mempool stat")
   349  
   350  	//suppose 10 txs are select into new block
   351  	for j := 0; j < 10; j++ {
   352  		simulateBlockGen(txs[:10]...)
   353  
   354  		total, orphan := pool.Size()
   355  		assert.EqualValuesf(t, []int{total, orphan}, []int{10 * (9 - j), 0}, "wrong mempool stat")
   356  
   357  		removed := txs[:10]
   358  
   359  		for _, tx := range removed {
   360  			found := pool.exist(tx.GetHash())
   361  			assert.Nil(t, found, "wrong transaction removed")
   362  		}
   363  
   364  		leftover := txs[10:]
   365  		for _, tx := range leftover {
   366  			found := pool.exist(tx.GetHash())
   367  			assert.NotNil(t, found, "wrong transaction removed")
   368  		}
   369  		txs = txs[10:]
   370  	}
   371  
   372  	l, e := pool.get(maxBlockBodySize)
   373  	assert.NoError(t, e, "get should succeed")
   374  	assert.Equalf(t, len(l), 0, "leftover found")
   375  }
   376  
   377  func TestDeleteInvokeRearrange(t *testing.T) {
   378  
   379  	initTest(t)
   380  	defer deinitTest()
   381  	txs := make([]types.Transaction, 0)
   382  
   383  	missing := map[int]bool{
   384  		7: true, 8: true, 9: true,
   385  		17: true, 18: true, 19: true,
   386  		27: true, 28: true, 29: true,
   387  		33: true, 34: true, 35: true,
   388  		50: true}
   389  
   390  	for i := 1; i < 51; i++ {
   391  		tmp := genTx(0, 0, uint64(i), uint64(i))
   392  		txs = append(txs, tmp)
   393  		if _, v := missing[i]; v {
   394  			continue
   395  		}
   396  		assert.NoError(t, pool.put(tmp), "tx should be accepted")
   397  	}
   398  
   399  	total, orphan := pool.Size()
   400  	assert.EqualValuesf(t, []int{total, orphan}, []int{37, 31}, "wrong mempool stat")
   401  
   402  	// txs currently
   403  	// ready: 1~6 orphan: 10~16, 20~26, 30~32, 36~49
   404  	// test senario : check boundary, middle, end of each tx chunk
   405  	// 1. gen block including 1~4
   406  	// 2. gen block including 5~8
   407  	// 3. gen block including 9~13
   408  	// 4. gen block including  14~28
   409  	// 5. gen block including 29~30
   410  	// 6. gen block including 31~32
   411  	// 7. gen block including 33~35
   412  	// 8. gen blocin including ~50
   413  	start := []int{1, 5, 9, 14, 29, 31, 33, 36}
   414  	end := []int{4, 8, 13, 28, 30, 32, 35, 50}
   415  	for i := 0; i < len(start); i++ {
   416  		s, e := start[i]-1, end[i]
   417  		simulateBlockGen(txs[s:e]...)
   418  
   419  		//p1, p2 := pool.Size()
   420  		//t.Errorf("%d, %d, %d", i, p1, p2)
   421  		removed := txs[s:e]
   422  		for _, tx := range removed {
   423  			found := pool.exist(tx.GetHash())
   424  			assert.Nil(t, found, "wrong transaction removed")
   425  		}
   426  
   427  		leftover := txs[e:]
   428  		for _, tx := range leftover {
   429  			n := tx.GetBody().GetNonce()
   430  			if _, v := missing[int(n)]; v {
   431  				continue
   432  			}
   433  			if pool.exist(tx.GetHash()) == nil {
   434  				t.Errorf("wrong tx removed [%s]", tx.GetBody().String())
   435  			}
   436  		}
   437  	}
   438  }
   439  
   440  func TestSwitchingBestBlock(t *testing.T) {
   441  	initTest(t)
   442  	defer deinitTest()
   443  
   444  	txs := make([]types.Transaction, 0)
   445  	tx0 := genTx(0, 1, 1, 1)
   446  	tx1 := genTx(0, 1, 2, 1)
   447  	txs = append(txs, tx0, tx1)
   448  
   449  	err := pool.puts(txs...)
   450  	if len(err) != 2 || err[0] != nil || err[1] != nil {
   451  		t.Errorf("put should succeed, %s", err)
   452  	}
   453  	simulateBlockGen(txs...)
   454  
   455  	tx2 := genTx(0, 1, 3, 1)
   456  	if err := pool.put(tx2); err != nil {
   457  		t.Errorf("put should succeed, %s", err)
   458  	}
   459  	ready, orphan := pool.Size()
   460  	if ready != 1 || orphan != 0 {
   461  		t.Errorf("size wrong:%d, %d", ready, orphan)
   462  	}
   463  
   464  	simulateBlockGen(txs[:1]...)
   465  
   466  	ready, orphan = pool.Size()
   467  	if ready != 1 || orphan != 1 {
   468  		t.Errorf("size wrong:%d, %d", ready, orphan)
   469  	}
   470  
   471  	tx4 := genTx(0, 1, 5, 1)
   472  	if err := pool.put(tx4); err != nil {
   473  		t.Errorf("put should succeed, %s", err.Error())
   474  	}
   475  
   476  	ready, orphan = pool.Size()
   477  	if ready != 2 || orphan != 2 {
   478  		t.Errorf("size wrong:%d, %d", ready, orphan)
   479  	}
   480  
   481  	if err := pool.put(tx1); err != nil {
   482  		t.Errorf("put should succeed, %s", err.Error())
   483  	}
   484  	ready, orphan = pool.Size()
   485  	if ready != 3 || orphan != 1 {
   486  		t.Errorf("size wrong:%d, %d", ready, orphan)
   487  	}
   488  }
   489  
   490  func TestDumpAndLoad(t *testing.T) {
   491  	initTest(t)
   492  	//set temporary path for test
   493  	pool.dumpPath = "./mempool_dump_test"
   494  	txs := make([]*types.Tx, 0)
   495  
   496  	if _, err := os.Stat(pool.dumpPath); os.IsExist(err) {
   497  		if os.Remove(pool.dumpPath) != nil {
   498  			t.Errorf("init test failed (rm %s failed)", pool.dumpPath)
   499  		}
   500  	}
   501  
   502  	pool.dumpTxsToFile()
   503  	if _, err := os.Stat(pool.dumpPath); err != nil && !os.IsNotExist(err) {
   504  		t.Errorf("err should be NotExist ,but %s", err.Error())
   505  	}
   506  
   507  	if !atomic.CompareAndSwapInt32(&pool.status, initial, running) {
   508  		t.Errorf("pool status should be initial, but %d", pool.status)
   509  	}
   510  	pool.dumpTxsToFile()
   511  	if _, err := os.Stat(pool.dumpPath); err != nil && !os.IsNotExist(err) {
   512  		t.Errorf("err should be NotExist ,but %s", err.Error())
   513  	}
   514  
   515  	for i := 0; i < 100; i++ {
   516  		tmp := genTx(0, 0, uint64(i+1), uint64(i+1))
   517  		txs = append(txs, tmp.GetTx())
   518  		if err := pool.put(tmp); err != nil {
   519  			t.Errorf("put should succeed, %s", err.Error())
   520  		}
   521  	}
   522  
   523  	pool.dumpTxsToFile()
   524  	if _, err := os.Stat(pool.dumpPath); err != nil {
   525  		t.Errorf("dump file should be created but, %s", err.Error())
   526  	}
   527  	deinitTest()
   528  
   529  	initTest(t)
   530  	pool.dumpPath = "./mempool_dump_test"
   531  	ready, orphan := pool.Size()
   532  	if ready != 0 || orphan != 0 {
   533  		t.Errorf("size wrong:%d, %d", ready, orphan)
   534  	}
   535  	if !atomic.CompareAndSwapInt32(&pool.status, initial, running) {
   536  		t.Errorf("pool status should be initial, but %d", pool.status)
   537  	}
   538  	pool.loadTxs()
   539  	ready, orphan = pool.Size()
   540  	if ready != 0 || orphan != 0 {
   541  		t.Errorf("size wrong:%d, %d", ready, orphan)
   542  	}
   543  
   544  	if !atomic.CompareAndSwapInt32(&pool.status, running, initial) {
   545  		t.Errorf("pool status should be initial, but %d", pool.status)
   546  	}
   547  
   548  	pool.loadTxs()
   549  	ready, orphan = pool.Size()
   550  	if ready != 100 || orphan != 0 {
   551  		t.Errorf("size wrong:%d, %d", ready, orphan)
   552  	}
   553  	deinitTest()
   554  	os.Remove(pool.dumpPath) // nolint: errcheck
   555  }
   556  
   557  func TestEvictOnProfit(t *testing.T) {
   558  	initTest(t)
   559  	defer deinitTest()
   560  
   561  	if err := pool.put(genTx(0, 0, 1, 3)); err != nil {
   562  		t.Errorf("put should succeed, %s", err.Error())
   563  	}
   564  	if err := pool.put(genTx(0, 0, 1, 10)); err == nil {
   565  		t.Errorf("put should failed") //FIXME
   566  	}
   567  
   568  	if err := pool.put(genTx(0, 0, 5, 3)); err != nil {
   569  		t.Errorf("put should succeed, %s", err.Error())
   570  	}
   571  	pool.put(genTx(0, 0, 6, 3))
   572  	pool.put(genTx(0, 0, 7, 3))
   573  
   574  	if err := pool.put(genTx(0, 0, 6, 10)); err == nil {
   575  		t.Errorf("put should failed") // FIXME
   576  	}
   577  }
   578  
   579  func TestDeleteInvokePriceFilterOut(t *testing.T) {
   580  	initTest(t)
   581  	defer deinitTest()
   582  
   583  	checkRemainder := func(total int, orphan int) {
   584  		w, o := pool.Size()
   585  		if w != total || o != orphan {
   586  			t.Fatalf("pool should have %d tx(%d orphans) but(%d/%d)\n", total, orphan, w, o)
   587  		}
   588  	}
   589  	txs := make([]types.Transaction, 0)
   590  	txs = append(txs, genTx(0, 1, 1, defaultBalance-6))
   591  	txs = append(txs, genTx(0, 1, 2, 2))
   592  	txs = append(txs, genTx(0, 1, 3, 10))
   593  	txs = append(txs, genTx(0, 1, 4, 5))
   594  
   595  	for _, tx := range txs {
   596  		pool.put(tx)
   597  	}
   598  	checkRemainder(len(txs), 0)
   599  	simulateBlockGen(txs[:1]...)
   600  
   601  	checkRemainder(2, 1)
   602  	simulateBlockGen(txs[1:2]...)
   603  	checkRemainder(0, 0)
   604  }