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 = ¶ms.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 }