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