github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/light/postprocess.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:39</date> 10 //</624450096674639872> 11 12 13 package light 14 15 import ( 16 "context" 17 "encoding/binary" 18 "errors" 19 "fmt" 20 "math/big" 21 "time" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/common/bitutil" 25 "github.com/ethereum/go-ethereum/core" 26 "github.com/ethereum/go-ethereum/core/rawdb" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/params" 31 "github.com/ethereum/go-ethereum/rlp" 32 "github.com/ethereum/go-ethereum/trie" 33 ) 34 35 //indexerconfig包括一组用于链索引器的配置。 36 type IndexerConfig struct { 37 //用于创建CHT的块频率。 38 ChtSize uint64 39 40 //特殊的辅助字段表示服务器配置的客户端chtsize,否则表示服务器的chtsize。 41 PairChtSize uint64 42 43 //生成/接受规范哈希帮助trie所需的确认数。 44 ChtConfirms uint64 45 46 //用于创建新Bloom位的块频率。 47 BloomSize uint64 48 49 //在认为一个大方坯段可能是最终的及其旋转的钻头之前所需的确认数量。 50 //计算。 51 BloomConfirms uint64 52 53 //创建Bloomtrie的块频率。 54 BloomTrieSize uint64 55 56 //生成/接受Bloom Trie所需的确认数。 57 BloomTrieConfirms uint64 58 } 59 60 var ( 61 //DefaultServerIndexerConfig将一组配置包装为服务器端的默认索引器配置。 62 DefaultServerIndexerConfig = &IndexerConfig{ 63 ChtSize: params.CHTFrequencyServer, 64 PairChtSize: params.CHTFrequencyClient, 65 ChtConfirms: params.HelperTrieProcessConfirmations, 66 BloomSize: params.BloomBitsBlocks, 67 BloomConfirms: params.BloomConfirms, 68 BloomTrieSize: params.BloomTrieFrequency, 69 BloomTrieConfirms: params.HelperTrieProcessConfirmations, 70 } 71 //default client indexer config将一组配置包装为客户端的默认索引器配置。 72 DefaultClientIndexerConfig = &IndexerConfig{ 73 ChtSize: params.CHTFrequencyClient, 74 PairChtSize: params.CHTFrequencyServer, 75 ChtConfirms: params.HelperTrieConfirmations, 76 BloomSize: params.BloomBitsBlocksClient, 77 BloomConfirms: params.HelperTrieConfirmations, 78 BloomTrieSize: params.BloomTrieFrequency, 79 BloomTrieConfirms: params.HelperTrieConfirmations, 80 } 81 //test server indexer config将一组配置包装为服务器端的测试索引器配置。 82 TestServerIndexerConfig = &IndexerConfig{ 83 ChtSize: 64, 84 PairChtSize: 512, 85 ChtConfirms: 4, 86 BloomSize: 64, 87 BloomConfirms: 4, 88 BloomTrieSize: 512, 89 BloomTrieConfirms: 4, 90 } 91 //test client indexer config将一组配置包装为客户端的测试索引器配置。 92 TestClientIndexerConfig = &IndexerConfig{ 93 ChtSize: 512, 94 PairChtSize: 64, 95 ChtConfirms: 32, 96 BloomSize: 512, 97 BloomConfirms: 32, 98 BloomTrieSize: 512, 99 BloomTrieConfirms: 32, 100 } 101 ) 102 103 //TrustedCheckpoints将每个已知的检查点与它所属的链的Genesis哈希相关联 104 var trustedCheckpoints = map[common.Hash]*params.TrustedCheckpoint{ 105 params.MainnetGenesisHash: params.MainnetTrustedCheckpoint, 106 params.TestnetGenesisHash: params.TestnetTrustedCheckpoint, 107 params.RinkebyGenesisHash: params.RinkebyTrustedCheckpoint, 108 } 109 110 var ( 111 ErrNoTrustedCht = errors.New("no trusted canonical hash trie") 112 ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie") 113 ErrNoHeader = errors.New("header not found") 114 chtPrefix = []byte("chtRoot-") //chtprefix+chtnum(uint64 big endian)->trie根哈希 115 ChtTablePrefix = "cht-" 116 ) 117 118 //chtnode结构以rlp编码格式存储在规范哈希trie中。 119 type ChtNode struct { 120 Hash common.Hash 121 Td *big.Int 122 } 123 124 //getchtroot从数据库中读取与给定节关联的cht根 125 //请注意,SECTIONIDX是根据LES/1 CHT截面尺寸指定的。 126 func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 127 var encNumber [8]byte 128 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 129 data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...)) 130 return common.BytesToHash(data) 131 } 132 133 //storechtroot将与给定节关联的cht根写入数据库 134 //请注意,SECTIONIDX是根据LES/1 CHT截面尺寸指定的。 135 func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { 136 var encNumber [8]byte 137 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 138 db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) 139 } 140 141 //chindexerbackend实现core.chainindexerbackend。 142 type ChtIndexerBackend struct { 143 diskdb, trieTable ethdb.Database 144 odr OdrBackend 145 triedb *trie.Database 146 section, sectionSize uint64 147 lastHash common.Hash 148 trie *trie.Trie 149 } 150 151 //newchtIndexer创建cht链索引器 152 func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64) *core.ChainIndexer { 153 trieTable := ethdb.NewTable(db, ChtTablePrefix) 154 backend := &ChtIndexerBackend{ 155 diskdb: db, 156 odr: odr, 157 trieTable: trieTable, 158 triedb: trie.NewDatabaseWithCache(trieTable, 1), //使用一个很小的缓存只会降低内存 159 sectionSize: size, 160 } 161 return core.NewChainIndexer(db, ethdb.NewTable(db, "chtIndex-"), backend, size, confirms, time.Millisecond*100, "cht") 162 } 163 164 //fetchMissingNodes尝试从 165 //ODR后端,以便能够添加新条目和计算后续根散列 166 func (c *ChtIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error { 167 batch := c.trieTable.NewBatch() 168 r := &ChtRequest{ChtRoot: root, ChtNum: section - 1, BlockNum: section*c.sectionSize - 1, Config: c.odr.IndexerConfig()} 169 for { 170 err := c.odr.Retrieve(ctx, r) 171 switch err { 172 case nil: 173 r.Proof.Store(batch) 174 return batch.Write() 175 case ErrNoPeers: 176 //如果没有要服务的对等端,请稍后重试 177 select { 178 case <-ctx.Done(): 179 return ctx.Err() 180 case <-time.After(time.Second * 10): 181 //保持在循环中再试一次 182 } 183 default: 184 return err 185 } 186 } 187 } 188 189 //重置实现core.chainindexerbackend 190 func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { 191 var root common.Hash 192 if section > 0 { 193 root = GetChtRoot(c.diskdb, section-1, lastSectionHead) 194 } 195 var err error 196 c.trie, err = trie.New(root, c.triedb) 197 198 if err != nil && c.odr != nil { 199 err = c.fetchMissingNodes(ctx, section, root) 200 if err == nil { 201 c.trie, err = trie.New(root, c.triedb) 202 } 203 } 204 205 c.section = section 206 return err 207 } 208 209 //进程实现core.chainindexerbackend 210 func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error { 211 hash, num := header.Hash(), header.Number.Uint64() 212 c.lastHash = hash 213 214 td := rawdb.ReadTd(c.diskdb, hash, num) 215 if td == nil { 216 panic(nil) 217 } 218 var encNumber [8]byte 219 binary.BigEndian.PutUint64(encNumber[:], num) 220 data, _ := rlp.EncodeToBytes(ChtNode{hash, td}) 221 c.trie.Update(encNumber[:], data) 222 return nil 223 } 224 225 //commit实现core.chainindexerbackend 226 func (c *ChtIndexerBackend) Commit() error { 227 root, err := c.trie.Commit(nil) 228 if err != nil { 229 return err 230 } 231 c.triedb.Commit(root, false) 232 233 if ((c.section+1)*c.sectionSize)%params.CHTFrequencyClient == 0 { 234 log.Info("Storing CHT", "section", c.section*c.sectionSize/params.CHTFrequencyClient, "head", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root)) 235 } 236 StoreChtRoot(c.diskdb, c.section, c.lastHash, root) 237 return nil 238 } 239 240 var ( 241 bloomTriePrefix = []byte("bltRoot-") //bloomtrieffix+bloomtrienum(uint64 big endian)->trie根哈希 242 BloomTrieTablePrefix = "blt-" 243 ) 244 245 //GetBloomTrieRoot从数据库中读取与给定节关联的BloomTrie根。 246 func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 247 var encNumber [8]byte 248 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 249 data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...)) 250 return common.BytesToHash(data) 251 } 252 253 //StoreBloomTrieRoot将与给定节关联的BloomTrie根写入数据库 254 func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { 255 var encNumber [8]byte 256 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 257 db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) 258 } 259 260 //BloomTrieIndexerBackend实现core.chainIndexerBackend 261 type BloomTrieIndexerBackend struct { 262 diskdb, trieTable ethdb.Database 263 triedb *trie.Database 264 odr OdrBackend 265 section uint64 266 parentSize uint64 267 size uint64 268 bloomTrieRatio uint64 269 trie *trie.Trie 270 sectionHeads []common.Hash 271 } 272 273 //Newbloomtrieeindexer创建了一个bloomtrie链索引器 274 func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64) *core.ChainIndexer { 275 trieTable := ethdb.NewTable(db, BloomTrieTablePrefix) 276 backend := &BloomTrieIndexerBackend{ 277 diskdb: db, 278 odr: odr, 279 trieTable: trieTable, 280 triedb: trie.NewDatabaseWithCache(trieTable, 1), //使用一个很小的缓存只会降低内存 281 parentSize: parentSize, 282 size: size, 283 } 284 backend.bloomTrieRatio = size / parentSize 285 backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio) 286 return core.NewChainIndexer(db, ethdb.NewTable(db, "bltIndex-"), backend, size, 0, time.Millisecond*100, "bloomtrie") 287 } 288 289 //fetchMissingNodes尝试从 290 //ODR后端,以便能够添加新条目和计算后续根散列 291 func (b *BloomTrieIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error { 292 indexCh := make(chan uint, types.BloomBitLength) 293 type res struct { 294 nodes *NodeSet 295 err error 296 } 297 resCh := make(chan res, types.BloomBitLength) 298 for i := 0; i < 20; i++ { 299 go func() { 300 for bitIndex := range indexCh { 301 r := &BloomRequest{BloomTrieRoot: root, BloomTrieNum: section - 1, BitIdx: bitIndex, SectionIndexList: []uint64{section - 1}, Config: b.odr.IndexerConfig()} 302 for { 303 if err := b.odr.Retrieve(ctx, r); err == ErrNoPeers { 304 //如果没有要服务的对等端,请稍后重试 305 select { 306 case <-ctx.Done(): 307 resCh <- res{nil, ctx.Err()} 308 return 309 case <-time.After(time.Second * 10): 310 //保持在循环中再试一次 311 } 312 } else { 313 resCh <- res{r.Proofs, err} 314 break 315 } 316 } 317 } 318 }() 319 } 320 321 for i := uint(0); i < types.BloomBitLength; i++ { 322 indexCh <- i 323 } 324 close(indexCh) 325 batch := b.trieTable.NewBatch() 326 for i := uint(0); i < types.BloomBitLength; i++ { 327 res := <-resCh 328 if res.err != nil { 329 return res.err 330 } 331 res.nodes.Store(batch) 332 } 333 return batch.Write() 334 } 335 336 //重置实现core.chainindexerbackend 337 func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { 338 var root common.Hash 339 if section > 0 { 340 root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead) 341 } 342 var err error 343 b.trie, err = trie.New(root, b.triedb) 344 if err != nil && b.odr != nil { 345 err = b.fetchMissingNodes(ctx, section, root) 346 if err == nil { 347 b.trie, err = trie.New(root, b.triedb) 348 } 349 } 350 b.section = section 351 return err 352 } 353 354 //进程实现core.chainindexerbackend 355 func (b *BloomTrieIndexerBackend) Process(ctx context.Context, header *types.Header) error { 356 num := header.Number.Uint64() - b.section*b.size 357 if (num+1)%b.parentSize == 0 { 358 b.sectionHeads[num/b.parentSize] = header.Hash() 359 } 360 return nil 361 } 362 363 //commit实现core.chainindexerbackend 364 func (b *BloomTrieIndexerBackend) Commit() error { 365 var compSize, decompSize uint64 366 367 for i := uint(0); i < types.BloomBitLength; i++ { 368 var encKey [10]byte 369 binary.BigEndian.PutUint16(encKey[0:2], uint16(i)) 370 binary.BigEndian.PutUint64(encKey[2:10], b.section) 371 var decomp []byte 372 for j := uint64(0); j < b.bloomTrieRatio; j++ { 373 data, err := rawdb.ReadBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j]) 374 if err != nil { 375 return err 376 } 377 decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSize/8)) 378 if err2 != nil { 379 return err2 380 } 381 decomp = append(decomp, decompData...) 382 } 383 comp := bitutil.CompressBytes(decomp) 384 385 decompSize += uint64(len(decomp)) 386 compSize += uint64(len(comp)) 387 if len(comp) > 0 { 388 b.trie.Update(encKey[:], comp) 389 } else { 390 b.trie.Delete(encKey[:]) 391 } 392 } 393 root, err := b.trie.Commit(nil) 394 if err != nil { 395 return err 396 } 397 b.triedb.Commit(root, false) 398 399 sectionHead := b.sectionHeads[b.bloomTrieRatio-1] 400 log.Info("Storing bloom trie", "section", b.section, "head", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression", float64(compSize)/float64(decompSize)) 401 StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root) 402 return nil 403 } 404