github.com/dim4egster/coreth@v0.10.2/plugin/evm/gossiper_atomic_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  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/dim4egster/qmallgo/ids"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	"github.com/dim4egster/coreth/plugin/evm/message"
    16  )
    17  
    18  // locally issued txs should be gossiped
    19  func TestMempoolAtmTxsIssueTxAndGossiping(t *testing.T) {
    20  	assert := assert.New(t)
    21  
    22  	_, vm, _, sharedMemory, sender := GenesisVM(t, true, "", "", "")
    23  	defer func() {
    24  		assert.NoError(vm.Shutdown())
    25  	}()
    26  
    27  	// Create conflicting transactions
    28  	importTxs := createImportTxOptions(t, vm, sharedMemory)
    29  	tx, conflictingTx := importTxs[0], importTxs[1]
    30  
    31  	var gossiped int
    32  	var gossipedLock sync.Mutex // needed to prevent race
    33  	sender.CantSendAppGossip = false
    34  	sender.SendAppGossipF = func(gossipedBytes []byte) error {
    35  		gossipedLock.Lock()
    36  		defer gossipedLock.Unlock()
    37  
    38  		notifyMsgIntf, err := message.ParseGossipMessage(vm.networkCodec, gossipedBytes)
    39  		assert.NoError(err)
    40  
    41  		requestMsg, ok := notifyMsgIntf.(message.AtomicTxGossip)
    42  		assert.NotEmpty(requestMsg.Tx)
    43  		assert.True(ok)
    44  
    45  		txg := Tx{}
    46  		_, err = Codec.Unmarshal(requestMsg.Tx, &txg)
    47  		assert.NoError(err)
    48  		unsignedBytes, err := Codec.Marshal(codecVersion, &txg.UnsignedAtomicTx)
    49  		assert.NoError(err)
    50  		txg.Initialize(unsignedBytes, requestMsg.Tx)
    51  		assert.Equal(tx.ID(), txg.ID())
    52  		gossiped++
    53  		return nil
    54  	}
    55  
    56  	// Optimistically gossip raw tx
    57  	assert.NoError(vm.issueTx(tx, true /*=local*/))
    58  	time.Sleep(waitBlockTime * 3)
    59  	gossipedLock.Lock()
    60  	assert.Equal(1, gossiped)
    61  	gossipedLock.Unlock()
    62  
    63  	// Test hash on retry
    64  	assert.NoError(vm.gossiper.GossipAtomicTxs([]*Tx{tx}))
    65  	gossipedLock.Lock()
    66  	assert.Equal(1, gossiped)
    67  	gossipedLock.Unlock()
    68  
    69  	// Attempt to gossip conflicting tx
    70  	assert.ErrorIs(vm.issueTx(conflictingTx, true /*=local*/), errConflictingAtomicTx)
    71  	gossipedLock.Lock()
    72  	assert.Equal(1, gossiped)
    73  	gossipedLock.Unlock()
    74  }
    75  
    76  // show that a txID discovered from gossip is requested to the same node only if
    77  // the txID is unknown
    78  func TestMempoolAtmTxsAppGossipHandling(t *testing.T) {
    79  	assert := assert.New(t)
    80  
    81  	_, vm, _, sharedMemory, sender := GenesisVM(t, true, "", "", "")
    82  	defer func() {
    83  		assert.NoError(vm.Shutdown())
    84  	}()
    85  
    86  	nodeID := ids.GenerateTestNodeID()
    87  
    88  	var (
    89  		txGossiped     int
    90  		txGossipedLock sync.Mutex
    91  		txRequested    bool
    92  	)
    93  	sender.CantSendAppGossip = false
    94  	sender.SendAppGossipF = func(_ []byte) error {
    95  		txGossipedLock.Lock()
    96  		defer txGossipedLock.Unlock()
    97  
    98  		txGossiped++
    99  		return nil
   100  	}
   101  	sender.SendAppRequestF = func(_ ids.NodeIDSet, _ uint32, _ []byte) error {
   102  		txRequested = true
   103  		return nil
   104  	}
   105  
   106  	// Create conflicting transactions
   107  	importTxs := createImportTxOptions(t, vm, sharedMemory)
   108  	tx, conflictingTx := importTxs[0], importTxs[1]
   109  
   110  	// gossip tx and check it is accepted and gossiped
   111  	msg := message.AtomicTxGossip{
   112  		Tx: tx.SignedBytes(),
   113  	}
   114  	msgBytes, err := message.BuildGossipMessage(vm.networkCodec, msg)
   115  	assert.NoError(err)
   116  
   117  	// show that no txID is requested
   118  	assert.NoError(vm.AppGossip(nodeID, msgBytes))
   119  	time.Sleep(waitBlockTime * 3)
   120  
   121  	assert.False(txRequested, "tx should not have been requested")
   122  	txGossipedLock.Lock()
   123  	assert.Equal(1, txGossiped, "tx should have been gossiped")
   124  	txGossipedLock.Unlock()
   125  	assert.True(vm.mempool.has(tx.ID()))
   126  
   127  	// show that tx is not re-gossiped
   128  	assert.NoError(vm.AppGossip(nodeID, msgBytes))
   129  	txGossipedLock.Lock()
   130  	assert.Equal(1, txGossiped, "tx should have only been gossiped once")
   131  	txGossipedLock.Unlock()
   132  
   133  	// show that conflicting tx is not added to mempool
   134  	msg = message.AtomicTxGossip{
   135  		Tx: conflictingTx.SignedBytes(),
   136  	}
   137  	msgBytes, err = message.BuildGossipMessage(vm.networkCodec, msg)
   138  	assert.NoError(err)
   139  	assert.NoError(vm.AppGossip(nodeID, msgBytes))
   140  	assert.False(txRequested, "tx should not have been requested")
   141  	txGossipedLock.Lock()
   142  	assert.Equal(1, txGossiped, "tx should not have been gossiped")
   143  	txGossipedLock.Unlock()
   144  	assert.False(vm.mempool.has(conflictingTx.ID()), "conflicting tx should not be in the atomic mempool")
   145  }
   146  
   147  // show that txs already marked as invalid are not re-requested on gossiping
   148  func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) {
   149  	t.Skip("FLAKY")
   150  	assert := assert.New(t)
   151  
   152  	_, vm, _, sharedMemory, sender := GenesisVM(t, true, "", "", "")
   153  	defer func() {
   154  		assert.NoError(vm.Shutdown())
   155  	}()
   156  	mempool := vm.mempool
   157  
   158  	var (
   159  		txGossiped     int
   160  		txGossipedLock sync.Mutex
   161  		txRequested    bool
   162  	)
   163  	sender.CantSendAppGossip = false
   164  	sender.SendAppGossipF = func(_ []byte) error {
   165  		txGossipedLock.Lock()
   166  		defer txGossipedLock.Unlock()
   167  
   168  		txGossiped++
   169  		return nil
   170  	}
   171  	sender.SendAppRequestF = func(ids.NodeIDSet, uint32, []byte) error {
   172  		txRequested = true
   173  		return nil
   174  	}
   175  
   176  	// Create a transaction and mark it as invalid by discarding it
   177  	importTxs := createImportTxOptions(t, vm, sharedMemory)
   178  	tx, conflictingTx := importTxs[0], importTxs[1]
   179  	txID := tx.ID()
   180  
   181  	mempool.AddTx(tx)
   182  	mempool.NextTx()
   183  	mempool.DiscardCurrentTx(txID)
   184  
   185  	// Check the mempool does not contain the discarded transaction
   186  	assert.False(mempool.has(txID))
   187  
   188  	// Gossip the transaction to the VM and ensure that it is not added to the mempool
   189  	// and is not re-gossipped.
   190  	nodeID := ids.GenerateTestNodeID()
   191  	msg := message.AtomicTxGossip{
   192  		Tx: tx.SignedBytes(),
   193  	}
   194  	msgBytes, err := message.BuildGossipMessage(vm.networkCodec, msg)
   195  	assert.NoError(err)
   196  
   197  	assert.NoError(vm.AppGossip(nodeID, msgBytes))
   198  	assert.False(txRequested, "tx shouldn't be requested")
   199  	txGossipedLock.Lock()
   200  	assert.Zero(txGossiped, "tx should not have been gossiped")
   201  	txGossipedLock.Unlock()
   202  
   203  	assert.False(mempool.has(txID))
   204  
   205  	// Gossip the transaction that conflicts with the originally
   206  	// discarded tx and ensure it is accepted into the mempool and gossipped
   207  	// to the network.
   208  	nodeID = ids.GenerateTestNodeID()
   209  	msg = message.AtomicTxGossip{
   210  		Tx: conflictingTx.SignedBytes(),
   211  	}
   212  	msgBytes, err = message.BuildGossipMessage(vm.networkCodec, msg)
   213  	assert.NoError(err)
   214  
   215  	assert.NoError(vm.AppGossip(nodeID, msgBytes))
   216  	time.Sleep(waitBlockTime * 3)
   217  	assert.False(txRequested, "tx shouldn't be requested")
   218  	txGossipedLock.Lock()
   219  	assert.Equal(1, txGossiped, "conflicting tx should have been gossiped")
   220  	txGossipedLock.Unlock()
   221  
   222  	assert.False(mempool.has(txID))
   223  	assert.True(mempool.has(conflictingTx.ID()))
   224  }