github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/core/chain_indexer_test.go (about) 1 // Copyright 2017 The go-simplechain Authors 2 // This file is part of the go-simplechain library. 3 // 4 // The go-simplechain 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-simplechain 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-simplechain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "context" 21 "fmt" 22 "math/big" 23 "math/rand" 24 "testing" 25 "time" 26 27 "github.com/bigzoro/my_simplechain/common" 28 "github.com/bigzoro/my_simplechain/core/rawdb" 29 "github.com/bigzoro/my_simplechain/core/types" 30 ) 31 32 // Runs multiple tests with randomized parameters. 33 func TestChainIndexerSingle(t *testing.T) { 34 for i := 0; i < 10; i++ { 35 testChainIndexer(t, 1) 36 } 37 } 38 39 // Runs multiple tests with randomized parameters and different number of 40 // chain backends. 41 func TestChainIndexerWithChildren(t *testing.T) { 42 for i := 2; i < 8; i++ { 43 testChainIndexer(t, i) 44 } 45 } 46 47 // testChainIndexer runs a test with either a single chain indexer or a chain of 48 // multiple backends. The section size and required confirmation count parameters 49 // are randomized. 50 func testChainIndexer(t *testing.T, count int) { 51 //内存型db 52 db := rawdb.NewMemoryDatabase() 53 54 defer db.Close() 55 56 // Create a chain of indexers and ensure they all report empty 57 backends := make([]*testChainIndexBackend, count) 58 59 for i := 0; i < count; i++ { 60 var ( 61 sectionSize = uint64(rand.Intn(100) + 1) 62 confirmsReq = uint64(rand.Intn(10)) 63 ) 64 backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} 65 66 backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) 67 68 if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { 69 t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0) 70 } 71 if i > 0 { 72 backends[i-1].indexer.AddChildIndexer(backends[i].indexer) 73 } 74 } 75 defer backends[0].indexer.Close() // parent indexer shuts down children 76 // notify pings the root indexer about a new head or reorg, then expect 77 // processed blocks if a section is processable 78 notify := func(headNum, failNum uint64, reorg bool) { 79 backends[0].indexer.newHead(headNum, reorg) 80 if reorg { 81 for _, backend := range backends { 82 headNum = backend.reorg(headNum) 83 backend.assertSections() 84 } 85 return 86 } 87 var cascade bool 88 for _, backend := range backends { 89 headNum, cascade = backend.assertBlocks(headNum, failNum) 90 if !cascade { 91 break 92 } 93 backend.assertSections() 94 } 95 } 96 // inject inserts a new random canonical header into the database directly 97 inject := func(number uint64) { 98 header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()} 99 if number > 0 { 100 header.ParentHash = rawdb.ReadCanonicalHash(db, number-1) 101 } 102 rawdb.WriteHeader(db, header) 103 rawdb.WriteCanonicalHash(db, header.Hash(), number) 104 } 105 // Start indexer with an already existing chain 106 for i := uint64(0); i <= 100; i++ { 107 inject(i) 108 } 109 110 notify(100, 100, false) 111 112 // Add new blocks one by one 113 for i := uint64(101); i <= 1000; i++ { 114 inject(i) 115 notify(i, i, false) 116 } 117 // Do a reorg 118 notify(500, 500, true) 119 // Create new fork 120 for i := uint64(501); i <= 1000; i++ { 121 inject(i) 122 notify(i, i, false) 123 } 124 for i := uint64(1001); i <= 1500; i++ { 125 inject(i) 126 } 127 // Failed processing scenario where less blocks are available than notified 128 notify(2000, 1500, false) 129 130 // Notify about a reorg (which could have caused the missing blocks if happened during processing) 131 notify(1500, 1500, true) 132 133 // Create new fork 134 for i := uint64(1501); i <= 2000; i++ { 135 inject(i) 136 notify(i, i, false) 137 } 138 } 139 140 // testChainIndexBackend implements ChainIndexerBackend 141 type testChainIndexBackend struct { 142 t *testing.T 143 indexer *ChainIndexer 144 section, headerCnt, stored uint64 145 processCh chan uint64 146 } 147 148 // assertSections verifies if a chain indexer has the correct number of section. 149 func (b *testChainIndexBackend) assertSections() { 150 // Keep trying for 3 seconds if it does not match 151 var sections uint64 152 for i := 0; i < 300; i++ { 153 sections, _, _ = b.indexer.Sections() 154 if sections == b.stored { 155 return 156 } 157 time.Sleep(10 * time.Millisecond) 158 } 159 b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored) 160 } 161 162 // assertBlocks expects processing calls after new blocks have arrived. If the 163 // failNum < headNum then we are simulating a scenario where a reorg has happened 164 // after the processing has started and the processing of a section fails. 165 func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) { 166 var sections uint64 167 if headNum >= b.indexer.confirmsReq { 168 sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize 169 if sections > b.stored { 170 // expect processed blocks 171 for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ { 172 if expectd > failNum { 173 // rolled back after processing started, no more process calls expected 174 // wait until updating is done to make sure that processing actually fails 175 var updating bool 176 for i := 0; i < 300; i++ { 177 b.indexer.lock.Lock() 178 updating = b.indexer.knownSections > b.indexer.storedSections 179 b.indexer.lock.Unlock() 180 if !updating { 181 break 182 } 183 time.Sleep(10 * time.Millisecond) 184 } 185 if updating { 186 b.t.Fatalf("update did not finish") 187 } 188 sections = expectd / b.indexer.sectionSize 189 break 190 } 191 select { 192 case <-time.After(10 * time.Second): 193 b.t.Fatalf("Expected processed block #%d, got nothing", expectd) 194 case processed := <-b.processCh: 195 if processed != expectd { 196 b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed) 197 } 198 } 199 } 200 b.stored = sections 201 } 202 } 203 if b.stored == 0 { 204 return 0, false 205 } 206 return b.stored*b.indexer.sectionSize - 1, true 207 } 208 209 func (b *testChainIndexBackend) reorg(headNum uint64) uint64 { 210 firstChanged := (headNum + 1) / b.indexer.sectionSize 211 if firstChanged < b.stored { 212 b.stored = firstChanged 213 } 214 return b.stored * b.indexer.sectionSize 215 } 216 217 func (b *testChainIndexBackend) Reset(ctx context.Context, section uint64, prevHead common.Hash) error { 218 b.section = section 219 b.headerCnt = 0 220 return nil 221 } 222 223 func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Header) error { 224 b.headerCnt++ 225 if b.headerCnt > b.indexer.sectionSize { 226 b.t.Error("Processing too many headers") 227 } 228 //t.processCh <- header.Number.Uint64() 229 select { 230 case <-time.After(10 * time.Second): 231 b.t.Fatal("Unexpected call to Process") 232 case b.processCh <- header.Number.Uint64(): 233 } 234 return nil 235 } 236 237 func (b *testChainIndexBackend) Commit() error { 238 if b.headerCnt != b.indexer.sectionSize { 239 b.t.Error("Not enough headers processed") 240 } 241 return nil 242 }