github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/handler_test.go (about) 1 // Copyright 2016 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "bytes" 21 "math/big" 22 "math/rand" 23 "testing" 24 "time" 25 26 "github.com/SmartMeshFoundation/Spectrum/common" 27 "github.com/SmartMeshFoundation/Spectrum/consensus/ethash" 28 "github.com/SmartMeshFoundation/Spectrum/core" 29 "github.com/SmartMeshFoundation/Spectrum/core/types" 30 "github.com/SmartMeshFoundation/Spectrum/crypto" 31 "github.com/SmartMeshFoundation/Spectrum/eth/downloader" 32 "github.com/SmartMeshFoundation/Spectrum/ethdb" 33 "github.com/SmartMeshFoundation/Spectrum/light" 34 "github.com/SmartMeshFoundation/Spectrum/p2p" 35 "github.com/SmartMeshFoundation/Spectrum/params" 36 "github.com/SmartMeshFoundation/Spectrum/rlp" 37 "github.com/SmartMeshFoundation/Spectrum/trie" 38 ) 39 40 func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error { 41 type resp struct { 42 ReqID, BV uint64 43 Data interface{} 44 } 45 return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data}) 46 } 47 48 func testCheckProof(t *testing.T, exp *light.NodeSet, got light.NodeList) { 49 if exp.KeyCount() > len(got) { 50 t.Errorf("proof has fewer nodes than expected") 51 return 52 } 53 if exp.KeyCount() < len(got) { 54 t.Errorf("proof has more nodes than expected") 55 return 56 } 57 for _, node := range got { 58 n, _ := exp.Get(crypto.Keccak256(node)) 59 if !bytes.Equal(n, node) { 60 t.Errorf("proof contents mismatch") 61 return 62 } 63 } 64 } 65 66 // Tests that block headers can be retrieved from a remote chain based on user queries. 67 func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) } 68 69 func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) } 70 71 func testGetBlockHeaders(t *testing.T, protocol int) { 72 db, _ := ethdb.NewMemDatabase() 73 pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil, nil, db) 74 bc := pm.blockchain.(*core.BlockChain) 75 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 76 defer peer.close() 77 78 // Create a "random" unknown hash for testing 79 var unknown common.Hash 80 for i := range unknown { 81 unknown[i] = byte(i) 82 } 83 // Create a batch of tests for various scenarios 84 limit := uint64(MaxHeaderFetch) 85 tests := []struct { 86 query *getBlockHeadersData // The query to execute for header retrieval 87 expect []common.Hash // The hashes of the block whose headers are expected 88 }{ 89 // A single random block should be retrievable by hash and number too 90 { 91 &getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, 92 []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, 93 }, { 94 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, 95 []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, 96 }, 97 // Multiple headers should be retrievable in both directions 98 { 99 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, 100 []common.Hash{ 101 bc.GetBlockByNumber(limit / 2).Hash(), 102 bc.GetBlockByNumber(limit/2 + 1).Hash(), 103 bc.GetBlockByNumber(limit/2 + 2).Hash(), 104 }, 105 }, { 106 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, 107 []common.Hash{ 108 bc.GetBlockByNumber(limit / 2).Hash(), 109 bc.GetBlockByNumber(limit/2 - 1).Hash(), 110 bc.GetBlockByNumber(limit/2 - 2).Hash(), 111 }, 112 }, 113 // Multiple headers with skip lists should be retrievable 114 { 115 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, 116 []common.Hash{ 117 bc.GetBlockByNumber(limit / 2).Hash(), 118 bc.GetBlockByNumber(limit/2 + 4).Hash(), 119 bc.GetBlockByNumber(limit/2 + 8).Hash(), 120 }, 121 }, { 122 &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, 123 []common.Hash{ 124 bc.GetBlockByNumber(limit / 2).Hash(), 125 bc.GetBlockByNumber(limit/2 - 4).Hash(), 126 bc.GetBlockByNumber(limit/2 - 8).Hash(), 127 }, 128 }, 129 // The chain endpoints should be retrievable 130 { 131 &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, 132 []common.Hash{bc.GetBlockByNumber(0).Hash()}, 133 }, { 134 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, 135 []common.Hash{bc.CurrentBlock().Hash()}, 136 }, 137 // Ensure protocol limits are honored 138 /*{ 139 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, 140 bc.GetBlockHashesFromHash(bc.CurrentBlock().Hash(), limit), 141 },*/ 142 // Check that requesting more than available is handled gracefully 143 { 144 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, 145 []common.Hash{ 146 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), 147 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(), 148 }, 149 }, { 150 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, 151 []common.Hash{ 152 bc.GetBlockByNumber(4).Hash(), 153 bc.GetBlockByNumber(0).Hash(), 154 }, 155 }, 156 // Check that requesting more than available is handled gracefully, even if mid skip 157 { 158 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, 159 []common.Hash{ 160 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), 161 bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(), 162 }, 163 }, { 164 &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, 165 []common.Hash{ 166 bc.GetBlockByNumber(4).Hash(), 167 bc.GetBlockByNumber(1).Hash(), 168 }, 169 }, 170 // Check that non existing headers aren't returned 171 { 172 &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, 173 []common.Hash{}, 174 }, { 175 &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, 176 []common.Hash{}, 177 }, 178 } 179 // Run each of the tests and verify the results against the chain 180 var reqID uint64 181 for i, tt := range tests { 182 // Collect the headers to expect in the response 183 headers := []*types.Header{} 184 for _, hash := range tt.expect { 185 headers = append(headers, bc.GetHeaderByHash(hash)) 186 } 187 // Send the hash request and verify the response 188 reqID++ 189 cost := peer.GetRequestCost(GetBlockHeadersMsg, int(tt.query.Amount)) 190 sendRequest(peer.app, GetBlockHeadersMsg, reqID, cost, tt.query) 191 if err := expectResponse(peer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { 192 t.Errorf("test %d: headers mismatch: %v", i, err) 193 } 194 } 195 } 196 197 // Tests that block contents can be retrieved from a remote chain based on their hashes. 198 func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) } 199 200 func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) } 201 202 func testGetBlockBodies(t *testing.T, protocol int) { 203 db, _ := ethdb.NewMemDatabase() 204 pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil, nil, db) 205 bc := pm.blockchain.(*core.BlockChain) 206 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 207 defer peer.close() 208 209 // Create a batch of tests for various scenarios 210 limit := MaxBodyFetch 211 tests := []struct { 212 random int // Number of blocks to fetch randomly from the chain 213 explicit []common.Hash // Explicitly requested blocks 214 available []bool // Availability of explicitly requested blocks 215 expected int // Total number of existing blocks to expect 216 }{ 217 {1, nil, nil, 1}, // A single random block should be retrievable 218 {10, nil, nil, 10}, // Multiple random blocks should be retrievable 219 {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable 220 //{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned 221 {0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable 222 {0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable 223 {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned 224 225 // Existing and non-existing blocks interleaved should not cause problems 226 {0, []common.Hash{ 227 {}, 228 bc.GetBlockByNumber(1).Hash(), 229 {}, 230 bc.GetBlockByNumber(10).Hash(), 231 {}, 232 bc.GetBlockByNumber(100).Hash(), 233 {}, 234 }, []bool{false, true, false, true, false, true, false}, 3}, 235 } 236 // Run each of the tests and verify the results against the chain 237 var reqID uint64 238 for i, tt := range tests { 239 // Collect the hashes to request, and the response to expect 240 hashes, seen := []common.Hash{}, make(map[int64]bool) 241 bodies := []*types.Body{} 242 243 for j := 0; j < tt.random; j++ { 244 for { 245 num := rand.Int63n(int64(bc.CurrentBlock().NumberU64())) 246 if !seen[num] { 247 seen[num] = true 248 249 block := bc.GetBlockByNumber(uint64(num)) 250 hashes = append(hashes, block.Hash()) 251 if len(bodies) < tt.expected { 252 bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) 253 } 254 break 255 } 256 } 257 } 258 for j, hash := range tt.explicit { 259 hashes = append(hashes, hash) 260 if tt.available[j] && len(bodies) < tt.expected { 261 block := bc.GetBlockByHash(hash) 262 bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) 263 } 264 } 265 reqID++ 266 // Send the hash request and verify the response 267 cost := peer.GetRequestCost(GetBlockBodiesMsg, len(hashes)) 268 sendRequest(peer.app, GetBlockBodiesMsg, reqID, cost, hashes) 269 if err := expectResponse(peer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { 270 t.Errorf("test %d: bodies mismatch: %v", i, err) 271 } 272 } 273 } 274 275 // Tests that the contract codes can be retrieved based on account addresses. 276 func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) } 277 278 func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) } 279 280 func testGetCode(t *testing.T, protocol int) { 281 // Assemble the test environment 282 db, _ := ethdb.NewMemDatabase() 283 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 284 bc := pm.blockchain.(*core.BlockChain) 285 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 286 defer peer.close() 287 288 var codereqs []*CodeReq 289 var codes [][]byte 290 291 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 292 header := bc.GetHeaderByNumber(i) 293 req := &CodeReq{ 294 BHash: header.Hash(), 295 AccKey: crypto.Keccak256(testContractAddr[:]), 296 } 297 codereqs = append(codereqs, req) 298 if i >= testContractDeployed { 299 codes = append(codes, testContractCodeDeployed) 300 } 301 } 302 303 cost := peer.GetRequestCost(GetCodeMsg, len(codereqs)) 304 sendRequest(peer.app, GetCodeMsg, 42, cost, codereqs) 305 if err := expectResponse(peer.app, CodeMsg, 42, testBufLimit, codes); err != nil { 306 t.Errorf("codes mismatch: %v", err) 307 } 308 } 309 310 // Tests that the transaction receipts can be retrieved based on hashes. 311 func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) } 312 313 func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) } 314 315 func testGetReceipt(t *testing.T, protocol int) { 316 // Assemble the test environment 317 db, _ := ethdb.NewMemDatabase() 318 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 319 bc := pm.blockchain.(*core.BlockChain) 320 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 321 defer peer.close() 322 323 // Collect the hashes to request, and the response to expect 324 hashes, receipts := []common.Hash{}, []types.Receipts{} 325 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 326 block := bc.GetBlockByNumber(i) 327 328 hashes = append(hashes, block.Hash()) 329 receipts = append(receipts, core.GetBlockReceipts(db, block.Hash(), block.NumberU64())) 330 } 331 // Send the hash request and verify the response 332 cost := peer.GetRequestCost(GetReceiptsMsg, len(hashes)) 333 sendRequest(peer.app, GetReceiptsMsg, 42, cost, hashes) 334 if err := expectResponse(peer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { 335 t.Errorf("receipts mismatch: %v", err) 336 } 337 } 338 339 // Tests that trie merkle proofs can be retrieved 340 func TestGetProofsLes1(t *testing.T) { testGetProofs(t, 1) } 341 342 func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) } 343 344 func testGetProofs(t *testing.T, protocol int) { 345 // Assemble the test environment 346 db, _ := ethdb.NewMemDatabase() 347 pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) 348 bc := pm.blockchain.(*core.BlockChain) 349 peer, _ := newTestPeer(t, "peer", protocol, pm, true) 350 defer peer.close() 351 352 var ( 353 proofreqs []ProofReq 354 proofsV1 [][]rlp.RawValue 355 ) 356 proofsV2 := light.NewNodeSet() 357 358 accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}} 359 for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { 360 header := bc.GetHeaderByNumber(i) 361 root := header.Root 362 trie, _ := trie.New(root, db) 363 364 for _, acc := range accounts { 365 req := ProofReq{ 366 BHash: header.Hash(), 367 Key: crypto.Keccak256(acc[:]), 368 } 369 proofreqs = append(proofreqs, req) 370 371 switch protocol { 372 case 1: 373 var proof light.NodeList 374 trie.Prove(crypto.Keccak256(acc[:]), 0, &proof) 375 proofsV1 = append(proofsV1, proof) 376 case 2: 377 trie.Prove(crypto.Keccak256(acc[:]), 0, proofsV2) 378 } 379 } 380 } 381 // Send the proof request and verify the response 382 switch protocol { 383 case 1: 384 cost := peer.GetRequestCost(GetProofsV1Msg, len(proofreqs)) 385 sendRequest(peer.app, GetProofsV1Msg, 42, cost, proofreqs) 386 if err := expectResponse(peer.app, ProofsV1Msg, 42, testBufLimit, proofsV1); err != nil { 387 t.Errorf("proofs mismatch: %v", err) 388 } 389 case 2: 390 cost := peer.GetRequestCost(GetProofsV2Msg, len(proofreqs)) 391 sendRequest(peer.app, GetProofsV2Msg, 42, cost, proofreqs) 392 msg, err := peer.app.ReadMsg() 393 if err != nil { 394 t.Errorf("Message read error: %v", err) 395 } 396 var resp struct { 397 ReqID, BV uint64 398 Data light.NodeList 399 } 400 if err := msg.Decode(&resp); err != nil { 401 t.Errorf("reply decode error: %v", err) 402 } 403 if msg.Code != ProofsV2Msg { 404 t.Errorf("Message code mismatch") 405 } 406 if resp.ReqID != 42 { 407 t.Errorf("ReqID mismatch") 408 } 409 if resp.BV != testBufLimit { 410 t.Errorf("BV mismatch") 411 } 412 testCheckProof(t, proofsV2, resp.Data) 413 } 414 } 415 416 func TestTransactionStatusLes2(t *testing.T) { 417 db, _ := ethdb.NewMemDatabase() 418 pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db) 419 chain := pm.blockchain.(*core.BlockChain) 420 config := core.DefaultTxPoolConfig 421 config.Journal = "" 422 txpool := core.NewTxPool(config, params.TestChainConfig, chain) 423 pm.txpool = txpool 424 peer, _ := newTestPeer(t, "peer", 2, pm, true) 425 defer peer.close() 426 427 var reqID uint64 428 429 test := func(tx *types.Transaction, send bool, expStatus txStatus) { 430 reqID++ 431 if send { 432 cost := peer.GetRequestCost(SendTxV2Msg, 1) 433 sendRequest(peer.app, SendTxV2Msg, reqID, cost, types.Transactions{tx}) 434 } else { 435 cost := peer.GetRequestCost(GetTxStatusMsg, 1) 436 sendRequest(peer.app, GetTxStatusMsg, reqID, cost, []common.Hash{tx.Hash()}) 437 } 438 if err := expectResponse(peer.app, TxStatusMsg, reqID, testBufLimit, []txStatus{expStatus}); err != nil { 439 t.Errorf("transaction status mismatch") 440 } 441 } 442 443 signer := types.HomesteadSigner{} 444 445 // test error status by sending an underpriced transaction 446 tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey) 447 test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced}) 448 449 tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), bigTxGas, big.NewInt(100000000000), nil), signer, testBankKey) 450 test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown 451 test(tx1, true, txStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending 452 test(tx1, true, txStatus{Status: core.TxStatusPending}) // adding it again should not return an error 453 454 tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), bigTxGas, big.NewInt(100000000000), nil), signer, testBankKey) 455 tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), bigTxGas, big.NewInt(100000000000), nil), signer, testBankKey) 456 // send transactions in the wrong order, tx3 should be queued 457 test(tx3, true, txStatus{Status: core.TxStatusQueued}) 458 test(tx2, true, txStatus{Status: core.TxStatusPending}) 459 // query again, now tx3 should be pending too 460 test(tx3, false, txStatus{Status: core.TxStatusPending}) 461 462 // generate and add a block with tx1 and tx2 included 463 gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) { 464 block.AddTx(tx1) 465 block.AddTx(tx2) 466 }) 467 if _, err := chain.InsertChain(gchain); err != nil { 468 panic(err) 469 } 470 // wait until TxPool processes the inserted block 471 for i := 0; i < 10; i++ { 472 if pending, _ := txpool.Stats(); pending == 1 { 473 break 474 } 475 time.Sleep(100 * time.Millisecond) 476 } 477 if pending, _ := txpool.Stats(); pending != 1 { 478 t.Fatalf("pending count mismatch: have %d, want 1", pending) 479 } 480 481 // check if their status is included now 482 block1hash := core.GetCanonicalHash(db, 1) 483 test(tx1, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}}) 484 test(tx2, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}}) 485 486 // create a reorg that rolls them back 487 gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {}) 488 if _, err := chain.InsertChain(gchain); err != nil { 489 panic(err) 490 } 491 // wait until TxPool processes the reorg 492 for i := 0; i < 10; i++ { 493 if pending, _ := txpool.Stats(); pending == 3 { 494 break 495 } 496 time.Sleep(100 * time.Millisecond) 497 } 498 if pending, _ := txpool.Stats(); pending != 3 { 499 t.Fatalf("pending count mismatch: have %d, want 3", pending) 500 } 501 // check if their status is pending again 502 test(tx1, false, txStatus{Status: core.TxStatusPending}) 503 test(tx2, false, txStatus{Status: core.TxStatusPending}) 504 }