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