github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/les/test_helper.go (about) 1 // Copyright 2016 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. 19 20 package les 21 22 import ( 23 "context" 24 "crypto/rand" 25 "math/big" 26 "testing" 27 "time" 28 29 "github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind" 30 "github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind/backends" 31 "github.com/ethereum-optimism/optimism/l2geth/common" 32 "github.com/ethereum-optimism/optimism/l2geth/common/mclock" 33 "github.com/ethereum-optimism/optimism/l2geth/consensus/ethash" 34 "github.com/ethereum-optimism/optimism/l2geth/contracts/checkpointoracle/contract" 35 "github.com/ethereum-optimism/optimism/l2geth/core" 36 "github.com/ethereum-optimism/optimism/l2geth/core/rawdb" 37 "github.com/ethereum-optimism/optimism/l2geth/core/types" 38 "github.com/ethereum-optimism/optimism/l2geth/crypto" 39 "github.com/ethereum-optimism/optimism/l2geth/eth" 40 "github.com/ethereum-optimism/optimism/l2geth/ethdb" 41 "github.com/ethereum-optimism/optimism/l2geth/event" 42 "github.com/ethereum-optimism/optimism/l2geth/les/checkpointoracle" 43 "github.com/ethereum-optimism/optimism/l2geth/les/flowcontrol" 44 "github.com/ethereum-optimism/optimism/l2geth/light" 45 "github.com/ethereum-optimism/optimism/l2geth/p2p" 46 "github.com/ethereum-optimism/optimism/l2geth/p2p/enode" 47 "github.com/ethereum-optimism/optimism/l2geth/params" 48 ) 49 50 var ( 51 bankKey, _ = crypto.GenerateKey() 52 bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey) 53 bankFunds = big.NewInt(1000000000000000000) 54 55 userKey1, _ = crypto.GenerateKey() 56 userKey2, _ = crypto.GenerateKey() 57 userAddr1 = crypto.PubkeyToAddress(userKey1.PublicKey) 58 userAddr2 = crypto.PubkeyToAddress(userKey2.PublicKey) 59 60 testContractAddr common.Address 61 testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") 62 testContractCodeDeployed = testContractCode[16:] 63 testContractDeployed = uint64(2) 64 65 testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029") 66 67 // Checkpoint registrar relative 68 registrarAddr common.Address 69 signerKey, _ = crypto.GenerateKey() 70 signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey) 71 ) 72 73 var ( 74 // The block frequency for creating checkpoint(only used in test) 75 sectionSize = big.NewInt(128) 76 77 // The number of confirmations needed to generate a checkpoint(only used in test). 78 processConfirms = big.NewInt(1) 79 80 // The token bucket buffer limit for testing purpose. 81 testBufLimit = uint64(1000000) 82 83 // The buffer recharging speed for testing purpose. 84 testBufRecharge = uint64(1000) 85 ) 86 87 /* 88 contract test { 89 90 uint256[100] data; 91 92 function Put(uint256 addr, uint256 value) { 93 data[addr] = value; 94 } 95 96 function Get(uint256 addr) constant returns (uint256 value) { 97 return data[addr]; 98 } 99 } 100 */ 101 102 // prepare pre-commits specified number customized blocks into chain. 103 func prepare(n int, backend *backends.SimulatedBackend) { 104 var ( 105 ctx = context.Background() 106 signer = types.HomesteadSigner{} 107 ) 108 for i := 0; i < n; i++ { 109 switch i { 110 case 0: 111 // deploy checkpoint contract 112 registrarAddr, _, _, _ = contract.DeployCheckpointOracle(bind.NewKeyedTransactor(bankKey), backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1)) 113 // bankUser transfers some ether to user1 114 nonce, _ := backend.PendingNonceAt(ctx, bankAddr) 115 tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey) 116 backend.SendTransaction(ctx, tx) 117 case 1: 118 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 119 userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1) 120 121 // bankUser transfers more ether to user1 122 tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1000), params.TxGas, nil, nil), signer, bankKey) 123 backend.SendTransaction(ctx, tx1) 124 125 // user1 relays ether to user2 126 tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1000), params.TxGas, nil, nil), signer, userKey1) 127 backend.SendTransaction(ctx, tx2) 128 129 // user1 deploys a test contract 130 tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, userKey1) 131 backend.SendTransaction(ctx, tx3) 132 testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1) 133 134 // user1 deploys a event contract 135 tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, userKey1) 136 backend.SendTransaction(ctx, tx4) 137 case 2: 138 // bankUser transfer some ether to signer 139 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 140 tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, nil, nil), signer, bankKey) 141 backend.SendTransaction(ctx, tx1) 142 143 // invoke test contract 144 data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") 145 tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey) 146 backend.SendTransaction(ctx, tx2) 147 case 3: 148 // invoke test contract 149 bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) 150 data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") 151 tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey) 152 backend.SendTransaction(ctx, tx) 153 } 154 backend.Commit() 155 } 156 } 157 158 // testIndexers creates a set of indexers with specified params for testing purpose. 159 func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig) []*core.ChainIndexer { 160 var indexers [3]*core.ChainIndexer 161 indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms) 162 indexers[1] = eth.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) 163 indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize) 164 // make bloomTrieIndexer as a child indexer of bloom indexer. 165 indexers[1].AddChildIndexer(indexers[2]) 166 return indexers[:] 167 } 168 169 func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *peerSet, ulcServers []string, ulcFraction int) *clientHandler { 170 var ( 171 evmux = new(event.TypeMux) 172 engine = ethash.NewFaker() 173 gspec = core.Genesis{ 174 Config: params.AllEthashProtocolChanges, 175 Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, 176 GasLimit: 100000000, 177 } 178 oracle *checkpointoracle.CheckpointOracle 179 ) 180 genesis := gspec.MustCommit(db) 181 chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) 182 if indexers != nil { 183 checkpointConfig := ¶ms.CheckpointOracleConfig{ 184 Address: crypto.CreateAddress(bankAddr, 0), 185 Signers: []common.Address{signerAddr}, 186 Threshold: 1, 187 } 188 getLocal := func(index uint64) params.TrustedCheckpoint { 189 chtIndexer := indexers[0] 190 sectionHead := chtIndexer.SectionHead(index) 191 return params.TrustedCheckpoint{ 192 SectionIndex: index, 193 SectionHead: sectionHead, 194 CHTRoot: light.GetChtRoot(db, index, sectionHead), 195 BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), 196 } 197 } 198 oracle = checkpointoracle.New(checkpointConfig, getLocal) 199 } 200 client := &LightEthereum{ 201 lesCommons: lesCommons{ 202 genesis: genesis.Hash(), 203 config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, 204 chainConfig: params.AllEthashProtocolChanges, 205 iConfig: light.TestClientIndexerConfig, 206 chainDb: db, 207 oracle: oracle, 208 chainReader: chain, 209 peers: peers, 210 closeCh: make(chan struct{}), 211 }, 212 reqDist: odr.retriever.dist, 213 retriever: odr.retriever, 214 odr: odr, 215 engine: engine, 216 blockchain: chain, 217 eventMux: evmux, 218 } 219 client.handler = newClientHandler(ulcServers, ulcFraction, nil, client) 220 221 if client.oracle != nil { 222 client.oracle.Start(backend) 223 } 224 return client.handler 225 } 226 227 func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, peers *peerSet, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend) { 228 var ( 229 gspec = core.Genesis{ 230 Config: params.AllEthashProtocolChanges, 231 Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, 232 GasLimit: 100000000, 233 } 234 oracle *checkpointoracle.CheckpointOracle 235 ) 236 genesis := gspec.MustCommit(db) 237 238 // create a simulation backend and pre-commit several customized block to the database. 239 simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000) 240 prepare(blocks, simulation) 241 242 txpoolConfig := core.DefaultTxPoolConfig 243 txpoolConfig.Journal = "" 244 txpool := core.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain()) 245 if indexers != nil { 246 checkpointConfig := ¶ms.CheckpointOracleConfig{ 247 Address: crypto.CreateAddress(bankAddr, 0), 248 Signers: []common.Address{signerAddr}, 249 Threshold: 1, 250 } 251 getLocal := func(index uint64) params.TrustedCheckpoint { 252 chtIndexer := indexers[0] 253 sectionHead := chtIndexer.SectionHead(index) 254 return params.TrustedCheckpoint{ 255 SectionIndex: index, 256 SectionHead: sectionHead, 257 CHTRoot: light.GetChtRoot(db, index, sectionHead), 258 BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), 259 } 260 } 261 oracle = checkpointoracle.New(checkpointConfig, getLocal) 262 } 263 server := &LesServer{ 264 lesCommons: lesCommons{ 265 genesis: genesis.Hash(), 266 config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, 267 chainConfig: params.AllEthashProtocolChanges, 268 iConfig: light.TestServerIndexerConfig, 269 chainDb: db, 270 chainReader: simulation.Blockchain(), 271 oracle: oracle, 272 peers: peers, 273 closeCh: make(chan struct{}), 274 }, 275 servingQueue: newServingQueue(int64(time.Millisecond*10), 1), 276 defParams: flowcontrol.ServerParams{ 277 BufLimit: testBufLimit, 278 MinRecharge: testBufRecharge, 279 }, 280 fcManager: flowcontrol.NewClientManager(nil, clock), 281 } 282 server.costTracker, server.freeCapacity = newCostTracker(db, server.config) 283 server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism. 284 server.clientPool = newClientPool(db, 1, clock, nil) 285 server.clientPool.setLimits(10000, 10000) // Assign enough capacity for clientpool 286 server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }) 287 if server.oracle != nil { 288 server.oracle.Start(simulation) 289 } 290 server.servingQueue.setThreads(4) 291 server.handler.start() 292 return server.handler, simulation 293 } 294 295 // testPeer is a simulated peer to allow testing direct network calls. 296 type testPeer struct { 297 peer *peer 298 299 net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging 300 app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side 301 } 302 303 // newTestPeer creates a new peer registered at the given protocol manager. 304 func newTestPeer(t *testing.T, name string, version int, handler *serverHandler, shake bool, testCost uint64) (*testPeer, <-chan error) { 305 // Create a message pipe to communicate through 306 app, net := p2p.MsgPipe() 307 308 // Generate a random id and create the peer 309 var id enode.ID 310 rand.Read(id[:]) 311 peer := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) 312 313 // Start the peer on a new thread 314 errCh := make(chan error, 1) 315 go func() { 316 select { 317 case <-handler.closeCh: 318 errCh <- p2p.DiscQuitting 319 case errCh <- handler.handle(peer): 320 } 321 }() 322 tp := &testPeer{ 323 app: app, 324 net: net, 325 peer: peer, 326 } 327 // Execute any implicitly requested handshakes and return 328 if shake { 329 // Customize the cost table if required. 330 if testCost != 0 { 331 handler.server.costTracker.testCostList = testCostList(testCost) 332 } 333 var ( 334 genesis = handler.blockchain.Genesis() 335 head = handler.blockchain.CurrentHeader() 336 td = handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) 337 ) 338 tp.handshake(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), testCostList(testCost)) 339 } 340 return tp, errCh 341 } 342 343 // close terminates the local side of the peer, notifying the remote protocol 344 // manager of termination. 345 func (p *testPeer) close() { 346 p.app.Close() 347 } 348 349 func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler) (*testPeer, <-chan error, *testPeer, <-chan error) { 350 // Create a message pipe to communicate through 351 app, net := p2p.MsgPipe() 352 353 // Generate a random id and create the peer 354 var id enode.ID 355 rand.Read(id[:]) 356 357 peer1 := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) 358 peer2 := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), app) 359 360 // Start the peer on a new thread 361 errc1 := make(chan error, 1) 362 errc2 := make(chan error, 1) 363 go func() { 364 select { 365 case <-server.closeCh: 366 errc1 <- p2p.DiscQuitting 367 case errc1 <- server.handle(peer1): 368 } 369 }() 370 go func() { 371 select { 372 case <-client.closeCh: 373 errc1 <- p2p.DiscQuitting 374 case errc1 <- client.handle(peer2): 375 } 376 }() 377 return &testPeer{peer: peer1, net: net, app: app}, errc1, &testPeer{peer: peer2, net: app, app: net}, errc2 378 } 379 380 // handshake simulates a trivial handshake that expects the same state from the 381 // remote side as we are simulating locally. 382 func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, costList RequestCostList) { 383 var expList keyValueList 384 expList = expList.add("protocolVersion", uint64(p.peer.version)) 385 expList = expList.add("networkId", uint64(NetworkId)) 386 expList = expList.add("headTd", td) 387 expList = expList.add("headHash", head) 388 expList = expList.add("headNum", headNum) 389 expList = expList.add("genesisHash", genesis) 390 sendList := make(keyValueList, len(expList)) 391 copy(sendList, expList) 392 expList = expList.add("serveHeaders", nil) 393 expList = expList.add("serveChainSince", uint64(0)) 394 expList = expList.add("serveStateSince", uint64(0)) 395 expList = expList.add("serveRecentState", uint64(core.TriesInMemory-4)) 396 expList = expList.add("txRelay", nil) 397 expList = expList.add("flowControl/BL", testBufLimit) 398 expList = expList.add("flowControl/MRR", testBufRecharge) 399 expList = expList.add("flowControl/MRC", costList) 400 401 if err := p2p.ExpectMsg(p.app, StatusMsg, expList); err != nil { 402 t.Fatalf("status recv: %v", err) 403 } 404 if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { 405 t.Fatalf("status send: %v", err) 406 } 407 p.peer.fcParams = flowcontrol.ServerParams{ 408 BufLimit: testBufLimit, 409 MinRecharge: testBufRecharge, 410 } 411 } 412 413 type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) 414 415 // testClient represents a client for testing with necessary auxiliary fields. 416 type testClient struct { 417 clock mclock.Clock 418 db ethdb.Database 419 peer *testPeer 420 handler *clientHandler 421 422 chtIndexer *core.ChainIndexer 423 bloomIndexer *core.ChainIndexer 424 bloomTrieIndexer *core.ChainIndexer 425 } 426 427 // testServer represents a server for testing with necessary auxiliary fields. 428 type testServer struct { 429 clock mclock.Clock 430 backend *backends.SimulatedBackend 431 db ethdb.Database 432 peer *testPeer 433 handler *serverHandler 434 435 chtIndexer *core.ChainIndexer 436 bloomIndexer *core.ChainIndexer 437 bloomTrieIndexer *core.ChainIndexer 438 } 439 440 func newServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, simClock bool, newPeer bool, testCost uint64) (*testServer, func()) { 441 db := rawdb.NewMemoryDatabase() 442 indexers := testIndexers(db, nil, light.TestServerIndexerConfig) 443 444 var clock mclock.Clock = &mclock.System{} 445 if simClock { 446 clock = &mclock.Simulated{} 447 } 448 handler, b := newTestServerHandler(blocks, indexers, db, newPeerSet(), clock) 449 450 var peer *testPeer 451 if newPeer { 452 peer, _ = newTestPeer(t, "peer", protocol, handler, true, testCost) 453 } 454 455 cIndexer, bIndexer, btIndexer := indexers[0], indexers[1], indexers[2] 456 cIndexer.Start(handler.blockchain) 457 bIndexer.Start(handler.blockchain) 458 459 // Wait until indexers generate enough index data. 460 if callback != nil { 461 callback(cIndexer, bIndexer, btIndexer) 462 } 463 server := &testServer{ 464 clock: clock, 465 backend: b, 466 db: db, 467 peer: peer, 468 handler: handler, 469 chtIndexer: cIndexer, 470 bloomIndexer: bIndexer, 471 bloomTrieIndexer: btIndexer, 472 } 473 teardown := func() { 474 if newPeer { 475 peer.close() 476 b.Close() 477 } 478 cIndexer.Close() 479 bIndexer.Close() 480 } 481 return server, teardown 482 } 483 484 func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, ulcServers []string, ulcFraction int, simClock bool, connect bool) (*testServer, *testClient, func()) { 485 sdb, cdb := rawdb.NewMemoryDatabase(), rawdb.NewMemoryDatabase() 486 speers, cPeers := newPeerSet(), newPeerSet() 487 488 var clock mclock.Clock = &mclock.System{} 489 if simClock { 490 clock = &mclock.Simulated{} 491 } 492 dist := newRequestDistributor(cPeers, clock) 493 rm := newRetrieveManager(cPeers, dist, nil) 494 odr := NewLesOdr(cdb, light.TestClientIndexerConfig, rm) 495 496 sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig) 497 cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig) 498 499 scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2] 500 ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2] 501 odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer) 502 503 server, b := newTestServerHandler(blocks, sindexers, sdb, speers, clock) 504 client := newTestClientHandler(b, odr, cIndexers, cdb, cPeers, ulcServers, ulcFraction) 505 506 scIndexer.Start(server.blockchain) 507 sbIndexer.Start(server.blockchain) 508 ccIndexer.Start(client.backend.blockchain) 509 cbIndexer.Start(client.backend.blockchain) 510 511 if callback != nil { 512 callback(scIndexer, sbIndexer, sbtIndexer) 513 } 514 var ( 515 speer, cpeer *testPeer 516 err1, err2 <-chan error 517 ) 518 if connect { 519 cpeer, err1, speer, err2 = newTestPeerPair("peer", protocol, server, client) 520 select { 521 case <-time.After(time.Millisecond * 300): 522 case err := <-err1: 523 t.Fatalf("peer 1 handshake error: %v", err) 524 case err := <-err2: 525 t.Fatalf("peer 2 handshake error: %v", err) 526 } 527 } 528 s := &testServer{ 529 clock: clock, 530 backend: b, 531 db: sdb, 532 peer: cpeer, 533 handler: server, 534 chtIndexer: scIndexer, 535 bloomIndexer: sbIndexer, 536 bloomTrieIndexer: sbtIndexer, 537 } 538 c := &testClient{ 539 clock: clock, 540 db: cdb, 541 peer: speer, 542 handler: client, 543 chtIndexer: ccIndexer, 544 bloomIndexer: cbIndexer, 545 bloomTrieIndexer: cbtIndexer, 546 } 547 teardown := func() { 548 if connect { 549 speer.close() 550 cpeer.close() 551 } 552 ccIndexer.Close() 553 cbIndexer.Close() 554 scIndexer.Close() 555 sbIndexer.Close() 556 b.Close() 557 } 558 return s, c, teardown 559 }