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