github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/eth/handler_test.go (about) 1 // Copyright 2015 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 eth 18 19 import ( 20 "fmt" 21 "math" 22 "math/big" 23 "math/rand" 24 "testing" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/consensus/ethash" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/rawdb" 31 "github.com/ethereum/go-ethereum/core/state" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/core/vm" 34 "github.com/ethereum/go-ethereum/crypto" 35 "github.com/ethereum/go-ethereum/eth/downloader" 36 "github.com/ethereum/go-ethereum/event" 37 "github.com/ethereum/go-ethereum/p2p" 38 "github.com/ethereum/go-ethereum/params" 39 ) 40 41 // Tests that protocol versions and modes of operations are matched up properly. 42 func TestProtocolCompatibility(t *testing.T) { 43 // Define the compatibility chart 44 tests := []struct { 45 version uint 46 mode downloader.SyncMode 47 compatible bool 48 }{ 49 {61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true}, 50 {61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true}, 51 } 52 // Make sure anything we screw up is restored 53 backup := ProtocolVersions 54 defer func() { ProtocolVersions = backup }() 55 56 // Try all available compatibility configs and check for errors 57 for i, tt := range tests { 58 ProtocolVersions = []uint{tt.version} 59 60 pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil) 61 if pm != nil { 62 defer pm.Stop() 63 } 64 if (err == nil && !tt.compatible) || (err != nil && tt.compatible) { 65 t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible) 66 } 67 } 68 } 69 70 // Tests that block headers can be retrieved from a remote chain based on user queries. 71 func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } 72 func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } 73 74 func testGetBlockHeaders(t *testing.T, protocol int) { 75 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) 76 peer, _ := newTestPeer("peer", protocol, pm, true) 77 defer peer.close() 78 79 // Create a "random" unknown hash for testing 80 var unknown common.Hash 81 for i := range unknown { 82 unknown[i] = byte(i) 83 } 84 // Create a batch of tests for various scenarios 85 limit := uint64(downloader.MaxHeaderFetch) 86 tests := []struct { 87 query *getBlockHeadersData // The query to execute for header retrieval 88 expect []common.Hash // The hashes of the block whose headers are expected 89 }{ 90 // A single random block should be retrievable by hash and number too 91 { 92 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 93 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 94 }, { 95 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, 96 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 97 }, 98 // Multiple headers should be retrievable in both directions 99 { 100 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, 101 []common.Hash{ 102 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 103 pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), 104 pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), 105 }, 106 }, { 107 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 108 []common.Hash{ 109 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 110 pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), 111 pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), 112 }, 113 }, 114 // Multiple headers with skip lists should be retrievable 115 { 116 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 117 []common.Hash{ 118 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 119 pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), 120 pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), 121 }, 122 }, { 123 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 124 []common.Hash{ 125 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 126 pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), 127 pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), 128 }, 129 }, 130 // The chain endpoints should be retrievable 131 { 132 &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, 133 []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, 134 }, { 135 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, 136 []common.Hash{pm.blockchain.CurrentBlock().Hash()}, 137 }, 138 // Ensure protocol limits are honored 139 { 140 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 141 pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), 142 }, 143 // Check that requesting more than available is handled gracefully 144 { 145 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 146 []common.Hash{ 147 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 148 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), 149 }, 150 }, { 151 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 152 []common.Hash{ 153 pm.blockchain.GetBlockByNumber(4).Hash(), 154 pm.blockchain.GetBlockByNumber(0).Hash(), 155 }, 156 }, 157 // Check that requesting more than available is handled gracefully, even if mid skip 158 { 159 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 160 []common.Hash{ 161 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 162 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), 163 }, 164 }, { 165 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 166 []common.Hash{ 167 pm.blockchain.GetBlockByNumber(4).Hash(), 168 pm.blockchain.GetBlockByNumber(1).Hash(), 169 }, 170 }, 171 // Check a corner case where requesting more can iterate past the endpoints 172 { 173 &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, 174 []common.Hash{ 175 pm.blockchain.GetBlockByNumber(2).Hash(), 176 pm.blockchain.GetBlockByNumber(1).Hash(), 177 pm.blockchain.GetBlockByNumber(0).Hash(), 178 }, 179 }, 180 // Check a corner case where skipping overflow loops back into the chain start 181 { 182 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, 183 []common.Hash{ 184 pm.blockchain.GetBlockByNumber(3).Hash(), 185 }, 186 }, 187 // Check a corner case where skipping overflow loops back to the same header 188 { 189 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, 190 []common.Hash{ 191 pm.blockchain.GetBlockByNumber(1).Hash(), 192 }, 193 }, 194 // Check that non existing headers aren't returned 195 { 196 &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, 197 []common.Hash{}, 198 }, { 199 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, 200 []common.Hash{}, 201 }, 202 } 203 // Run each of the tests and verify the results against the chain 204 for i, tt := range tests { 205 // Collect the headers to expect in the response 206 headers := []*types.Header{} 207 for _, hash := range tt.expect { 208 headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) 209 } 210 // Send the hash request and verify the response 211 p2p.Send(peer.app, 0x03, tt.query) 212 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 213 t.Errorf("test %d: headers mismatch: %v", i, err) 214 } 215 // If the test used number origins, repeat with hashes as the too 216 if tt.query.Origin.Hash == (common.Hash{}) { 217 if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { 218 tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 219 220 p2p.Send(peer.app, 0x03, tt.query) 221 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 222 t.Errorf("test %d: headers mismatch: %v", i, err) 223 } 224 } 225 } 226 } 227 } 228 229 // Tests that block contents can be retrieved from a remote chain based on their hashes. 230 func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } 231 func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } 232 233 func testGetBlockBodies(t *testing.T, protocol int) { 234 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) 235 peer, _ := newTestPeer("peer", protocol, pm, true) 236 defer peer.close() 237 238 // Create a batch of tests for various scenarios 239 limit := downloader.MaxBlockFetch 240 tests := []struct { 241 random int // Number of blocks to fetch randomly from the chain 242 explicit []common.Hash // Explicitly requested blocks 243 available []bool // Availability of explicitly requested blocks 244 expected int // Total number of existing blocks to expect 245 }{ 246 {1, nil, nil, 1}, // A single random block should be retrievable 247 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 248 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 249 {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 250 {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 251 {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 252 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 253 254 // Existing and non-existing blocks interleaved should not cause problems 255 {0, []common.Hash{ 256 {}, 257 pm.blockchain.GetBlockByNumber(1).Hash(), 258 {}, 259 pm.blockchain.GetBlockByNumber(10).Hash(), 260 {}, 261 pm.blockchain.GetBlockByNumber(100).Hash(), 262 {}, 263 }, []bool{false, true, false, true, false, true, false}, 3}, 264 } 265 // Run each of the tests and verify the results against the chain 266 for i, tt := range tests { 267 // Collect the hashes to request, and the response to expect 268 hashes, seen := []common.Hash{}, make(map[int64]bool) 269 bodies := []*blockBody{} 270 271 for j := 0; j < tt.random; j++ { 272 for { 273 num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) 274 if !seen[num] { 275 seen[num] = true 276 277 block := pm.blockchain.GetBlockByNumber(uint64(num)) 278 hashes = append(hashes, block.Hash()) 279 if len(bodies) < tt.expected { 280 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 281 } 282 break 283 } 284 } 285 } 286 for j, hash := range tt.explicit { 287 hashes = append(hashes, hash) 288 if tt.available[j] && len(bodies) < tt.expected { 289 block := pm.blockchain.GetBlockByHash(hash) 290 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 291 } 292 } 293 // Send the hash request and verify the response 294 p2p.Send(peer.app, 0x05, hashes) 295 if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { 296 t.Errorf("test %d: bodies mismatch: %v", i, err) 297 } 298 } 299 } 300 301 // Tests that the node state database can be retrieved based on hashes. 302 func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } 303 304 func testGetNodeData(t *testing.T, protocol int) { 305 // Define three accounts to simulate transactions with 306 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 307 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 308 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 309 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 310 311 signer := types.HomesteadSigner{} 312 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 313 generator := func(i int, block *core.BlockGen) { 314 switch i { 315 case 0: 316 // In block 1, the test bank sends account #1 some ether. 317 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 318 block.AddTx(tx) 319 case 1: 320 // In block 2, the test bank sends some more ether to account #1. 321 // acc1Addr passes it on to account #2. 322 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 323 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 324 block.AddTx(tx1) 325 block.AddTx(tx2) 326 case 2: 327 // Block 3 is empty but was mined by account #2. 328 block.SetCoinbase(acc2Addr) 329 block.SetExtra([]byte("yeehaw")) 330 case 3: 331 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 332 b2 := block.PrevBlock(1).Header() 333 b2.Extra = []byte("foo") 334 block.AddUncle(b2) 335 b3 := block.PrevBlock(2).Header() 336 b3.Extra = []byte("foo") 337 block.AddUncle(b3) 338 } 339 } 340 // Assemble the test environment 341 pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) 342 peer, _ := newTestPeer("peer", protocol, pm, true) 343 defer peer.close() 344 345 // Fetch for now the entire chain db 346 hashes := []common.Hash{} 347 348 it := db.NewIterator() 349 for it.Next() { 350 if key := it.Key(); len(key) == common.HashLength { 351 hashes = append(hashes, common.BytesToHash(key)) 352 } 353 } 354 it.Release() 355 356 p2p.Send(peer.app, 0x0d, hashes) 357 msg, err := peer.app.ReadMsg() 358 if err != nil { 359 t.Fatalf("failed to read node data response: %v", err) 360 } 361 if msg.Code != 0x0e { 362 t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) 363 } 364 var data [][]byte 365 if err := msg.Decode(&data); err != nil { 366 t.Fatalf("failed to decode response node data: %v", err) 367 } 368 // Verify that all hashes correspond to the requested data, and reconstruct a state tree 369 for i, want := range hashes { 370 if hash := crypto.Keccak256Hash(data[i]); hash != want { 371 t.Errorf("data hash mismatch: have %x, want %x", hash, want) 372 } 373 } 374 statedb := rawdb.NewMemoryDatabase() 375 for i := 0; i < len(data); i++ { 376 statedb.Put(hashes[i].Bytes(), data[i]) 377 } 378 accounts := []common.Address{testBank, acc1Addr, acc2Addr} 379 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 380 trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb)) 381 382 for j, acc := range accounts { 383 state, _ := pm.blockchain.State() 384 bw := state.GetBalance(acc) 385 bh := trie.GetBalance(acc) 386 387 if (bw != nil && bh == nil) || (bw == nil && bh != nil) { 388 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 389 } 390 if bw != nil && bh != nil && bw.Cmp(bw) != 0 { 391 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 392 } 393 } 394 } 395 } 396 397 // Tests that the transaction receipts can be retrieved based on hashes. 398 func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } 399 400 func testGetReceipt(t *testing.T, protocol int) { 401 // Define three accounts to simulate transactions with 402 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 403 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 404 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 405 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 406 407 signer := types.HomesteadSigner{} 408 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 409 generator := func(i int, block *core.BlockGen) { 410 switch i { 411 case 0: 412 // In block 1, the test bank sends account #1 some ether. 413 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 414 block.AddTx(tx) 415 case 1: 416 // In block 2, the test bank sends some more ether to account #1. 417 // acc1Addr passes it on to account #2. 418 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 419 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 420 block.AddTx(tx1) 421 block.AddTx(tx2) 422 case 2: 423 // Block 3 is empty but was mined by account #2. 424 block.SetCoinbase(acc2Addr) 425 block.SetExtra([]byte("yeehaw")) 426 case 3: 427 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 428 b2 := block.PrevBlock(1).Header() 429 b2.Extra = []byte("foo") 430 block.AddUncle(b2) 431 b3 := block.PrevBlock(2).Header() 432 b3.Extra = []byte("foo") 433 block.AddUncle(b3) 434 } 435 } 436 // Assemble the test environment 437 pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) 438 peer, _ := newTestPeer("peer", protocol, pm, true) 439 defer peer.close() 440 441 // Collect the hashes to request, and the response to expect 442 hashes, receipts := []common.Hash{}, []types.Receipts{} 443 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 444 block := pm.blockchain.GetBlockByNumber(i) 445 446 hashes = append(hashes, block.Hash()) 447 receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash())) 448 } 449 // Send the hash request and verify the response 450 p2p.Send(peer.app, 0x0f, hashes) 451 if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { 452 t.Errorf("receipts mismatch: %v", err) 453 } 454 } 455 456 // Tests that post eth protocol handshake, clients perform a mutual checkpoint 457 // challenge to validate each other's chains. Hash mismatches, or missing ones 458 // during a fast sync should lead to the peer getting dropped. 459 func TestCheckpointChallenge(t *testing.T) { 460 tests := []struct { 461 syncmode downloader.SyncMode 462 checkpoint bool 463 timeout bool 464 empty bool 465 match bool 466 drop bool 467 }{ 468 // If checkpointing is not enabled locally, don't challenge and don't drop 469 {downloader.FullSync, false, false, false, false, false}, 470 {downloader.FastSync, false, false, false, false, false}, 471 {downloader.LightSync, false, false, false, false, false}, 472 473 // If checkpointing is enabled locally and remote response is empty, only drop during fast sync 474 {downloader.FullSync, true, false, true, false, false}, 475 {downloader.FastSync, true, false, true, false, true}, // Special case, fast sync, unsynced peer 476 {downloader.LightSync, true, false, true, false, false}, 477 478 // If checkpointing is enabled locally and remote response mismatches, always drop 479 {downloader.FullSync, true, false, false, false, true}, 480 {downloader.FastSync, true, false, false, false, true}, 481 {downloader.LightSync, true, false, false, false, true}, 482 483 // If checkpointing is enabled locally and remote response matches, never drop 484 {downloader.FullSync, true, false, false, true, false}, 485 {downloader.FastSync, true, false, false, true, false}, 486 {downloader.LightSync, true, false, false, true, false}, 487 488 // If checkpointing is enabled locally and remote times out, always drop 489 {downloader.FullSync, true, true, false, true, true}, 490 {downloader.FastSync, true, true, false, true, true}, 491 {downloader.LightSync, true, true, false, true, true}, 492 } 493 for _, tt := range tests { 494 t.Run(fmt.Sprintf("sync %v checkpoint %v timeout %v empty %v match %v", tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match), func(t *testing.T) { 495 testCheckpointChallenge(t, tt.syncmode, tt.checkpoint, tt.timeout, tt.empty, tt.match, tt.drop) 496 }) 497 } 498 } 499 500 func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) { 501 // Reduce the checkpoint handshake challenge timeout 502 defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout) 503 syncChallengeTimeout = 250 * time.Millisecond 504 505 // Initialize a chain and generate a fake CHT if checkpointing is enabled 506 var ( 507 db = rawdb.NewMemoryDatabase() 508 config = new(params.ChainConfig) 509 genesis = (&core.Genesis{Config: config}).MustCommit(db) 510 ) 511 // If checkpointing is enabled, create and inject a fake CHT and the corresponding 512 // chllenge response. 513 var response *types.Header 514 if checkpoint { 515 index := uint64(rand.Intn(500)) 516 number := (index+1)*params.CHTFrequency - 1 517 response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")} 518 519 cht := ¶ms.TrustedCheckpoint{ 520 SectionIndex: index, 521 SectionHead: response.Hash(), 522 } 523 params.TrustedCheckpoints[genesis.Hash()] = cht 524 defer delete(params.TrustedCheckpoints, genesis.Hash()) 525 } 526 // Create a checkpoint aware protocol manager 527 blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil) 528 if err != nil { 529 t.Fatalf("failed to create new blockchain: %v", err) 530 } 531 pm, err := NewProtocolManager(config, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), ethash.NewFaker(), blockchain, db, nil) 532 if err != nil { 533 t.Fatalf("failed to start test protocol manager: %v", err) 534 } 535 pm.Start(1000) 536 defer pm.Stop() 537 538 // Connect a new peer and check that we receive the checkpoint challenge 539 peer, _ := newTestPeer("peer", eth63, pm, true) 540 defer peer.close() 541 542 if checkpoint { 543 challenge := &getBlockHeadersData{ 544 Origin: hashOrNumber{Number: response.Number.Uint64()}, 545 Amount: 1, 546 Skip: 0, 547 Reverse: false, 548 } 549 if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil { 550 t.Fatalf("challenge mismatch: %v", err) 551 } 552 // Create a block to reply to the challenge if no timeout is simulated 553 if !timeout { 554 if empty { 555 if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{}); err != nil { 556 t.Fatalf("failed to answer challenge: %v", err) 557 } 558 } else if match { 559 if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{response}); err != nil { 560 t.Fatalf("failed to answer challenge: %v", err) 561 } 562 } else { 563 if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{{Number: response.Number}}); err != nil { 564 t.Fatalf("failed to answer challenge: %v", err) 565 } 566 } 567 } 568 } 569 // Wait until the test timeout passes to ensure proper cleanup 570 time.Sleep(syncChallengeTimeout + 100*time.Millisecond) 571 572 // Verify that the remote peer is maintained or dropped 573 if drop { 574 if peers := pm.peers.Len(); peers != 0 { 575 t.Fatalf("peer count mismatch: have %d, want %d", peers, 0) 576 } 577 } else { 578 if peers := pm.peers.Len(); peers != 1 { 579 t.Fatalf("peer count mismatch: have %d, want %d", peers, 1) 580 } 581 } 582 } 583 584 func TestBroadcastBlock(t *testing.T) { 585 var tests = []struct { 586 totalPeers int 587 broadcastExpected int 588 }{ 589 {1, 1}, 590 {2, 2}, 591 {3, 3}, 592 {4, 4}, 593 {5, 4}, 594 {9, 4}, 595 {12, 4}, 596 {16, 4}, 597 {26, 5}, 598 {100, 10}, 599 } 600 for _, test := range tests { 601 testBroadcastBlock(t, test.totalPeers, test.broadcastExpected) 602 } 603 } 604 605 func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) { 606 var ( 607 evmux = new(event.TypeMux) 608 pow = ethash.NewFaker() 609 db = rawdb.NewMemoryDatabase() 610 config = ¶ms.ChainConfig{} 611 gspec = &core.Genesis{Config: config} 612 genesis = gspec.MustCommit(db) 613 ) 614 blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil) 615 if err != nil { 616 t.Fatalf("failed to create new blockchain: %v", err) 617 } 618 pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, nil) 619 if err != nil { 620 t.Fatalf("failed to start test protocol manager: %v", err) 621 } 622 pm.Start(1000) 623 defer pm.Stop() 624 var peers []*testPeer 625 for i := 0; i < totalPeers; i++ { 626 peer, _ := newTestPeer(fmt.Sprintf("peer %d", i), eth63, pm, true) 627 defer peer.close() 628 peers = append(peers, peer) 629 } 630 chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) {}) 631 pm.BroadcastBlock(chain[0], true /*propagate*/) 632 633 errCh := make(chan error, totalPeers) 634 doneCh := make(chan struct{}, totalPeers) 635 for _, peer := range peers { 636 go func(p *testPeer) { 637 if err := p2p.ExpectMsg(p.app, NewBlockMsg, &newBlockData{Block: chain[0], TD: big.NewInt(131136)}); err != nil { 638 errCh <- err 639 } else { 640 doneCh <- struct{}{} 641 } 642 }(peer) 643 } 644 timeout := time.After(300 * time.Millisecond) 645 var receivedCount int 646 outer: 647 for { 648 select { 649 case err = <-errCh: 650 break outer 651 case <-doneCh: 652 receivedCount++ 653 if receivedCount == totalPeers { 654 break outer 655 } 656 case <-timeout: 657 break outer 658 } 659 } 660 for _, peer := range peers { 661 peer.app.Close() 662 } 663 if err != nil { 664 t.Errorf("error matching block by peer: %v", err) 665 } 666 if receivedCount != broadcastExpected { 667 t.Errorf("block broadcast to %d peers, expected %d", receivedCount, broadcastExpected) 668 } 669 }