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