github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/core/chain_indexer_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:35</date> 10 //</624450078433611776> 11 12 13 package core 14 15 import ( 16 "context" 17 "fmt" 18 "math/big" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/core/rawdb" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/ethdb" 27 ) 28 29 //使用随机参数运行多个测试。 30 func TestChainIndexerSingle(t *testing.T) { 31 for i := 0; i < 10; i++ { 32 testChainIndexer(t, 1) 33 } 34 } 35 36 //使用随机参数和不同数量的 37 //链后端。 38 func TestChainIndexerWithChildren(t *testing.T) { 39 for i := 2; i < 8; i++ { 40 testChainIndexer(t, i) 41 } 42 } 43 44 //TestChainIndexer使用单个链索引器或 45 //多个后端。节大小和所需的确认计数参数 46 //是随机的。 47 func testChainIndexer(t *testing.T, count int) { 48 db := ethdb.NewMemDatabase() 49 defer db.Close() 50 51 //创建索引器链并确保它们都报告为空 52 backends := make([]*testChainIndexBackend, count) 53 for i := 0; i < count; i++ { 54 var ( 55 sectionSize = uint64(rand.Intn(100) + 1) 56 confirmsReq = uint64(rand.Intn(10)) 57 ) 58 backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)} 59 backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i)) 60 61 if sections, _, _ := backends[i].indexer.Sections(); sections != 0 { 62 t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0) 63 } 64 if i > 0 { 65 backends[i-1].indexer.AddChildIndexer(backends[i].indexer) 66 } 67 } 68 defer backends[0].indexer.Close() //父索引器关闭子级 69 //通知ping根索引器有关新头或REORG的信息,然后 70 //如果节可处理,则处理块 71 notify := func(headNum, failNum uint64, reorg bool) { 72 backends[0].indexer.newHead(headNum, reorg) 73 if reorg { 74 for _, backend := range backends { 75 headNum = backend.reorg(headNum) 76 backend.assertSections() 77 } 78 return 79 } 80 var cascade bool 81 for _, backend := range backends { 82 headNum, cascade = backend.assertBlocks(headNum, failNum) 83 if !cascade { 84 break 85 } 86 backend.assertSections() 87 } 88 } 89 //inject将新的随机规范头直接插入数据库 90 inject := func(number uint64) { 91 header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()} 92 if number > 0 { 93 header.ParentHash = rawdb.ReadCanonicalHash(db, number-1) 94 } 95 rawdb.WriteHeader(db, header) 96 rawdb.WriteCanonicalHash(db, header.Hash(), number) 97 } 98 //使用已存在的链启动索引器 99 for i := uint64(0); i <= 100; i++ { 100 inject(i) 101 } 102 notify(100, 100, false) 103 104 //逐个添加新块 105 for i := uint64(101); i <= 1000; i++ { 106 inject(i) 107 notify(i, i, false) 108 } 109 //做一次练习 110 notify(500, 500, true) 111 112 //创建新的叉子 113 for i := uint64(501); i <= 1000; i++ { 114 inject(i) 115 notify(i, i, false) 116 } 117 for i := uint64(1001); i <= 1500; i++ { 118 inject(i) 119 } 120 //处理可用块少于通知块的方案失败 121 notify(2000, 1500, false) 122 123 //通知有关REORG的信息(如果在处理过程中发生,可能会导致丢失的块) 124 notify(1500, 1500, true) 125 126 //创建新的叉子 127 for i := uint64(1501); i <= 2000; i++ { 128 inject(i) 129 notify(i, i, false) 130 } 131 } 132 133 //testchainindexbackend实现chainindexerbackend 134 type testChainIndexBackend struct { 135 t *testing.T 136 indexer *ChainIndexer 137 section, headerCnt, stored uint64 138 processCh chan uint64 139 } 140 141 //断言节验证链索引器的节数是否正确。 142 func (b *testChainIndexBackend) assertSections() { 143 //如果不匹配,继续尝试3秒钟 144 var sections uint64 145 for i := 0; i < 300; i++ { 146 sections, _, _ = b.indexer.Sections() 147 if sections == b.stored { 148 return 149 } 150 time.Sleep(10 * time.Millisecond) 151 } 152 b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored) 153 } 154 155 //断言块需要在新块到达后进行处理调用。如果 156 //failnum<headnum,然后我们将模拟发生REORG的场景 157 //在处理开始并且一个部分的处理失败之后。 158 func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) { 159 var sections uint64 160 if headNum >= b.indexer.confirmsReq { 161 sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize 162 if sections > b.stored { 163 //预期已处理的块 164 for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ { 165 if expectd > failNum { 166 //在处理开始后回滚,不需要更多的处理调用 167 //等待更新完成,以确保处理实际失败 168 var updating bool 169 for i := 0; i < 300; i++ { 170 b.indexer.lock.Lock() 171 updating = b.indexer.knownSections > b.indexer.storedSections 172 b.indexer.lock.Unlock() 173 if !updating { 174 break 175 } 176 time.Sleep(10 * time.Millisecond) 177 } 178 if updating { 179 b.t.Fatalf("update did not finish") 180 } 181 sections = expectd / b.indexer.sectionSize 182 break 183 } 184 select { 185 case <-time.After(10 * time.Second): 186 b.t.Fatalf("Expected processed block #%d, got nothing", expectd) 187 case processed := <-b.processCh: 188 if processed != expectd { 189 b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed) 190 } 191 } 192 } 193 b.stored = sections 194 } 195 } 196 if b.stored == 0 { 197 return 0, false 198 } 199 return b.stored*b.indexer.sectionSize - 1, true 200 } 201 202 func (b *testChainIndexBackend) reorg(headNum uint64) uint64 { 203 firstChanged := headNum / b.indexer.sectionSize 204 if firstChanged < b.stored { 205 b.stored = firstChanged 206 } 207 return b.stored * b.indexer.sectionSize 208 } 209 210 func (b *testChainIndexBackend) Reset(ctx context.Context, section uint64, prevHead common.Hash) error { 211 b.section = section 212 b.headerCnt = 0 213 return nil 214 } 215 216 func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Header) error { 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 return nil 228 } 229 230 func (b *testChainIndexBackend) Commit() error { 231 if b.headerCnt != b.indexer.sectionSize { 232 b.t.Error("Not enough headers processed") 233 } 234 return nil 235 } 236