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 }