github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/les/test_helper.go (about) 1 // Copyright 2019 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 // This file contains some shares testing functionality, common to multiple 18 // different files and modules being tested. Client based network and Server 19 // based network can be created easily with available APIs. 20 21 package les 22 23 import ( 24 "context" 25 "crypto/rand" 26 "fmt" 27 "math/big" 28 "sync/atomic" 29 "testing" 30 "time" 31 32 "github.com/tirogen/go-ethereum/accounts/abi/bind" 33 "github.com/tirogen/go-ethereum/accounts/abi/bind/backends" 34 "github.com/tirogen/go-ethereum/common" 35 "github.com/tirogen/go-ethereum/common/mclock" 36 "github.com/tirogen/go-ethereum/consensus" 37 "github.com/tirogen/go-ethereum/consensus/ethash" 38 "github.com/tirogen/go-ethereum/contracts/checkpointoracle/contract" 39 "github.com/tirogen/go-ethereum/core" 40 "github.com/tirogen/go-ethereum/core/forkid" 41 "github.com/tirogen/go-ethereum/core/rawdb" 42 "github.com/tirogen/go-ethereum/core/txpool" 43 "github.com/tirogen/go-ethereum/core/types" 44 "github.com/tirogen/go-ethereum/crypto" 45 "github.com/tirogen/go-ethereum/eth/ethconfig" 46 "github.com/tirogen/go-ethereum/ethdb" 47 "github.com/tirogen/go-ethereum/event" 48 "github.com/tirogen/go-ethereum/les/checkpointoracle" 49 "github.com/tirogen/go-ethereum/les/flowcontrol" 50 vfs "github.com/tirogen/go-ethereum/les/vflux/server" 51 "github.com/tirogen/go-ethereum/light" 52 "github.com/tirogen/go-ethereum/p2p" 53 "github.com/tirogen/go-ethereum/p2p/enode" 54 "github.com/tirogen/go-ethereum/params" 55 ) 56 57 var ( 58 bankKey, _ = crypto.GenerateKey() 59 bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey) 60 bankFunds = big.NewInt(1_000_000_000_000_000_000) 61 62 userKey1, _ = crypto.GenerateKey() 63 userKey2, _ = crypto.GenerateKey() 64 userAddr1 = crypto.PubkeyToAddress(userKey1.PublicKey) 65 userAddr2 = crypto.PubkeyToAddress(userKey2.PublicKey) 66 67 testContractAddr common.Address 68 testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") 69 testContractCodeDeployed = testContractCode[16:] 70 testContractDeployed = uint64(2) 71 72 testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029") 73 74 // Checkpoint oracle relative fields 75 oracleAddr common.Address 76 signerKey, _ = crypto.GenerateKey() 77 signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey) 78 ) 79 80 var ( 81 // The block frequency for creating checkpoint(only used in test) 82 sectionSize = big.NewInt(128) 83 84 // The number of confirmations needed to generate a checkpoint(only used in test). 85 processConfirms = big.NewInt(1) 86 87 // The token bucket buffer limit for testing purpose. 88 testBufLimit = uint64(1000000) 89 90 // The buffer recharging speed for testing purpose. 91 testBufRecharge = uint64(1000) 92 ) 93 94 /* 95 contract test { 96 97 uint256[100] data; 98 99 function Put(uint256 addr, uint256 value) { 100 data[addr] = value; 101 } 102 103 function Get(uint256 addr) constant returns (uint256 value) { 104 return data[addr]; 105 } 106 } 107 */ 108 109 // prepare pre-commits specified number customized blocks into chain. 110 func prepare(n int, backend *backends.SimulatedBackend) { 111 var ( 112 ctx = context.Background() 113 signer = types.HomesteadSigner{} 114 ) 115 for i := 0; i < n; i++ { 116 switch i { 117 case 0: 118 // Builtin-block 119 // number: 1 120 // txs: 2 121 122 // deploy checkpoint contract 123 auth, _ := bind.NewKeyedTransactorWithChainID(bankKey, big.NewInt(1337)) 124 oracleAddr, _, _, _ = contract.DeployCheckpointOracle(auth, backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1)) 125 126 // bankUser transfers some ether to user1 127 nonce, _ := backend.PendingNonceAt(ctx, bankAddr) 128 tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) 129 backend.SendTransaction(ctx, tx) 130 case 1: 131 // Builtin-block 132 // number: 2 133 // txs: 4 134 135 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 136 userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1) 137 138 // bankUser transfers more ether to user1 139 tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) 140 backend.SendTransaction(ctx, tx1) 141 142 // user1 relays ether to user2 143 tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1_000_000_000_000_000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, userKey1) 144 backend.SendTransaction(ctx, tx2) 145 146 // user1 deploys a test contract 147 tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testContractCode), signer, userKey1) 148 backend.SendTransaction(ctx, tx3) 149 testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1) 150 151 // user1 deploys a event contract 152 tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(params.InitialBaseFee), testEventEmitterCode), signer, userKey1) 153 backend.SendTransaction(ctx, tx4) 154 case 2: 155 // Builtin-block 156 // number: 3 157 // txs: 2 158 159 // bankUser transfer some ether to signer 160 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 161 tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, bankKey) 162 backend.SendTransaction(ctx, tx1) 163 164 // invoke test contract 165 data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") 166 tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey) 167 backend.SendTransaction(ctx, tx2) 168 case 3: 169 // Builtin-block 170 // number: 4 171 // txs: 1 172 173 // invoke test contract 174 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 175 data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") 176 tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, big.NewInt(params.InitialBaseFee), data), signer, bankKey) 177 backend.SendTransaction(ctx, tx) 178 } 179 backend.Commit() 180 } 181 } 182 183 // testIndexers creates a set of indexers with specified params for testing purpose. 184 func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig, disablePruning bool) []*core.ChainIndexer { 185 var indexers [3]*core.ChainIndexer 186 indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms, disablePruning) 187 indexers[1] = core.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) 188 indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize, disablePruning) 189 // make bloomTrieIndexer as a child indexer of bloom indexer. 190 indexers[1].AddChildIndexer(indexers[2]) 191 return indexers[:] 192 } 193 194 func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *serverPeerSet, ulcServers []string, ulcFraction int) (*clientHandler, func()) { 195 var ( 196 evmux = new(event.TypeMux) 197 engine = ethash.NewFaker() 198 gspec = core.Genesis{ 199 Config: params.AllEthashProtocolChanges, 200 Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, 201 GasLimit: 100000000, 202 BaseFee: big.NewInt(params.InitialBaseFee), 203 } 204 oracle *checkpointoracle.CheckpointOracle 205 ) 206 genesis := gspec.MustCommit(db) 207 chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) 208 if indexers != nil { 209 checkpointConfig := ¶ms.CheckpointOracleConfig{ 210 Address: crypto.CreateAddress(bankAddr, 0), 211 Signers: []common.Address{signerAddr}, 212 Threshold: 1, 213 } 214 getLocal := func(index uint64) params.TrustedCheckpoint { 215 chtIndexer := indexers[0] 216 sectionHead := chtIndexer.SectionHead(index) 217 return params.TrustedCheckpoint{ 218 SectionIndex: index, 219 SectionHead: sectionHead, 220 CHTRoot: light.GetChtRoot(db, index, sectionHead), 221 BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), 222 } 223 } 224 oracle = checkpointoracle.New(checkpointConfig, getLocal) 225 } 226 client := &LightEthereum{ 227 lesCommons: lesCommons{ 228 genesis: genesis.Hash(), 229 config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId}, 230 chainConfig: params.AllEthashProtocolChanges, 231 iConfig: light.TestClientIndexerConfig, 232 chainDb: db, 233 oracle: oracle, 234 chainReader: chain, 235 closeCh: make(chan struct{}), 236 }, 237 peers: peers, 238 reqDist: odr.retriever.dist, 239 retriever: odr.retriever, 240 odr: odr, 241 engine: engine, 242 blockchain: chain, 243 eventMux: evmux, 244 merger: consensus.NewMerger(rawdb.NewMemoryDatabase()), 245 } 246 client.handler = newClientHandler(ulcServers, ulcFraction, nil, client) 247 248 if client.oracle != nil { 249 client.oracle.Start(backend) 250 } 251 client.handler.start() 252 return client.handler, func() { 253 client.handler.stop() 254 } 255 } 256 257 func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend, func()) { 258 var ( 259 gspec = core.Genesis{ 260 Config: params.AllEthashProtocolChanges, 261 Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, 262 GasLimit: 100000000, 263 BaseFee: big.NewInt(params.InitialBaseFee), 264 } 265 oracle *checkpointoracle.CheckpointOracle 266 ) 267 genesis := gspec.MustCommit(db) 268 269 // create a simulation backend and pre-commit several customized block to the database. 270 simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000) 271 prepare(blocks, simulation) 272 273 txpoolConfig := txpool.DefaultConfig 274 txpoolConfig.Journal = "" 275 txpool := txpool.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain()) 276 if indexers != nil { 277 checkpointConfig := ¶ms.CheckpointOracleConfig{ 278 Address: crypto.CreateAddress(bankAddr, 0), 279 Signers: []common.Address{signerAddr}, 280 Threshold: 1, 281 } 282 getLocal := func(index uint64) params.TrustedCheckpoint { 283 chtIndexer := indexers[0] 284 sectionHead := chtIndexer.SectionHead(index) 285 return params.TrustedCheckpoint{ 286 SectionIndex: index, 287 SectionHead: sectionHead, 288 CHTRoot: light.GetChtRoot(db, index, sectionHead), 289 BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), 290 } 291 } 292 oracle = checkpointoracle.New(checkpointConfig, getLocal) 293 } 294 server := &LesServer{ 295 lesCommons: lesCommons{ 296 genesis: genesis.Hash(), 297 config: ðconfig.Config{LightPeers: 100, NetworkId: NetworkId}, 298 chainConfig: params.AllEthashProtocolChanges, 299 iConfig: light.TestServerIndexerConfig, 300 chainDb: db, 301 chainReader: simulation.Blockchain(), 302 oracle: oracle, 303 closeCh: make(chan struct{}), 304 }, 305 peers: newClientPeerSet(), 306 servingQueue: newServingQueue(int64(time.Millisecond*10), 1), 307 defParams: flowcontrol.ServerParams{ 308 BufLimit: testBufLimit, 309 MinRecharge: testBufRecharge, 310 }, 311 fcManager: flowcontrol.NewClientManager(nil, clock), 312 } 313 server.costTracker, server.minCapacity = newCostTracker(db, server.config) 314 server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism. 315 server.clientPool = vfs.NewClientPool(db, testBufRecharge, defaultConnectedBias, clock, alwaysTrueFn) 316 server.clientPool.Start() 317 server.clientPool.SetLimits(10000, 10000) // Assign enough capacity for clientpool 318 server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }) 319 if server.oracle != nil { 320 server.oracle.Start(simulation) 321 } 322 server.servingQueue.setThreads(4) 323 server.handler.start() 324 closer := func() { server.Stop() } 325 return server.handler, simulation, closer 326 } 327 328 func alwaysTrueFn() bool { 329 return true 330 } 331 332 // testPeer is a simulated peer to allow testing direct network calls. 333 type testPeer struct { 334 cpeer *clientPeer 335 speer *serverPeer 336 337 net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging 338 app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side 339 } 340 341 // handshakeWithServer executes the handshake with the remote server peer. 342 func (p *testPeer) handshakeWithServer(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID) { 343 // It only works for the simulated client peer 344 if p.cpeer == nil { 345 t.Fatal("handshake for client peer only") 346 } 347 var sendList keyValueList 348 sendList = sendList.add("protocolVersion", uint64(p.cpeer.version)) 349 sendList = sendList.add("networkId", uint64(NetworkId)) 350 sendList = sendList.add("headTd", td) 351 sendList = sendList.add("headHash", head) 352 sendList = sendList.add("headNum", headNum) 353 sendList = sendList.add("genesisHash", genesis) 354 if p.cpeer.version >= lpv4 { 355 sendList = sendList.add("forkID", &forkID) 356 } 357 if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil { 358 t.Fatalf("status recv: %v", err) 359 } 360 if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil { 361 t.Fatalf("status send: %v", err) 362 } 363 } 364 365 // handshakeWithClient executes the handshake with the remote client peer. 366 func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, forkID forkid.ID, costList RequestCostList, recentTxLookup uint64) { 367 // It only works for the simulated client peer 368 if p.speer == nil { 369 t.Fatal("handshake for server peer only") 370 } 371 var sendList keyValueList 372 sendList = sendList.add("protocolVersion", uint64(p.speer.version)) 373 sendList = sendList.add("networkId", uint64(NetworkId)) 374 sendList = sendList.add("headTd", td) 375 sendList = sendList.add("headHash", head) 376 sendList = sendList.add("headNum", headNum) 377 sendList = sendList.add("genesisHash", genesis) 378 sendList = sendList.add("serveHeaders", nil) 379 sendList = sendList.add("serveChainSince", uint64(0)) 380 sendList = sendList.add("serveStateSince", uint64(0)) 381 sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4)) 382 sendList = sendList.add("txRelay", nil) 383 sendList = sendList.add("flowControl/BL", testBufLimit) 384 sendList = sendList.add("flowControl/MRR", testBufRecharge) 385 sendList = sendList.add("flowControl/MRC", costList) 386 if p.speer.version >= lpv4 { 387 sendList = sendList.add("forkID", &forkID) 388 sendList = sendList.add("recentTxLookup", recentTxLookup) 389 } 390 if err := p2p.ExpectMsg(p.app, StatusMsg, nil); err != nil { 391 t.Fatalf("status recv: %v", err) 392 } 393 if err := p2p.Send(p.app, StatusMsg, &sendList); err != nil { 394 t.Fatalf("status send: %v", err) 395 } 396 } 397 398 // close terminates the local side of the peer, notifying the remote protocol 399 // manager of termination. 400 func (p *testPeer) close() { 401 p.app.Close() 402 } 403 404 func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler, noInitAnnounce bool) (*testPeer, *testPeer, error) { 405 // Create a message pipe to communicate through 406 app, net := p2p.MsgPipe() 407 408 // Generate a random id and create the peer 409 var id enode.ID 410 rand.Read(id[:]) 411 412 peer1 := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) 413 peer2 := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), app) 414 415 // Start the peer on a new thread 416 errc1 := make(chan error, 1) 417 errc2 := make(chan error, 1) 418 go func() { 419 select { 420 case <-server.closeCh: 421 errc1 <- p2p.DiscQuitting 422 case errc1 <- server.handle(peer1): 423 } 424 }() 425 go func() { 426 select { 427 case <-client.closeCh: 428 errc2 <- p2p.DiscQuitting 429 case errc2 <- client.handle(peer2, noInitAnnounce): 430 } 431 }() 432 // Ensure the connection is established or exits when any error occurs 433 for { 434 select { 435 case err := <-errc1: 436 return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err) 437 case err := <-errc2: 438 return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err) 439 default: 440 } 441 if atomic.LoadUint32(&peer1.serving) == 1 && atomic.LoadUint32(&peer2.serving) == 1 { 442 break 443 } 444 time.Sleep(50 * time.Millisecond) 445 } 446 return &testPeer{cpeer: peer1, net: net, app: app}, &testPeer{speer: peer2, net: app, app: net}, nil 447 } 448 449 type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) 450 451 // testClient represents a client object for testing with necessary auxiliary fields. 452 type testClient struct { 453 clock mclock.Clock 454 db ethdb.Database 455 peer *testPeer 456 handler *clientHandler 457 458 chtIndexer *core.ChainIndexer 459 bloomIndexer *core.ChainIndexer 460 bloomTrieIndexer *core.ChainIndexer 461 } 462 463 // newRawPeer creates a new server peer connects to the server and do the handshake. 464 func (client *testClient) newRawPeer(t *testing.T, name string, version int, recentTxLookup uint64) (*testPeer, func(), <-chan error) { 465 // Create a message pipe to communicate through 466 app, net := p2p.MsgPipe() 467 468 // Generate a random id and create the peer 469 var id enode.ID 470 rand.Read(id[:]) 471 peer := newServerPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) 472 473 // Start the peer on a new thread 474 errCh := make(chan error, 1) 475 go func() { 476 select { 477 case <-client.handler.closeCh: 478 errCh <- p2p.DiscQuitting 479 case errCh <- client.handler.handle(peer, false): 480 } 481 }() 482 tp := &testPeer{ 483 app: app, 484 net: net, 485 speer: peer, 486 } 487 var ( 488 genesis = client.handler.backend.blockchain.Genesis() 489 head = client.handler.backend.blockchain.CurrentHeader() 490 td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64()) 491 ) 492 forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) 493 tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default 494 495 // Ensure the connection is established or exits when any error occurs 496 for { 497 select { 498 case <-errCh: 499 return nil, nil, nil 500 default: 501 } 502 if atomic.LoadUint32(&peer.serving) == 1 { 503 break 504 } 505 time.Sleep(50 * time.Millisecond) 506 } 507 closePeer := func() { 508 tp.speer.close() 509 tp.close() 510 } 511 return tp, closePeer, errCh 512 } 513 514 // testServer represents a server object for testing with necessary auxiliary fields. 515 type testServer struct { 516 clock mclock.Clock 517 backend *backends.SimulatedBackend 518 db ethdb.Database 519 peer *testPeer 520 handler *serverHandler 521 522 chtIndexer *core.ChainIndexer 523 bloomIndexer *core.ChainIndexer 524 bloomTrieIndexer *core.ChainIndexer 525 } 526 527 // newRawPeer creates a new client peer connects to the server and do the handshake. 528 func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*testPeer, func(), <-chan error) { 529 // Create a message pipe to communicate through 530 app, net := p2p.MsgPipe() 531 532 // Generate a random id and create the peer 533 var id enode.ID 534 rand.Read(id[:]) 535 peer := newClientPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) 536 537 // Start the peer on a new thread 538 errCh := make(chan error, 1) 539 go func() { 540 select { 541 case <-server.handler.closeCh: 542 errCh <- p2p.DiscQuitting 543 case errCh <- server.handler.handle(peer): 544 } 545 }() 546 tp := &testPeer{ 547 app: app, 548 net: net, 549 cpeer: peer, 550 } 551 var ( 552 genesis = server.handler.blockchain.Genesis() 553 head = server.handler.blockchain.CurrentHeader() 554 td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) 555 ) 556 forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64()) 557 tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID) 558 559 // Ensure the connection is established or exits when any error occurs 560 for { 561 select { 562 case <-errCh: 563 return nil, nil, nil 564 default: 565 } 566 if atomic.LoadUint32(&peer.serving) == 1 { 567 break 568 } 569 time.Sleep(50 * time.Millisecond) 570 } 571 closePeer := func() { 572 tp.cpeer.close() 573 tp.close() 574 } 575 return tp, closePeer, errCh 576 } 577 578 // testnetConfig wraps all the configurations for testing network. 579 type testnetConfig struct { 580 blocks int 581 protocol int 582 indexFn indexerCallback 583 ulcServers []string 584 ulcFraction int 585 simClock bool 586 connect bool 587 nopruning bool 588 } 589 590 func newClientServerEnv(t *testing.T, config testnetConfig) (*testServer, *testClient, func()) { 591 var ( 592 sdb = rawdb.NewMemoryDatabase() 593 cdb = rawdb.NewMemoryDatabase() 594 speers = newServerPeerSet() 595 ) 596 var clock mclock.Clock = &mclock.System{} 597 if config.simClock { 598 clock = &mclock.Simulated{} 599 } 600 dist := newRequestDistributor(speers, clock) 601 rm := newRetrieveManager(speers, dist, func() time.Duration { return time.Millisecond * 500 }) 602 odr := NewLesOdr(cdb, light.TestClientIndexerConfig, speers, rm) 603 604 sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig, true) 605 cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig, config.nopruning) 606 607 scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2] 608 ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2] 609 odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer) 610 611 server, b, serverClose := newTestServerHandler(config.blocks, sindexers, sdb, clock) 612 client, clientClose := newTestClientHandler(b, odr, cIndexers, cdb, speers, config.ulcServers, config.ulcFraction) 613 614 scIndexer.Start(server.blockchain) 615 sbIndexer.Start(server.blockchain) 616 ccIndexer.Start(client.backend.blockchain) 617 cbIndexer.Start(client.backend.blockchain) 618 619 if config.indexFn != nil { 620 config.indexFn(scIndexer, sbIndexer, sbtIndexer) 621 } 622 var ( 623 err error 624 speer, cpeer *testPeer 625 ) 626 if config.connect { 627 done := make(chan struct{}) 628 client.syncEnd = func(_ *types.Header) { close(done) } 629 cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client, false) 630 if err != nil { 631 t.Fatalf("Failed to connect testing peers %v", err) 632 } 633 select { 634 case <-done: 635 case <-time.After(10 * time.Second): 636 t.Fatal("test peer did not connect and sync within 3s") 637 } 638 } 639 s := &testServer{ 640 clock: clock, 641 backend: b, 642 db: sdb, 643 peer: cpeer, 644 handler: server, 645 chtIndexer: scIndexer, 646 bloomIndexer: sbIndexer, 647 bloomTrieIndexer: sbtIndexer, 648 } 649 c := &testClient{ 650 clock: clock, 651 db: cdb, 652 peer: speer, 653 handler: client, 654 chtIndexer: ccIndexer, 655 bloomIndexer: cbIndexer, 656 bloomTrieIndexer: cbtIndexer, 657 } 658 teardown := func() { 659 if config.connect { 660 speer.close() 661 cpeer.close() 662 cpeer.cpeer.close() 663 speer.speer.close() 664 } 665 ccIndexer.Close() 666 cbIndexer.Close() 667 scIndexer.Close() 668 sbIndexer.Close() 669 dist.close() 670 serverClose() 671 b.Close() 672 clientClose() 673 } 674 return s, c, teardown 675 } 676 677 // NewFuzzerPeer creates a client peer for test purposes, and also returns 678 // a function to close the peer: this is needed to avoid goroutine leaks in the 679 // exec queue. 680 func NewFuzzerPeer(version int) (p *clientPeer, closer func()) { 681 p = newClientPeer(version, 0, p2p.NewPeer(enode.ID{}, "", nil), nil) 682 return p, func() { p.peerCommons.close() } 683 }