github.com/MetalBlockchain/subnet-evm@v0.4.9/plugin/evm/tx_gossiping_test.go (about)

     1  // (c) 2019-2021, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package evm
     5  
     6  import (
     7  	"context"
     8  	"crypto/ecdsa"
     9  	"encoding/json"
    10  	"fmt"
    11  	"math/big"
    12  	"strings"
    13  	"sync"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/MetalBlockchain/metalgo/ids"
    18  	"github.com/MetalBlockchain/metalgo/utils/set"
    19  
    20  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/crypto"
    22  	"github.com/ethereum/go-ethereum/rlp"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"github.com/MetalBlockchain/subnet-evm/core"
    27  	"github.com/MetalBlockchain/subnet-evm/core/types"
    28  	"github.com/MetalBlockchain/subnet-evm/params"
    29  	"github.com/MetalBlockchain/subnet-evm/plugin/evm/message"
    30  )
    31  
    32  func fundAddressByGenesis(addrs []common.Address) (string, error) {
    33  	balance := big.NewInt(0xffffffffffffff)
    34  	genesis := &core.Genesis{
    35  		Difficulty: common.Big0,
    36  		GasLimit:   params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
    37  	}
    38  	funds := make(map[common.Address]core.GenesisAccount)
    39  	for _, addr := range addrs {
    40  		funds[addr] = core.GenesisAccount{
    41  			Balance: balance,
    42  		}
    43  	}
    44  	genesis.Alloc = funds
    45  	genesis.Config = params.TestChainConfig
    46  
    47  	bytes, err := json.Marshal(genesis)
    48  	return string(bytes), err
    49  }
    50  
    51  func getValidTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*types.Transaction {
    52  	res := make([]*types.Transaction, count)
    53  
    54  	to := common.Address{}
    55  	amount := big.NewInt(10000)
    56  	gasLimit := uint64(100000)
    57  
    58  	for i := 0; i < count; i++ {
    59  		tx, _ := types.SignTx(
    60  			types.NewTransaction(
    61  				uint64(i),
    62  				to,
    63  				amount,
    64  				gasLimit,
    65  				gasPrice,
    66  				[]byte(strings.Repeat("aaaaaaaaaa", 100))),
    67  			types.HomesteadSigner{}, key)
    68  		tx.SetFirstSeen(time.Now().Add(-1 * time.Minute))
    69  		res[i] = tx
    70  	}
    71  	return res
    72  }
    73  
    74  // show that locally issued eth txs are gossiped
    75  // Note: channel through which subnet-evm mempool push txs to vm is injected here
    76  // to ease up UT, which target only VM behaviors in response to subnet-evm mempool
    77  // signals
    78  func TestMempoolTxsAddedTxsGossipedAfterActivation(t *testing.T) {
    79  	t.Skip("FLAKY")
    80  	assert := assert.New(t)
    81  
    82  	key, err := crypto.GenerateKey()
    83  	assert.NoError(err)
    84  
    85  	addr := crypto.PubkeyToAddress(key.PublicKey)
    86  	cfgJson, err := fundAddressByGenesis([]common.Address{addr})
    87  	assert.NoError(err)
    88  
    89  	_, vm, _, sender := GenesisVM(t, true, cfgJson, "", "")
    90  	defer func() {
    91  		err := vm.Shutdown(context.Background())
    92  		assert.NoError(err)
    93  	}()
    94  	vm.txPool.SetGasPrice(common.Big1)
    95  	vm.txPool.SetMinFee(common.Big0)
    96  
    97  	// create eth txes
    98  	ethTxs := getValidTxs(key, 3, common.Big1)
    99  
   100  	var wg sync.WaitGroup
   101  	var wg2 sync.WaitGroup
   102  	wg.Add(2)
   103  	wg2.Add(1)
   104  	sender.CantSendAppGossip = false
   105  	seen := 0
   106  	sender.SendAppGossipF = func(_ context.Context, gossipedBytes []byte) error {
   107  		if seen == 0 {
   108  			notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes)
   109  			assert.NoError(err)
   110  
   111  			requestMsg, ok := notifyMsgIntf.(message.TxsGossip)
   112  			assert.True(ok)
   113  			assert.NotEmpty(requestMsg.Txs)
   114  
   115  			txs := make([]*types.Transaction, 0)
   116  			assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs))
   117  			assert.Len(txs, 2)
   118  			assert.ElementsMatch(
   119  				[]common.Hash{ethTxs[0].Hash(), ethTxs[1].Hash()},
   120  				[]common.Hash{txs[0].Hash(), txs[1].Hash()},
   121  			)
   122  			seen++
   123  			wg2.Done()
   124  		} else if seen == 1 {
   125  			notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes)
   126  			assert.NoError(err)
   127  
   128  			requestMsg, ok := notifyMsgIntf.(message.TxsGossip)
   129  			assert.True(ok)
   130  			assert.NotEmpty(requestMsg.Txs)
   131  
   132  			txs := make([]*types.Transaction, 0)
   133  			assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs))
   134  			assert.Len(txs, 1)
   135  			assert.Equal(ethTxs[2].Hash(), txs[0].Hash())
   136  
   137  			seen++
   138  		} else {
   139  			t.Fatal("should not be seen 3 times")
   140  		}
   141  		wg.Done()
   142  		return nil
   143  	}
   144  
   145  	// Notify VM about eth txs
   146  	errs := vm.txPool.AddRemotesSync(ethTxs[:2])
   147  	for _, err := range errs {
   148  		assert.NoError(err, "failed adding subnet-evm tx to mempool")
   149  	}
   150  
   151  	// Gossip txs again (shouldn't gossip hashes)
   152  	attemptAwait(t, &wg2, 5*time.Second) // wait until reorg processed
   153  	assert.NoError(vm.gossiper.GossipTxs(ethTxs[:2]))
   154  
   155  	errs = vm.txPool.AddRemotesSync(ethTxs)
   156  	assert.Contains(errs[0].Error(), "already known")
   157  	assert.Contains(errs[1].Error(), "already known")
   158  	assert.NoError(errs[2], "failed adding subnet-evm tx to mempool")
   159  
   160  	attemptAwait(t, &wg, 5*time.Second)
   161  }
   162  
   163  // show that locally issued eth txs are chunked correctly
   164  func TestMempoolTxsAddedTxsGossipedAfterActivationChunking(t *testing.T) {
   165  	t.Skip("FLAKY")
   166  	assert := assert.New(t)
   167  
   168  	key, err := crypto.GenerateKey()
   169  	assert.NoError(err)
   170  
   171  	addr := crypto.PubkeyToAddress(key.PublicKey)
   172  
   173  	cfgJson, err := fundAddressByGenesis([]common.Address{addr})
   174  	assert.NoError(err)
   175  
   176  	_, vm, _, sender := GenesisVM(t, true, cfgJson, "", "")
   177  	defer func() {
   178  		err := vm.Shutdown(context.Background())
   179  		assert.NoError(err)
   180  	}()
   181  	vm.txPool.SetGasPrice(common.Big1)
   182  	vm.txPool.SetMinFee(common.Big0)
   183  
   184  	// create eth txes
   185  	txs := getValidTxs(key, 100, common.Big1)
   186  
   187  	var wg sync.WaitGroup
   188  	wg.Add(2)
   189  	sender.CantSendAppGossip = false
   190  	seen := map[common.Hash]struct{}{}
   191  	sender.SendAppGossipF = func(_ context.Context, gossipedBytes []byte) error {
   192  		notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes)
   193  		assert.NoError(err)
   194  
   195  		requestMsg, ok := notifyMsgIntf.(message.TxsGossip)
   196  		assert.True(ok)
   197  		assert.NotEmpty(requestMsg.Txs)
   198  
   199  		txs := make([]*types.Transaction, 0)
   200  		assert.NoError(rlp.DecodeBytes(requestMsg.Txs, &txs))
   201  		for _, tx := range txs {
   202  			seen[tx.Hash()] = struct{}{}
   203  		}
   204  		wg.Done()
   205  		return nil
   206  	}
   207  
   208  	// Notify VM about eth txs
   209  	errs := vm.txPool.AddRemotesSync(txs)
   210  	for _, err := range errs {
   211  		assert.NoError(err, "failed adding subnet-evm tx to mempool")
   212  	}
   213  
   214  	attemptAwait(t, &wg, 5*time.Second)
   215  
   216  	for _, tx := range txs {
   217  		_, ok := seen[tx.Hash()]
   218  		assert.True(ok, "missing hash: %v", tx.Hash())
   219  	}
   220  }
   221  
   222  // show that a geth tx discovered from gossip is requested to the same node that
   223  // gossiped it
   224  func TestMempoolTxsAppGossipHandling(t *testing.T) {
   225  	t.Skip("FLAKY")
   226  	assert := assert.New(t)
   227  
   228  	key, err := crypto.GenerateKey()
   229  	assert.NoError(err)
   230  
   231  	addr := crypto.PubkeyToAddress(key.PublicKey)
   232  
   233  	cfgJson, err := fundAddressByGenesis([]common.Address{addr})
   234  	assert.NoError(err)
   235  
   236  	_, vm, _, sender := GenesisVM(t, true, cfgJson, "", "")
   237  	defer func() {
   238  		err := vm.Shutdown(context.Background())
   239  		assert.NoError(err)
   240  	}()
   241  	vm.txPool.SetGasPrice(common.Big1)
   242  	vm.txPool.SetMinFee(common.Big0)
   243  
   244  	var (
   245  		wg          sync.WaitGroup
   246  		txRequested bool
   247  	)
   248  	sender.CantSendAppGossip = false
   249  	sender.SendAppRequestF = func(context.Context, set.Set[ids.NodeID], uint32, []byte) error {
   250  		txRequested = true
   251  		return nil
   252  	}
   253  	wg.Add(1)
   254  	sender.SendAppGossipF = func(context.Context, []byte) error {
   255  		wg.Done()
   256  		return nil
   257  	}
   258  
   259  	// prepare a tx
   260  	tx := getValidTxs(key, 1, common.Big1)[0]
   261  
   262  	// show that unknown subnet-evm hashes is requested
   263  	txBytes, err := rlp.EncodeToBytes([]*types.Transaction{tx})
   264  	assert.NoError(err)
   265  	msg := message.TxsGossip{
   266  		Txs: txBytes,
   267  	}
   268  	msgBytes, err := message.BuildGossipMessage(vm.networkCodec, msg)
   269  	assert.NoError(err)
   270  
   271  	nodeID := ids.GenerateTestNodeID()
   272  	err = vm.AppGossip(context.Background(), nodeID, msgBytes)
   273  	assert.NoError(err)
   274  	assert.False(txRequested, "tx should not be requested")
   275  
   276  	// wait for transaction to be re-gossiped
   277  	attemptAwait(t, &wg, 5*time.Second)
   278  }
   279  
   280  func TestMempoolTxsRegossipSingleAccount(t *testing.T) {
   281  	assert := assert.New(t)
   282  
   283  	key, err := crypto.GenerateKey()
   284  	assert.NoError(err)
   285  
   286  	addr := crypto.PubkeyToAddress(key.PublicKey)
   287  
   288  	cfgJson, err := fundAddressByGenesis([]common.Address{addr})
   289  	assert.NoError(err)
   290  
   291  	_, vm, _, _ := GenesisVM(t, true, cfgJson, `{"local-txs-enabled":true}`, "")
   292  	defer func() {
   293  		err := vm.Shutdown(context.Background())
   294  		assert.NoError(err)
   295  	}()
   296  	vm.txPool.SetGasPrice(common.Big1)
   297  	vm.txPool.SetMinFee(common.Big0)
   298  
   299  	// create eth txes
   300  	txs := getValidTxs(key, 10, big.NewInt(226*params.GWei))
   301  
   302  	// Notify VM about eth txs
   303  	errs := vm.txPool.AddRemotesSync(txs)
   304  	for _, err := range errs {
   305  		assert.NoError(err, "failed adding subnet-evm tx to remote mempool")
   306  	}
   307  
   308  	// Only 1 transaction will be regossiped for an address (should be lowest
   309  	// nonce)
   310  	pushNetwork := vm.gossiper.(*pushGossiper)
   311  	queued := pushNetwork.queueRegossipTxs()
   312  	assert.Len(queued, 1, "unexpected length of queued txs")
   313  	assert.Equal(txs[0].Hash(), queued[0].Hash())
   314  }
   315  
   316  func TestMempoolTxsRegossip(t *testing.T) {
   317  	assert := assert.New(t)
   318  
   319  	keys := make([]*ecdsa.PrivateKey, 20)
   320  	addrs := make([]common.Address, 20)
   321  	for i := 0; i < 20; i++ {
   322  		key, err := crypto.GenerateKey()
   323  		assert.NoError(err)
   324  		keys[i] = key
   325  		addrs[i] = crypto.PubkeyToAddress(key.PublicKey)
   326  	}
   327  
   328  	cfgJson, err := fundAddressByGenesis(addrs)
   329  	assert.NoError(err)
   330  
   331  	_, vm, _, _ := GenesisVM(t, true, cfgJson, `{"local-txs-enabled":true}`, "")
   332  	defer func() {
   333  		err := vm.Shutdown(context.Background())
   334  		assert.NoError(err)
   335  	}()
   336  	vm.txPool.SetGasPrice(common.Big1)
   337  	vm.txPool.SetMinFee(common.Big0)
   338  
   339  	// create eth txes
   340  	ethTxs := make([]*types.Transaction, 20)
   341  	ethTxHashes := make([]common.Hash, 20)
   342  	for i := 0; i < 20; i++ {
   343  		txs := getValidTxs(keys[i], 1, big.NewInt(226*params.GWei))
   344  		tx := txs[0]
   345  		ethTxs[i] = tx
   346  		ethTxHashes[i] = tx.Hash()
   347  	}
   348  
   349  	// Notify VM about eth txs
   350  	errs := vm.txPool.AddRemotesSync(ethTxs[:10])
   351  	for _, err := range errs {
   352  		assert.NoError(err, "failed adding subnet-evm tx to remote mempool")
   353  	}
   354  	errs = vm.txPool.AddLocals(ethTxs[10:])
   355  	for _, err := range errs {
   356  		assert.NoError(err, "failed adding subnet-evm tx to local mempool")
   357  	}
   358  
   359  	// We expect 16 transactions (the default max number of transactions to
   360  	// regossip) comprised of 10 local txs and 5 remote txs (we prioritize local
   361  	// txs over remote).
   362  	pushNetwork := vm.gossiper.(*pushGossiper)
   363  	queued := pushNetwork.queueRegossipTxs()
   364  	assert.Len(queued, 16, "unexpected length of queued txs")
   365  
   366  	// Confirm queued transactions (should be ordered based on
   367  	// timestamp submitted, with local priorized over remote)
   368  	queuedTxHashes := make([]common.Hash, 16)
   369  	for i, tx := range queued {
   370  		queuedTxHashes[i] = tx.Hash()
   371  	}
   372  	assert.ElementsMatch(queuedTxHashes[:10], ethTxHashes[10:], "missing local transactions")
   373  
   374  	// NOTE: We don't care which remote transactions are included in this test
   375  	// (due to the non-deterministic way pending transactions are surfaced, this can be difficult
   376  	// to assert as well).
   377  }
   378  
   379  func TestMempoolTxsPriorityRegossip(t *testing.T) {
   380  	assert := assert.New(t)
   381  
   382  	key, err := crypto.GenerateKey()
   383  	assert.NoError(err)
   384  	addr := crypto.PubkeyToAddress(key.PublicKey)
   385  
   386  	key2, err := crypto.GenerateKey()
   387  	assert.NoError(err)
   388  	addr2 := crypto.PubkeyToAddress(key2.PublicKey)
   389  
   390  	cfgJson, err := fundAddressByGenesis([]common.Address{addr, addr2})
   391  	assert.NoError(err)
   392  
   393  	cfg := fmt.Sprintf(`{"local-txs-enabled":true,"priority-regossip-addresses":["%s"]}`, addr)
   394  	_, vm, _, _ := GenesisVM(t, true, cfgJson, cfg, "")
   395  	defer func() {
   396  		err := vm.Shutdown(context.Background())
   397  		assert.NoError(err)
   398  	}()
   399  	vm.txPool.SetGasPrice(common.Big1)
   400  	vm.txPool.SetMinFee(common.Big0)
   401  
   402  	// create eth txes
   403  	txs := getValidTxs(key, 10, big.NewInt(226*params.GWei))
   404  	txs2 := getValidTxs(key2, 10, big.NewInt(226*params.GWei))
   405  
   406  	// Notify VM about eth txs
   407  	for _, err := range vm.txPool.AddRemotesSync(txs) {
   408  		assert.NoError(err, "failed adding subnet-evm tx to remote mempool")
   409  	}
   410  	for _, err := range vm.txPool.AddRemotesSync(txs2) {
   411  		assert.NoError(err, "failed adding subnet-evm tx 2 to remote mempool")
   412  	}
   413  
   414  	// 10 transactions will be regossiped for a priority address (others ignored)
   415  	pushNetwork := vm.gossiper.(*pushGossiper)
   416  	queued := pushNetwork.queuePriorityRegossipTxs()
   417  	assert.Len(queued, 10, "unexpected length of queued txs")
   418  	assert.ElementsMatch(txs, queued)
   419  }