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