github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/core/chain_indexer_test.go (about) 1 package core 2 3 import ( 4 "fmt" 5 "math/big" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/neatio-net/neatio/chain/core/rawdb" 11 "github.com/neatio-net/neatio/chain/core/types" 12 "github.com/neatio-net/neatio/utilities/common" 13 ) 14 15 func TestChainIndexerSingle(t *testing.T) { 16 for i := 0; i < 10; i++ { 17 testChainIndexer(t, 1) 18 } 19 } 20 21 func TestChainIndexerWithChildren(t *testing.T) { 22 for i := 2; i < 8; i++ { 23 testChainIndexer(t, i) 24 } 25 } 26 27 func testChainIndexer(t *testing.T, count int) { 28 db := rawdb.NewMemoryDatabase() 29 defer db.Close() 30 31 backends := make([]*testChainIndexBackend, count) 32 for i := 0; i < count; i++ { 33 var ( 34 sectionSize = uint64(rand.Intn(100) + 1) 35 confirmsReq = uint64(rand.Intn(10)) 36 ) 37 backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} 38 backends[i].indexer = NewChainIndexer(db, rawdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) 39 40 if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { 41 t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0) 42 } 43 if i > 0 { 44 backends[i-1].indexer.AddChildIndexer(backends[i].indexer) 45 } 46 } 47 defer backends[0].indexer.Close() 48 49 notify := func(headNum, failNum uint64, reorg bool) { 50 backends[0].indexer.newHead(headNum, reorg) 51 if reorg { 52 for _, backend := range backends { 53 headNum = backend.reorg(headNum) 54 backend.assertSections() 55 } 56 return 57 } 58 var cascade bool 59 for _, backend := range backends { 60 headNum, cascade = backend.assertBlocks(headNum, failNum) 61 if !cascade { 62 break 63 } 64 backend.assertSections() 65 } 66 } 67 68 inject := func(number uint64) { 69 header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()} 70 if number > 0 { 71 header.ParentHash = rawdb.ReadCanonicalHash(db, number-1) 72 } 73 rawdb.WriteHeader(db, header) 74 rawdb.WriteCanonicalHash(db, header.Hash(), number) 75 } 76 77 for i := uint64(0); i <= 100; i++ { 78 inject(i) 79 } 80 notify(100, 100, false) 81 82 for i := uint64(101); i <= 1000; i++ { 83 inject(i) 84 notify(i, i, false) 85 } 86 87 notify(500, 500, true) 88 89 for i := uint64(501); i <= 1000; i++ { 90 inject(i) 91 notify(i, i, false) 92 } 93 for i := uint64(1001); i <= 1500; i++ { 94 inject(i) 95 } 96 97 notify(2000, 1500, false) 98 99 notify(1500, 1500, true) 100 101 for i := uint64(1501); i <= 2000; i++ { 102 inject(i) 103 notify(i, i, false) 104 } 105 } 106 107 type testChainIndexBackend struct { 108 t *testing.T 109 indexer *ChainIndexer 110 section, headerCnt, stored uint64 111 processCh chan uint64 112 } 113 114 func (b *testChainIndexBackend) assertSections() { 115 116 var sections uint64 117 for i := 0; i < 300; i++ { 118 sections, _, _ = b.indexer.Sections() 119 if sections == b.stored { 120 return 121 } 122 time.Sleep(10 * time.Millisecond) 123 } 124 b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored) 125 } 126 127 func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) { 128 var sections uint64 129 if headNum >= b.indexer.confirmsReq { 130 sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize 131 if sections > b.stored { 132 133 for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ { 134 if expectd > failNum { 135 136 var updating bool 137 for i := 0; i < 300; i++ { 138 b.indexer.lock.Lock() 139 updating = b.indexer.knownSections > b.indexer.storedSections 140 b.indexer.lock.Unlock() 141 if !updating { 142 break 143 } 144 time.Sleep(10 * time.Millisecond) 145 } 146 if updating { 147 b.t.Fatalf("update did not finish") 148 } 149 sections = expectd / b.indexer.sectionSize 150 break 151 } 152 select { 153 case <-time.After(10 * time.Second): 154 b.t.Fatalf("Expected processed block #%d, got nothing", expectd) 155 case processed := <-b.processCh: 156 if processed != expectd { 157 b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed) 158 } 159 } 160 } 161 b.stored = sections 162 } 163 } 164 if b.stored == 0 { 165 return 0, false 166 } 167 return b.stored*b.indexer.sectionSize - 1, true 168 } 169 170 func (b *testChainIndexBackend) reorg(headNum uint64) uint64 { 171 firstChanged := headNum / b.indexer.sectionSize 172 if firstChanged < b.stored { 173 b.stored = firstChanged 174 } 175 return b.stored * b.indexer.sectionSize 176 } 177 178 func (b *testChainIndexBackend) Reset(section uint64, prevHead common.Hash) error { 179 b.section = section 180 b.headerCnt = 0 181 return nil 182 } 183 184 func (b *testChainIndexBackend) Process(header *types.Header) { 185 b.headerCnt++ 186 if b.headerCnt > b.indexer.sectionSize { 187 b.t.Error("Processing too many headers") 188 } 189 190 select { 191 case <-time.After(10 * time.Second): 192 b.t.Fatal("Unexpected call to Process") 193 case b.processCh <- header.Number.Uint64(): 194 } 195 } 196 197 func (b *testChainIndexBackend) Commit() error { 198 if b.headerCnt != b.indexer.sectionSize { 199 b.t.Error("Not enough headers processed") 200 } 201 return nil 202 }