github.com/m3shine/gochain@v2.2.26+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 "context" 21 "math" 22 "math/big" 23 "math/rand" 24 "testing" 25 26 "github.com/gochain-io/gochain/common" 27 "github.com/gochain-io/gochain/core" 28 "github.com/gochain-io/gochain/core/state" 29 "github.com/gochain-io/gochain/core/types" 30 "github.com/gochain-io/gochain/crypto" 31 "github.com/gochain-io/gochain/eth/downloader" 32 "github.com/gochain-io/gochain/ethdb" 33 "github.com/gochain-io/gochain/p2p" 34 "github.com/gochain-io/gochain/params" 35 ) 36 37 // Tests that protocol versions and modes of operations are matched up properly. 38 func TestProtocolCompatibility(t *testing.T) { 39 ctx := context.Background() 40 // Define the compatibility chart 41 tests := []struct { 42 version uint 43 mode downloader.SyncMode 44 compatible bool 45 }{ 46 {61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true}, 47 {61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true}, 48 } 49 // Make sure anything we screw up is restored 50 backup := ProtocolVersions 51 defer func() { ProtocolVersions = backup }() 52 53 // Try all available compatibility configs and check for errors 54 for i, tt := range tests { 55 ProtocolVersions = []uint{tt.version} 56 57 pm, _, err := newTestProtocolManager(ctx, tt.mode, 0, nil, nil) 58 if pm != nil { 59 defer pm.Stop() 60 } 61 if (err == nil && !tt.compatible) || (err != nil && tt.compatible) { 62 t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible) 63 } 64 } 65 } 66 67 // Tests that block headers can be retrieved from a remote chain based on user queries. 68 func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) } 69 func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } 70 71 func testGetBlockHeaders(t *testing.T, protocol int) { 72 ctx := context.Background() 73 pm, _ := newTestProtocolManagerMust(ctx, t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) 74 peer, _ := newTestPeer(ctx, "peer", protocol, pm, true) 75 defer peer.close() 76 77 // Create a "random" unknown hash for testing 78 var unknown common.Hash 79 for i := range unknown { 80 unknown[i] = byte(i) 81 } 82 // Create a batch of tests for various scenarios 83 limit := uint64(downloader.MaxHeaderFetch) 84 tests := []struct { 85 query *getBlockHeadersData // The query to execute for header retrieval 86 expect []common.Hash // The hashes of the block whose headers are expected 87 }{ 88 // A single random block should be retrievable by hash and number too 89 { 90 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 91 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 92 }, { 93 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, 94 []common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, 95 }, 96 // Multiple headers should be retrievable in both directions 97 { 98 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, 99 []common.Hash{ 100 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 101 pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), 102 pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), 103 }, 104 }, { 105 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 106 []common.Hash{ 107 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 108 pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), 109 pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), 110 }, 111 }, 112 // Multiple headers with skip lists should be retrievable 113 { 114 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 115 []common.Hash{ 116 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 117 pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), 118 pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), 119 }, 120 }, { 121 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 122 []common.Hash{ 123 pm.blockchain.GetBlockByNumber(limit / 2).Hash(), 124 pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), 125 pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), 126 }, 127 }, 128 // The chain endpoints should be retrievable 129 { 130 &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, 131 []common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, 132 }, { 133 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, 134 []common.Hash{pm.blockchain.CurrentBlock().Hash()}, 135 }, 136 // Ensure protocol limits are honored 137 { 138 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 139 pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit), 140 }, 141 // Check that requesting more than available is handled gracefully 142 { 143 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 144 []common.Hash{ 145 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 146 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), 147 }, 148 }, { 149 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 150 []common.Hash{ 151 pm.blockchain.GetBlockByNumber(4).Hash(), 152 pm.blockchain.GetBlockByNumber(0).Hash(), 153 }, 154 }, 155 // Check that requesting more than available is handled gracefully, even if mid skip 156 { 157 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 158 []common.Hash{ 159 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), 160 pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), 161 }, 162 }, { 163 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 164 []common.Hash{ 165 pm.blockchain.GetBlockByNumber(4).Hash(), 166 pm.blockchain.GetBlockByNumber(1).Hash(), 167 }, 168 }, 169 // Check a corner case where requesting more can iterate past the endpoints 170 { 171 &getBlockHeadersData{Origin: hashOrNumber{Number: 2}, Amount: 5, Reverse: true}, 172 []common.Hash{ 173 pm.blockchain.GetBlockByNumber(2).Hash(), 174 pm.blockchain.GetBlockByNumber(1).Hash(), 175 pm.blockchain.GetBlockByNumber(0).Hash(), 176 }, 177 }, 178 // Check a corner case where skipping overflow loops back into the chain start 179 { 180 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1}, 181 []common.Hash{ 182 pm.blockchain.GetBlockByNumber(3).Hash(), 183 }, 184 }, 185 // Check a corner case where skipping overflow loops back to the same header 186 { 187 &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64}, 188 []common.Hash{ 189 pm.blockchain.GetBlockByNumber(1).Hash(), 190 }, 191 }, 192 // Check that non existing headers aren't returned 193 { 194 &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, 195 []common.Hash{}, 196 }, { 197 &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1}, 198 []common.Hash{}, 199 }, 200 } 201 // Run each of the tests and verify the results against the chain 202 for i, tt := range tests { 203 // Collect the headers to expect in the response 204 headers := []*types.Header{} 205 for _, hash := range tt.expect { 206 headers = append(headers, pm.blockchain.GetBlockByHash(hash).Header()) 207 } 208 // Send the hash request and verify the response 209 p2p.Send(peer.app, 0x03, tt.query) 210 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 211 t.Errorf("test %d: headers mismatch: %v", i, err) 212 } 213 // If the test used number origins, repeat with hashes as the too 214 if tt.query.Origin.Hash == (common.Hash{}) { 215 if origin := pm.blockchain.GetBlockByNumber(tt.query.Origin.Number); origin != nil { 216 tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0 217 218 p2p.Send(peer.app, 0x03, tt.query) 219 if err := p2p.ExpectMsg(peer.app, 0x04, headers); err != nil { 220 t.Errorf("test %d: headers mismatch: %v", i, err) 221 } 222 } 223 } 224 } 225 } 226 227 // Tests that block contents can be retrieved from a remote chain based on their hashes. 228 func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) } 229 func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } 230 231 func testGetBlockBodies(t *testing.T, protocol int) { 232 ctx := context.Background() 233 pm, _ := newTestProtocolManagerMust(ctx, t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) 234 peer, _ := newTestPeer(ctx, "peer", protocol, pm, true) 235 defer peer.close() 236 237 // Create a batch of tests for various scenarios 238 limit := downloader.MaxBlockFetch 239 tests := []struct { 240 random int // Number of blocks to fetch randomly from the chain 241 explicit []common.Hash // Explicitly requested blocks 242 available []bool // Availability of explicitly requested blocks 243 expected int // Total number of existing blocks to expect 244 }{ 245 {1, nil, nil, 1}, // A single random block should be retrievable 246 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 247 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 248 {limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 249 {0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 250 {0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 251 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 252 253 // Existing and non-existing blocks interleaved should not cause problems 254 {0, []common.Hash{ 255 {}, 256 pm.blockchain.GetBlockByNumber(1).Hash(), 257 {}, 258 pm.blockchain.GetBlockByNumber(10).Hash(), 259 {}, 260 pm.blockchain.GetBlockByNumber(100).Hash(), 261 {}, 262 }, []bool{false, true, false, true, false, true, false}, 3}, 263 } 264 // Run each of the tests and verify the results against the chain 265 for i, tt := range tests { 266 // Collect the hashes to request, and the response to expect 267 hashes, seen := []common.Hash{}, make(map[int64]bool) 268 bodies := []*blockBody{} 269 270 for j := 0; j < tt.random; j++ { 271 for { 272 num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64())) 273 if !seen[num] { 274 seen[num] = true 275 276 block := pm.blockchain.GetBlockByNumber(uint64(num)) 277 hashes = append(hashes, block.Hash()) 278 if len(bodies) < tt.expected { 279 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 280 } 281 break 282 } 283 } 284 } 285 for j, hash := range tt.explicit { 286 hashes = append(hashes, hash) 287 if tt.available[j] && len(bodies) < tt.expected { 288 block := pm.blockchain.GetBlockByHash(hash) 289 bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) 290 } 291 } 292 // Send the hash request and verify the response 293 p2p.Send(peer.app, 0x05, hashes) 294 if err := p2p.ExpectMsg(peer.app, 0x06, bodies); err != nil { 295 t.Errorf("test %d: bodies mismatch: %v", i, err) 296 } 297 } 298 } 299 300 // Tests that the node state database can be retrieved based on hashes. 301 func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) } 302 303 func testGetNodeData(t *testing.T, protocol int) { 304 ctx := context.Background() 305 // Define three accounts to simulate transactions with 306 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 307 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 308 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 309 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 310 311 signer := types.HomesteadSigner{} 312 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 313 generator := func(ctx context.Context, i int, block *core.BlockGen) { 314 switch i { 315 case 0: 316 // In block 1, the test bank sends account #1 some ether. 317 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 318 block.AddTx(ctx, tx) 319 case 1: 320 // In block 2, the test bank sends some more ether to account #1. 321 // acc1Addr passes it on to account #2. 322 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 323 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 324 block.AddTx(ctx, tx1) 325 block.AddTx(ctx, tx2) 326 case 2: 327 // Block 3 is empty but was mined by account #2. 328 block.SetCoinbase(acc2Addr) 329 block.SetExtra([]byte("yeehaw")) 330 case 3: 331 // Block 4 includes modified extra data. 332 b2 := block.PrevBlock(1).Header() 333 b2.Extra = []byte("foo") 334 b3 := block.PrevBlock(2).Header() 335 b3.Extra = []byte("foo") 336 } 337 } 338 // Assemble the test environment 339 pm, db := newTestProtocolManagerMust(ctx, t, downloader.FullSync, 4, generator, nil) 340 peer, _ := newTestPeer(ctx, "peer", protocol, pm, true) 341 defer peer.close() 342 343 // Fetch for now the entire chain db 344 hashes := []common.Hash{} 345 for _, key := range db.Keys() { 346 if len(key) == len(common.Hash{}) { 347 hashes = append(hashes, common.BytesToHash(key)) 348 } 349 } 350 p2p.Send(peer.app, 0x0d, hashes) 351 msg, err := peer.app.ReadMsg() 352 if err != nil { 353 t.Fatalf("failed to read node data response: %v", err) 354 } 355 if msg.Code != 0x0e { 356 t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c) 357 } 358 var data [][]byte 359 if err := msg.Decode(&data); err != nil { 360 t.Fatalf("failed to decode response node data: %v", err) 361 } 362 // Verify that all hashes correspond to the requested data, and reconstruct a state tree 363 for i, want := range hashes { 364 if hash := crypto.Keccak256Hash(data[i]); hash != want { 365 t.Errorf("data hash mismatch: have %x, want %x", hash, want) 366 } 367 } 368 statedb := ethdb.NewMemDatabase() 369 for i := 0; i < len(data); i++ { 370 statedb.Put(hashes[i].Bytes(), data[i]) 371 } 372 accounts := []common.Address{testBank, acc1Addr, acc2Addr} 373 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 374 trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb)) 375 376 for j, acc := range accounts { 377 state, _ := pm.blockchain.State() 378 bw := state.GetBalance(acc) 379 bh := trie.GetBalance(acc) 380 if (bw != nil && bh == nil) || (bw == nil && bh != nil) { 381 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 382 } 383 if bw != nil && bh != nil && bw.Cmp(bw) != 0 { 384 t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw) 385 } 386 } 387 } 388 } 389 390 // Tests that the transaction receipts can be retrieved based on hashes. 391 func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) } 392 393 func testGetReceipt(t *testing.T, protocol int) { 394 ctx := context.Background() 395 // Define three accounts to simulate transactions with 396 acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 397 acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") 398 acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey) 399 acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey) 400 401 signer := types.HomesteadSigner{} 402 // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test) 403 generator := func(ctx context.Context, i int, block *core.BlockGen) { 404 switch i { 405 case 0: 406 // In block 1, the test bank sends account #1 some ether. 407 tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) 408 block.AddTx(ctx, tx) 409 case 1: 410 // In block 2, the test bank sends some more ether to account #1. 411 // acc1Addr passes it on to account #2. 412 tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) 413 tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) 414 block.AddTx(ctx, tx1) 415 block.AddTx(ctx, tx2) 416 case 2: 417 // Block 3 is empty but was mined by account #2. 418 block.SetCoinbase(acc2Addr) 419 block.SetExtra([]byte("yeehaw")) 420 case 3: 421 // Block 4 includes modified extra data. 422 b2 := block.PrevBlock(1).Header() 423 b2.Extra = []byte("foo") 424 b3 := block.PrevBlock(2).Header() 425 b3.Extra = []byte("foo") 426 } 427 } 428 // Assemble the test environment 429 pm, _ := newTestProtocolManagerMust(ctx, t, downloader.FullSync, 4, generator, nil) 430 peer, _ := newTestPeer(ctx, "peer", protocol, pm, true) 431 defer peer.close() 432 433 // Collect the hashes to request, and the response to expect 434 hashes, receipts := []common.Hash{}, []types.Receipts{} 435 for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { 436 block := pm.blockchain.GetBlockByNumber(i) 437 438 hashes = append(hashes, block.Hash()) 439 receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash())) 440 } 441 // Send the hash request and verify the response 442 p2p.Send(peer.app, 0x0f, hashes) 443 if err := p2p.ExpectMsg(peer.app, 0x10, receipts); err != nil { 444 t.Errorf("receipts mismatch: %v", err) 445 } 446 }