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