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