github.com/theQRL/go-zond@v0.2.1/zond/handler_zond_test.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package zond 18 19 import ( 20 "fmt" 21 "math/big" 22 "testing" 23 "time" 24 25 "github.com/theQRL/go-zond/common" 26 "github.com/theQRL/go-zond/consensus/beacon" 27 "github.com/theQRL/go-zond/core" 28 "github.com/theQRL/go-zond/core/forkid" 29 "github.com/theQRL/go-zond/core/rawdb" 30 "github.com/theQRL/go-zond/core/types" 31 "github.com/theQRL/go-zond/core/vm" 32 "github.com/theQRL/go-zond/event" 33 "github.com/theQRL/go-zond/p2p" 34 "github.com/theQRL/go-zond/p2p/enode" 35 "github.com/theQRL/go-zond/params" 36 "github.com/theQRL/go-zond/zond/downloader" 37 "github.com/theQRL/go-zond/zond/protocols/zond" 38 ) 39 40 // testZondHandler is a mock event handler to listen for inbound network requests 41 // on the `zond` protocol and convert them into a more easily testable form. 42 type testZondHandler struct { 43 txAnnounces event.Feed 44 txBroadcasts event.Feed 45 } 46 47 func (h *testZondHandler) Chain() *core.BlockChain { panic("no backing chain") } 48 func (h *testZondHandler) TxPool() zond.TxPool { panic("no backing tx pool") } 49 func (h *testZondHandler) AcceptTxs() bool { return true } 50 func (h *testZondHandler) RunPeer(*zond.Peer, zond.Handler) error { panic("not used in tests") } 51 func (h *testZondHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") } 52 53 func (h *testZondHandler) Handle(peer *zond.Peer, packet zond.Packet) error { 54 switch packet := packet.(type) { 55 case *zond.NewPooledTransactionHashesPacket: 56 h.txAnnounces.Send(packet.Hashes) 57 return nil 58 59 case *zond.TransactionsPacket: 60 h.txBroadcasts.Send(([]*types.Transaction)(*packet)) 61 return nil 62 63 case *zond.PooledTransactionsResponse: 64 h.txBroadcasts.Send(([]*types.Transaction)(*packet)) 65 return nil 66 67 default: 68 panic(fmt.Sprintf("unexpected zond packet type in tests: %T", packet)) 69 } 70 } 71 72 // Tests that peers are correctly accepted (or rejected) based on the advertised 73 // fork IDs in the protocol handshake. 74 func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, zond.ETH68) } 75 76 func testForkIDSplit(t *testing.T, protocol uint) { 77 t.Parallel() 78 79 var ( 80 engine = beacon.NewFaker() 81 82 configNoFork = ¶ms.ChainConfig{} 83 configProFork = ¶ms.ChainConfig{} 84 dbNoFork = rawdb.NewMemoryDatabase() 85 dbProFork = rawdb.NewMemoryDatabase() 86 87 gspecNoFork = &core.Genesis{Config: configNoFork} 88 gspecProFork = &core.Genesis{Config: configProFork} 89 90 chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, gspecNoFork, engine, vm.Config{}, nil) 91 chainProFork, _ = core.NewBlockChain(dbProFork, nil, gspecProFork, engine, vm.Config{}, nil) 92 93 _, blocksNoFork, _ = core.GenerateChainWithGenesis(gspecNoFork, engine, 2, nil) 94 _, blocksProFork, _ = core.GenerateChainWithGenesis(gspecProFork, engine, 2, nil) 95 96 zondNoFork, _ = newHandler(&handlerConfig{ 97 Database: dbNoFork, 98 Chain: chainNoFork, 99 TxPool: newTestTxPool(), 100 Network: 1, 101 Sync: downloader.FullSync, 102 BloomCache: 1, 103 }) 104 zondProFork, _ = newHandler(&handlerConfig{ 105 Database: dbProFork, 106 Chain: chainProFork, 107 TxPool: newTestTxPool(), 108 Network: 1, 109 Sync: downloader.FullSync, 110 BloomCache: 1, 111 }) 112 ) 113 zondNoFork.Start(1000) 114 zondProFork.Start(1000) 115 116 // Clean up everything after ourselves 117 defer chainNoFork.Stop() 118 defer chainProFork.Stop() 119 120 defer zondNoFork.Stop() 121 defer zondProFork.Stop() 122 123 // Both nodes should allow the other to connect (same genesis, next fork is the same) 124 p2pNoFork, p2pProFork := p2p.MsgPipe() 125 defer p2pNoFork.Close() 126 defer p2pProFork.Close() 127 128 peerNoFork := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pNoFork), p2pNoFork, nil) 129 peerProFork := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pProFork), p2pProFork, nil) 130 defer peerNoFork.Close() 131 defer peerProFork.Close() 132 133 errc := make(chan error, 2) 134 go func(errc chan error) { 135 errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil }) 136 }(errc) 137 go func(errc chan error) { 138 errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil }) 139 }(errc) 140 141 for i := 0; i < 2; i++ { 142 select { 143 case err := <-errc: 144 if err != nil { 145 t.Fatalf("frontier nofork <-> profork failed: %v", err) 146 } 147 case <-time.After(250 * time.Millisecond): 148 t.Fatalf("frontier nofork <-> profork handler timeout") 149 } 150 } 151 // Progress into Homestead. Fork's match, so we don't care what the future holds 152 chainNoFork.InsertChain(blocksNoFork[:1]) 153 chainProFork.InsertChain(blocksProFork[:1]) 154 155 p2pNoFork, p2pProFork = p2p.MsgPipe() 156 defer p2pNoFork.Close() 157 defer p2pProFork.Close() 158 159 peerNoFork = zond.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil) 160 peerProFork = zond.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil) 161 defer peerNoFork.Close() 162 defer peerProFork.Close() 163 164 errc = make(chan error, 2) 165 go func(errc chan error) { 166 errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil }) 167 }(errc) 168 go func(errc chan error) { 169 errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil }) 170 }(errc) 171 172 for i := 0; i < 2; i++ { 173 select { 174 case err := <-errc: 175 if err != nil { 176 t.Fatalf("homestead nofork <-> profork failed: %v", err) 177 } 178 case <-time.After(250 * time.Millisecond): 179 t.Fatalf("homestead nofork <-> profork handler timeout") 180 } 181 } 182 // NOTE(rgeraldes24): revisit upon new fork 183 /* 184 // Progress into Spurious. Forks mismatch, signalling differing chains, reject 185 chainNoFork.InsertChain(blocksNoFork[1:2]) 186 chainProFork.InsertChain(blocksProFork[1:2]) 187 188 p2pNoFork, p2pProFork = p2p.MsgPipe() 189 defer p2pNoFork.Close() 190 defer p2pProFork.Close() 191 192 peerNoFork = zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pNoFork), p2pNoFork, nil) 193 peerProFork = zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pProFork), p2pProFork, nil) 194 defer peerNoFork.Close() 195 defer peerProFork.Close() 196 197 errc = make(chan error, 2) 198 go func(errc chan error) { 199 errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil }) 200 }(errc) 201 go func(errc chan error) { 202 errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil }) 203 }(errc) 204 205 var successes int 206 for i := 0; i < 2; i++ { 207 select { 208 case err := <-errc: 209 if err == nil { 210 successes++ 211 if successes == 2 { // Only one side disconnects 212 t.Fatalf("fork ID rejection didn't happen") 213 } 214 } 215 case <-time.After(250 * time.Millisecond): 216 t.Fatalf("split peers not rejected") 217 } 218 } 219 */ 220 } 221 222 // Tests that received transactions are added to the local pool. 223 func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, zond.ETH68) } 224 225 func testRecvTransactions(t *testing.T, protocol uint) { 226 t.Parallel() 227 228 // Create a message handler, configure it to accept transactions and watch them 229 handler := newTestHandler() 230 defer handler.close() 231 232 handler.handler.synced.Store(true) // mark synced to accept transactions 233 234 txs := make(chan core.NewTxsEvent) 235 sub := handler.txpool.SubscribeTransactions(txs) 236 defer sub.Unsubscribe() 237 238 // Create a source peer to send messages through and a sink handler to receive them 239 p2pSrc, p2pSink := p2p.MsgPipe() 240 defer p2pSrc.Close() 241 defer p2pSink.Close() 242 243 src := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pSrc), p2pSrc, handler.txpool) 244 sink := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pSink), p2pSink, handler.txpool) 245 defer src.Close() 246 defer sink.Close() 247 248 go handler.handler.runZondPeer(sink, func(peer *zond.Peer) error { 249 return zond.Handle((*zondHandler)(handler.handler), peer) 250 }) 251 // Run the handshake locally to avoid spinning up a source handler 252 var ( 253 genesis = handler.chain.Genesis() 254 head = handler.chain.CurrentBlock() 255 ) 256 if err := src.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { 257 t.Fatalf("failed to run protocol handshake") 258 } 259 // Send the transaction to the sink and verify that it's added to the tx pool 260 tx := types.NewTx(&types.DynamicFeeTx{ 261 Nonce: 0, 262 To: &common.Address{}, 263 Value: big.NewInt(0), 264 Gas: 100000, 265 GasFeeCap: big.NewInt(0), 266 Data: nil, 267 }) 268 tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey) 269 270 if err := src.SendTransactions([]*types.Transaction{tx}); err != nil { 271 t.Fatalf("failed to send transaction: %v", err) 272 } 273 select { 274 case event := <-txs: 275 if len(event.Txs) != 1 { 276 t.Errorf("wrong number of added transactions: got %d, want 1", len(event.Txs)) 277 } else if event.Txs[0].Hash() != tx.Hash() { 278 t.Errorf("added wrong tx hash: got %v, want %v", event.Txs[0].Hash(), tx.Hash()) 279 } 280 case <-time.After(2 * time.Second): 281 t.Errorf("no NewTxsEvent received within 2 seconds") 282 } 283 } 284 285 // This test checks that pending transactions are sent. 286 func TestSendTransactions68(t *testing.T) { testSendTransactions(t, zond.ETH68) } 287 288 func testSendTransactions(t *testing.T, protocol uint) { 289 t.Parallel() 290 291 // Create a message handler and fill the pool with big transactions 292 handler := newTestHandler() 293 defer handler.close() 294 295 insert := make([]*types.Transaction, 100) 296 for nonce := range insert { 297 tx := types.NewTx(&types.DynamicFeeTx{ 298 Nonce: uint64(nonce), 299 To: &common.Address{}, 300 Value: big.NewInt(0), 301 Gas: 100000, 302 GasFeeCap: big.NewInt(0), 303 Data: make([]byte, 10240), 304 }) 305 tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey) 306 insert[nonce] = tx 307 } 308 go handler.txpool.Add(insert, false, false) // Need goroutine to not block on feed 309 time.Sleep(250 * time.Millisecond) // Wait until tx events get out of the system (can't use events, tx broadcaster races with peer join) 310 311 // Create a source handler to send messages through and a sink peer to receive them 312 p2pSrc, p2pSink := p2p.MsgPipe() 313 defer p2pSrc.Close() 314 defer p2pSink.Close() 315 316 src := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pSrc), p2pSrc, handler.txpool) 317 sink := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pSink), p2pSink, handler.txpool) 318 defer src.Close() 319 defer sink.Close() 320 321 go handler.handler.runZondPeer(src, func(peer *zond.Peer) error { 322 return zond.Handle((*zondHandler)(handler.handler), peer) 323 }) 324 // Run the handshake locally to avoid spinning up a source handler 325 var ( 326 genesis = handler.chain.Genesis() 327 head = handler.chain.CurrentBlock() 328 ) 329 if err := sink.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil { 330 t.Fatalf("failed to run protocol handshake") 331 } 332 // After the handshake completes, the source handler should stream the sink 333 // the transactions, subscribe to all inbound network events 334 backend := new(testZondHandler) 335 336 anns := make(chan []common.Hash) 337 annSub := backend.txAnnounces.Subscribe(anns) 338 defer annSub.Unsubscribe() 339 340 bcasts := make(chan []*types.Transaction) 341 bcastSub := backend.txBroadcasts.Subscribe(bcasts) 342 defer bcastSub.Unsubscribe() 343 344 go zond.Handle(backend, sink) 345 346 // Make sure we get all the transactions on the correct channels 347 seen := make(map[common.Hash]struct{}) 348 for len(seen) < len(insert) { 349 switch protocol { 350 case 68: 351 select { 352 case hashes := <-anns: 353 for _, hash := range hashes { 354 if _, ok := seen[hash]; ok { 355 t.Errorf("duplicate transaction announced: %x", hash) 356 } 357 seen[hash] = struct{}{} 358 } 359 case <-bcasts: 360 t.Errorf("initial tx broadcast received on post zond/66") 361 } 362 363 default: 364 panic("unsupported protocol, please extend test") 365 } 366 } 367 for _, tx := range insert { 368 if _, ok := seen[tx.Hash()]; !ok { 369 t.Errorf("missing transaction: %x", tx.Hash()) 370 } 371 } 372 } 373 374 // Tests that transactions get propagated to all attached peers, either via direct 375 // broadcasts or via announcements/retrievals. 376 func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, zond.ETH68) } 377 378 func testTransactionPropagation(t *testing.T, protocol uint) { 379 t.Parallel() 380 381 // Create a source handler to send transactions from and a number of sinks 382 // to receive them. We need multiple sinks since a one-to-one peering would 383 // broadcast all transactions without announcement. 384 source := newTestHandler() 385 source.handler.snapSync.Store(false) // Avoid requiring snap, otherwise some will be dropped below 386 defer source.close() 387 388 sinks := make([]*testHandler, 10) 389 for i := 0; i < len(sinks); i++ { 390 sinks[i] = newTestHandler() 391 defer sinks[i].close() 392 393 sinks[i].handler.synced.Store(true) // mark synced to accept transactions 394 } 395 // Interconnect all the sink handlers with the source handler 396 for i, sink := range sinks { 397 sink := sink // Closure for goroutine below 398 399 sourcePipe, sinkPipe := p2p.MsgPipe() 400 defer sourcePipe.Close() 401 defer sinkPipe.Close() 402 403 sourcePeer := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{byte(i + 1)}, "", nil, sourcePipe), sourcePipe, source.txpool) 404 sinkPeer := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, sink.txpool) 405 defer sourcePeer.Close() 406 defer sinkPeer.Close() 407 408 go source.handler.runZondPeer(sourcePeer, func(peer *zond.Peer) error { 409 return zond.Handle((*zondHandler)(source.handler), peer) 410 }) 411 go sink.handler.runZondPeer(sinkPeer, func(peer *zond.Peer) error { 412 return zond.Handle((*zondHandler)(sink.handler), peer) 413 }) 414 } 415 // Subscribe to all the transaction pools 416 txChs := make([]chan core.NewTxsEvent, len(sinks)) 417 for i := 0; i < len(sinks); i++ { 418 txChs[i] = make(chan core.NewTxsEvent, 1024) 419 420 sub := sinks[i].txpool.SubscribeTransactions(txChs[i]) 421 defer sub.Unsubscribe() 422 } 423 // Fill the source pool with transactions and wait for them at the sinks 424 txs := make([]*types.Transaction, 1024) 425 for nonce := range txs { 426 tx := types.NewTx(&types.DynamicFeeTx{ 427 Nonce: uint64(nonce), 428 To: &common.Address{}, 429 Value: big.NewInt(0), 430 Gas: 100000, 431 GasFeeCap: big.NewInt(0), 432 Data: nil, 433 }) 434 tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey) 435 436 txs[nonce] = tx 437 } 438 source.txpool.Add(txs, false, false) 439 440 // Iterate through all the sinks and ensure they all got the transactions 441 for i := range sinks { 442 for arrived, timeout := 0, false; arrived < len(txs) && !timeout; { 443 select { 444 case event := <-txChs[i]: 445 arrived += len(event.Txs) 446 case <-time.After(2 * time.Second): 447 t.Errorf("sink %d: transaction propagation timed out: have %d, want %d", i, arrived, len(txs)) 448 timeout = true 449 } 450 } 451 } 452 }