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 17package eth 18 19import ( 20 "math" 21 "math/big" 22 "math/rand" 23 "testing" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/consensus/ethash" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/rawdb" 29 "github.com/ethereum/go-ethereum/core/state" 30 "github.com/ethereum/go-ethereum/core/types" 31 "github.com/ethereum/go-ethereum/core/vm" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/ethdb" 34 "github.com/ethereum/go-ethereum/p2p" 35 "github.com/ethereum/go-ethereum/p2p/enode" 36 "github.com/ethereum/go-ethereum/params" 37) 38 39var ( 40 // testKey is a private key to use for funding a tester account. 41 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 42 43 // testAddr is the Ethereum address of the tester account. 44 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 45) 46 47// testBackend is a mock implementation of the live Ethereum message handler. Its 48// purpose is to allow testing the request/reply workflows and wire serialization 49// in the `eth` protocol without actually doing any data processing. 50type testBackend struct { 51 db ethdb.Database 52 chain *core.BlockChain 53 txpool *core.TxPool 54} 55 56// newTestBackend creates an empty chain and wraps it into a mock backend. 57func newTestBackend(blocks int) *testBackend { 58 return newTestBackendWithGenerator(blocks, nil) 59} 60 61// newTestBackend creates a chain with a number of explicitly defined blocks and 62// wraps it into a mock backend. 63func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)) *testBackend { 64 // Create a database pre-initialize with a genesis block 65 db := rawdb.NewMemoryDatabase() 66 (&core.Genesis{ 67 Config: params.TestChainConfig, 68 Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}}, 69 }).MustCommit(db) 70 71 chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) 72 73 bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator) 74 if _, err := chain.InsertChain(bs); err != nil { 75 panic(err) 76 } 77 txconfig := core.DefaultTxPoolConfig 78 txconfig.Journal = "" // Don't litter the disk with test journals 79 80 return &testBackend{ 81 db: db, 82 chain: chain, 83 txpool: core.NewTxPool(txconfig, params.TestChainConfig, chain), 84 } 85} 86 87// close tears down the transaction pool and chain behind the mock backend. 88func (b *testBackend) close() { 89 b.txpool.Stop() 90 b.chain.Stop() 91} 92 93func (b *testBackend) Chain() *core.BlockChain { return b.chain } 94func (b *testBackend) TxPool() TxPool { return b.txpool } 95 96func (b *testBackend) RunPeer(peer *Peer, handler Handler) error { 97 // Normally the backend would do peer mainentance and handshakes. All that 98 // is omitted and we will just give control back to the handler. 99 return handler(peer) 100} 101func (b *testBackend) PeerInfo(enode.ID) interface{} { panic("not implemented") } 102 103func (b *testBackend) AcceptTxs() bool { 104 panic("data processing tests should be done in the handler package") 105} 106func (b *testBackend) Handle(*Peer, Packet) error { 107 panic("data processing tests should be done in the handler package") 108} 109 110// Tests that block headers can be retrieved from a remote chain based on user queries. 111func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) } 112 113func testGetBlockHeaders(t *testing.T, protocol uint) { 114 t.Parallel() 115 116 backend := newTestBackend(maxHeadersServe + 15) 117 defer backend.close() 118 119 peer, _ := newTestPeer("peer", protocol, backend) 120 defer peer.close() 121 122 // Create a "random" unknown hash for testing 123 var unknown common.Hash 124 for i := range unknown { 125 unknown[i] = byte(i) 126 } 127 getHashes := func(from, limit uint64) (hashes []common.Hash) { 128 for i := uint64(0); i < limit; i++ { 129 hashes = append(hashes, backend.chain.GetCanonicalHash(from-1-i)) 130 } 131 return hashes 132 } 133 // Create a batch of tests for various scenarios 134 limit := uint64(maxHeadersServe) 135 tests := []struct { 136 query *GetBlockHeadersPacket // The query to execute for header retrieval 137 expect []common.Hash // The hashes of the block whose headers are expected 138 }{ 139 // A single random block should be retrievable by hash 140 { 141 &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 142 []common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()}, 143 }, 144 // A single random block should be retrievable by number 145 { 146 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 1}, 147 []common.Hash{backend.chain.GetBlockByNumber(limit / 2).Hash()}, 148 }, 149 // Multiple headers should be retrievable in both directions 150 { 151 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 3}, 152 []common.Hash{ 153 backend.chain.GetBlockByNumber(limit / 2).Hash(), 154 backend.chain.GetBlockByNumber(limit/2 + 1).Hash(), 155 backend.chain.GetBlockByNumber(limit/2 + 2).Hash(), 156 }, 157 }, { 158 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 159 []common.Hash{ 160 backend.chain.GetBlockByNumber(limit / 2).Hash(), 161 backend.chain.GetBlockByNumber(limit/2 - 1).Hash(), 162 backend.chain.GetBlockByNumber(limit/2 - 2).Hash(), 163 }, 164 }, 165 // Multiple headers with skip lists should be retrievable 166 { 167 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 168 []common.Hash{ 169 backend.chain.GetBlockByNumber(limit / 2).Hash(), 170 backend.chain.GetBlockByNumber(limit/2 + 4).Hash(), 171 backend.chain.GetBlockByNumber(limit/2 + 8).Hash(), 172 }, 173 }, { 174 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 175 []common.Hash{ 176 backend.chain.GetBlockByNumber(limit / 2).Hash(), 177 backend.chain.GetBlockByNumber(limit/2 - 4).Hash(), 178 backend.chain.GetBlockByNumber(limit/2 - 8).Hash(), 179 }, 180 }, 181 // The chain endpoints should be retrievable 182 { 183 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 0}, Amount: 1}, 184 []common.Hash{backend.chain.GetBlockByNumber(0).Hash()}, 185 }, 186 { 187 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 1}, 188 []common.Hash{backend.chain.CurrentBlock().Hash()}, 189 }, 190 { // If the peer requests a bit into the future, we deliver what we have 191 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64()}, Amount: 10}, 192 []common.Hash{backend.chain.CurrentBlock().Hash()}, 193 }, 194 // Ensure protocol limits are honored 195 { 196 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 197 getHashes(backend.chain.CurrentBlock().NumberU64(), limit), 198 }, 199 // Check that requesting more than available is handled gracefully 200 { 201 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 202 []common.Hash{ 203 backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 4).Hash(), 204 backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64()).Hash(), 205 }, 206 }, { 207 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 208 []common.Hash{ 209 backend.chain.GetBlockByNumber(4).Hash(), 210 backend.chain.GetBlockByNumber(0).Hash(), 211 }, 212 }, 213 // Check that requesting more than available is handled gracefully, even if mid skip 214 { 215 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 216 []common.Hash{ 217 backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 4).Hash(), 218 backend.chain.GetBlockByNumber(backend.chain.CurrentBlock().NumberU64() - 1).Hash(), 219 }, 220 }, { 221 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 222 []common.Hash{ 223 backend.chain.GetBlockByNumber(4).Hash(), 224 backend.chain.GetBlockByNumber(1).Hash(), 225 }, 226 }, 227 // Check a corner case where requesting more can iterate past the endpoints 228 { 229 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: 2}, Amount: 5, Reverse: true}, 230 []common.Hash{ 231 backend.chain.GetBlockByNumber(2).Hash(), 232 backend.chain.GetBlockByNumber(1).Hash(), 233 backend.chain.GetBlockByNumber(0).Hash(), 234 }, 235 }, 236 // Check a corner case where skipping overflow loops back into the chain start 237 { 238 &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, 239 []common.Hash{ 240 backend.chain.GetBlockByNumber(3).Hash(), 241 }, 242 }, 243 // Check a corner case where skipping overflow loops back to the same header 244 { 245 &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: backend.chain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, 246 []common.Hash{ 247 backend.chain.GetBlockByNumber(1).Hash(), 248 }, 249 }, 250 // Check that non existing headers aren't returned 251 { 252 &GetBlockHeadersPacket{Origin: HashOrNumber{Hash: unknown}, Amount: 1}, 253 []common.Hash{}, 254 }, { 255 &GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() + 1}, Amount: 1}, 256 []common.Hash{}, 257 }, 258 } 259 // Run each of the tests and verify the results against the chain 260 for i, tt := range tests { 261 // Collect the headers to expect in the response 262 var headers []*types.Header 263 for _, hash := range tt.expect { 264 headers = append(headers, backend.chain.GetBlockByHash(hash).Header()) 265 } 266 // Send the hash request and verify the response 267 p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{ 268 RequestId: 123, 269 GetBlockHeadersPacket: tt.query, 270 }) 271 if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{ 272 RequestId: 123, 273 BlockHeadersPacket: headers, 274 }); err != nil { 275 t.Errorf("test %d: headers mismatch: %v", i, err) 276 } 277 // If the test used number origins, repeat with hashes as the too 278 if tt.query.Origin.Hash == (common.Hash{}) { 279 if origin := backend.chain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { 280 tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 281 282 p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{ 283 RequestId: 456, 284 GetBlockHeadersPacket: tt.query, 285 }) 286 if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{ 287 RequestId: 456, 288 BlockHeadersPacket: headers, 289 }); err != nil { 290 t.Errorf("test %d by hash: headers mismatch: %v", i, err) 291 } 292 } 293 } 294 } 295} 296 297// Tests that block contents can be retrieved from a remote chain based on their hashes. 298func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) } 299 300func testGetBlockBodies(t *testing.T, protocol uint) { 301 t.Parallel() 302 303 backend := newTestBackend(maxBodiesServe + 15) 304 defer backend.close() 305 306 peer, _ := newTestPeer("peer", protocol, backend) 307 defer peer.close() 308 309 // Create a batch of tests for various scenarios 310 limit := maxBodiesServe 311 tests := []struct { 312 random int // Number of blocks to fetch randomly from the chain 313 explicit []common.Hash // Explicitly requested blocks 314 available []bool // Availability of explicitly requested blocks 315 expected int // Total number of existing blocks to expect 316 }{ 317 {1, nil, nil, 1}, // A single random block should be retrievable 318 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 319 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 320 {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 321 {0, []common.Hash{backend.chain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 322 {0, []common.Hash{backend.chain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 323 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 324 325 // Existing and non-existing blocks interleaved should not cause problems 326 {0, []common.Hash{ 327 {}, 328 backend.chain.GetBlockByNumber(1).Hash(), 329 {}, 330 backend.chain.GetBlockByNumber(10).Hash(), 331 {}, 332 backend.chain.GetBlockByNumber(100).Hash(), 333 {}, 334 }, []bool{false, true, false, true, false, true, false}, 3}, 335 } 336 // Run each of the tests and verify the results against the chain 337 for i, tt := range tests { 338 // Collect the hashes to request, and the response to expectva 339 var ( 340 hashes []common.Hash 341 bodies []*BlockBody 342 seen = make(map[int64]bool) 343 ) 344 for j := 0; j < tt.random; j++ { 345 for { 346 num := rand.Int63n(int64(backend.chain.CurrentBlock().NumberU64())) 347 if !seen[num] { 348 seen[num] = true 349 350 block := backend.chain.GetBlockByNumber(uint64(num)) 351 hashes = append(hashes, block.Hash()) 352 if len(bodies) < tt.expected { 353 bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 354 } 355 break 356 } 357 } 358 } 359 for j, hash := range tt.explicit { 360 hashes = append(hashes, hash) 361 if tt.available[j] && len(bodies) < tt.expected { 362 block := backend.chain.GetBlockByHash(hash) 363 bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 364 } 365 } 366 // Send the hash request and verify the response 367 p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{ 368 RequestId: 123, 369 GetBlockBodiesPacket: hashes, 370 }) 371 if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{ 372 RequestId: 123, 373 BlockBodiesPacket: bodies, 374 }); err != nil { 375 t.Errorf("test %d: bodies mismatch: %v", i, err) 376 } 377 } 378} 379 380// Tests that the state trie nodes can be retrieved based on hashes. 381func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) } 382 383func testGetNodeData(t *testing.T, protocol uint) { 384 t.Parallel() 385 386 // Define three accounts to simulate transactions with 387 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 388 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 389 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 390 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 391 392 signer := types.HomesteadSigner{} 393 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makers_test) 394 generator := func(i int, block *core.BlockGen) { 395 switch i { 396 case 0: 397 // In block 1, the test bank sends account #1 some ether. 398 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) 399 block.AddTx(tx) 400 case 1: 401 // In block 2, the test bank sends some more ether to account #1. 402 // acc1Addr passes it on to account #2. 403 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) 404 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) 405 block.AddTx(tx1) 406 block.AddTx(tx2) 407 case 2: 408 // Block 3 is empty but was mined by account #2. 409 block.SetCoinbase(acc2Addr) 410 block.SetExtra([]byte("yeehaw")) 411 case 3: 412 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 413 b2 := block.PrevBlock(1).Header() 414 b2.Extra = []byte("foo") 415 block.AddUncle(b2) 416 b3 := block.PrevBlock(2).Header() 417 b3.Extra = []byte("foo") 418 block.AddUncle(b3) 419 } 420 } 421 // Assemble the test environment 422 backend := newTestBackendWithGenerator(4, generator) 423 defer backend.close() 424 425 peer, _ := newTestPeer("peer", protocol, backend) 426 defer peer.close() 427 428 // Collect all state tree hashes. 429 var hashes []common.Hash 430 it := backend.db.NewIterator(nil, nil) 431 for it.Next() { 432 if key := it.Key(); len(key) == common.HashLength { 433 hashes = append(hashes, common.BytesToHash(key)) 434 } 435 } 436 it.Release() 437 438 // Request all hashes. 439 p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{ 440 RequestId: 123, 441 GetNodeDataPacket: hashes, 442 }) 443 msg, err := peer.app.ReadMsg() 444 if err != nil { 445 t.Fatalf("failed to read node data response: %v", err) 446 } 447 if msg.Code != NodeDataMsg { 448 t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg) 449 } 450 var res NodeDataPacket66 451 if err := msg.Decode(&res); err != nil { 452 t.Fatalf("failed to decode response node data: %v", err) 453 } 454 455 // Verify that all hashes correspond to the requested data. 456 data := res.NodeDataPacket 457 for i, want := range hashes { 458 if hash := crypto.Keccak256Hash(data[i]); hash != want { 459 t.Errorf("data hash mismatch: have %x, want %x", hash, want) 460 } 461 } 462 463 // Reconstruct state tree from the received data. 464 reconstructDB := rawdb.NewMemoryDatabase() 465 for i := 0; i < len(data); i++ { 466 rawdb.WriteTrieNode(reconstructDB, hashes[i], data[i]) 467 } 468 469 // Sanity check whether all state matches. 470 accounts := []common.Address{testAddr, acc1Addr, acc2Addr} 471 for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ { 472 root := backend.chain.GetBlockByNumber(i).Root() 473 reconstructed, _ := state.New(root, state.NewDatabase(reconstructDB), nil) 474 for j, acc := range accounts { 475 state, _ := backend.chain.StateAt(root) 476 bw := state.GetBalance(acc) 477 bh := reconstructed.GetBalance(acc) 478 479 if (bw == nil) != (bh == nil) { 480 t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 481 } 482 if bw != nil && bh != nil && bw.Cmp(bh) != 0 { 483 t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 484 } 485 } 486 } 487} 488 489// Tests that the transaction receipts can be retrieved based on hashes. 490func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) } 491 492func testGetBlockReceipts(t *testing.T, protocol uint) { 493 t.Parallel() 494 495 // Define three accounts to simulate transactions with 496 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 497 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 498 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 499 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 500 501 signer := types.HomesteadSigner{} 502 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 503 generator := func(i int, block *core.BlockGen) { 504 switch i { 505 case 0: 506 // In block 1, the test bank sends account #1 some ether. 507 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(10_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) 508 block.AddTx(tx) 509 case 1: 510 // In block 2, the test bank sends some more ether to account #1. 511 // acc1Addr passes it on to account #2. 512 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testKey) 513 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key) 514 block.AddTx(tx1) 515 block.AddTx(tx2) 516 case 2: 517 // Block 3 is empty but was mined by account #2. 518 block.SetCoinbase(acc2Addr) 519 block.SetExtra([]byte("yeehaw")) 520 case 3: 521 // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). 522 b2 := block.PrevBlock(1).Header() 523 b2.Extra = []byte("foo") 524 block.AddUncle(b2) 525 b3 := block.PrevBlock(2).Header() 526 b3.Extra = []byte("foo") 527 block.AddUncle(b3) 528 } 529 } 530 // Assemble the test environment 531 backend := newTestBackendWithGenerator(4, generator) 532 defer backend.close() 533 534 peer, _ := newTestPeer("peer", protocol, backend) 535 defer peer.close() 536 537 // Collect the hashes to request, and the response to expect 538 var ( 539 hashes []common.Hash 540 receipts [][]*types.Receipt 541 ) 542 for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ { 543 block := backend.chain.GetBlockByNumber(i) 544 545 hashes = append(hashes, block.Hash()) 546 receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash())) 547 } 548 // Send the hash request and verify the response 549 p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{ 550 RequestId: 123, 551 GetReceiptsPacket: hashes, 552 }) 553 if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{ 554 RequestId: 123, 555 ReceiptsPacket: receipts, 556 }); err != nil { 557 t.Errorf("receipts mismatch: %v", err) 558 } 559} 560