github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/light/postprocess.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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 light 18 19 import ( 20 "context" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "time" 25 26 "github.com/PlatONnetwork/PlatON-Go/common" 27 "github.com/PlatONnetwork/PlatON-Go/common/bitutil" 28 "github.com/PlatONnetwork/PlatON-Go/core" 29 "github.com/PlatONnetwork/PlatON-Go/core/rawdb" 30 "github.com/PlatONnetwork/PlatON-Go/core/types" 31 "github.com/PlatONnetwork/PlatON-Go/ethdb" 32 "github.com/PlatONnetwork/PlatON-Go/log" 33 "github.com/PlatONnetwork/PlatON-Go/params" 34 "github.com/PlatONnetwork/PlatON-Go/rlp" 35 "github.com/PlatONnetwork/PlatON-Go/trie" 36 ) 37 38 // IndexerConfig includes a set of configs for chain indexers. 39 type IndexerConfig struct { 40 // The block frequency for creating CHTs. 41 ChtSize uint64 42 43 // A special auxiliary field represents client's chtsize for server config, otherwise represents server's chtsize. 44 PairChtSize uint64 45 46 // The number of confirmations needed to generate/accept a canonical hash help trie. 47 ChtConfirms uint64 48 49 // The block frequency for creating new bloom bits. 50 BloomSize uint64 51 52 // The number of confirmation needed before a bloom section is considered probably final and its rotated bits 53 // are calculated. 54 BloomConfirms uint64 55 56 // The block frequency for creating BloomTrie. 57 BloomTrieSize uint64 58 59 // The number of confirmations needed to generate/accept a bloom trie. 60 BloomTrieConfirms uint64 61 } 62 63 var ( 64 // DefaultServerIndexerConfig wraps a set of configs as a default indexer config for server side. 65 DefaultServerIndexerConfig = &IndexerConfig{ 66 ChtSize: params.CHTFrequencyServer, 67 PairChtSize: params.CHTFrequencyClient, 68 ChtConfirms: params.HelperTrieProcessConfirmations, 69 BloomSize: params.BloomBitsBlocks, 70 BloomConfirms: params.BloomConfirms, 71 BloomTrieSize: params.BloomTrieFrequency, 72 BloomTrieConfirms: params.HelperTrieProcessConfirmations, 73 } 74 // DefaultClientIndexerConfig wraps a set of configs as a default indexer config for client side. 75 DefaultClientIndexerConfig = &IndexerConfig{ 76 ChtSize: params.CHTFrequencyClient, 77 PairChtSize: params.CHTFrequencyServer, 78 ChtConfirms: params.HelperTrieConfirmations, 79 BloomSize: params.BloomBitsBlocksClient, 80 BloomConfirms: params.HelperTrieConfirmations, 81 BloomTrieSize: params.BloomTrieFrequency, 82 BloomTrieConfirms: params.HelperTrieConfirmations, 83 } 84 // TestServerIndexerConfig wraps a set of configs as a test indexer config for server side. 85 TestServerIndexerConfig = &IndexerConfig{ 86 ChtSize: 256, 87 PairChtSize: 2048, 88 ChtConfirms: 16, 89 BloomSize: 256, 90 BloomConfirms: 16, 91 BloomTrieSize: 2048, 92 BloomTrieConfirms: 16, 93 } 94 // TestClientIndexerConfig wraps a set of configs as a test indexer config for client side. 95 TestClientIndexerConfig = &IndexerConfig{ 96 ChtSize: 2048, 97 PairChtSize: 256, 98 ChtConfirms: 128, 99 BloomSize: 2048, 100 BloomConfirms: 128, 101 BloomTrieSize: 2048, 102 BloomTrieConfirms: 128, 103 } 104 ) 105 106 // trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to 107 var trustedCheckpoints = map[common.Hash]*params.TrustedCheckpoint{ 108 params.MainnetGenesisHash: params.MainnetTrustedCheckpoint, 109 params.TestnetGenesisHash: params.TestnetTrustedCheckpoint, 110 params.BeatnetGenesisHash: params.BetanetTrustedCheckpoint, 111 params.InnerTestnetGenesisHash: params.InnerTestnetTrustedCheckpoint, 112 params.InnerDevnetGenesisHash: params.InnerDevnetTrustedCheckpoint, 113 } 114 115 var ( 116 ErrNoTrustedCht = errors.New("no trusted canonical hash trie") 117 ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie") 118 ErrNoHeader = errors.New("header not found") 119 chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash 120 ChtTablePrefix = "cht-" 121 ) 122 123 // ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format 124 type ChtNode struct { 125 Hash common.Hash 126 } 127 128 // GetChtRoot reads the CHT root associated to the given section from the database 129 // Note that sectionIdx is specified according to LES/1 CHT section size. 130 func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 131 var encNumber [8]byte 132 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 133 data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...)) 134 return common.BytesToHash(data) 135 } 136 137 // StoreChtRoot writes the CHT root associated to the given section into the database 138 // Note that sectionIdx is specified according to LES/1 CHT section size. 139 func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { 140 var encNumber [8]byte 141 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 142 db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) 143 } 144 145 // ChtIndexerBackend implements core.ChainIndexerBackend. 146 type ChtIndexerBackend struct { 147 diskdb, trieTable ethdb.Database 148 odr OdrBackend 149 triedb *trie.Database 150 section, sectionSize uint64 151 lastHash common.Hash 152 trie *trie.Trie 153 } 154 155 // NewChtIndexer creates a Cht chain indexer 156 func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64) *core.ChainIndexer { 157 trieTable := ethdb.NewTable(db, ChtTablePrefix) 158 backend := &ChtIndexerBackend{ 159 diskdb: db, 160 odr: odr, 161 trieTable: trieTable, 162 triedb: trie.NewDatabase(trieTable), 163 sectionSize: size, 164 } 165 return core.NewChainIndexer(db, ethdb.NewTable(db, "chtIndex-"), backend, size, confirms, time.Millisecond*100, "cht") 166 } 167 168 // fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the 169 // ODR backend in order to be able to add new entries and calculate subsequent root hashes 170 func (c *ChtIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error { 171 batch := c.trieTable.NewBatch() 172 r := &ChtRequest{ChtRoot: root, ChtNum: section - 1, BlockNum: section*c.sectionSize - 1, Config: c.odr.IndexerConfig()} 173 for { 174 err := c.odr.Retrieve(ctx, r) 175 switch err { 176 case nil: 177 r.Proof.Store(batch) 178 return batch.Write() 179 case ErrNoPeers: 180 // if there are no peers to serve, retry later 181 select { 182 case <-ctx.Done(): 183 return ctx.Err() 184 case <-time.After(time.Second * 10): 185 // stay in the loop and try again 186 } 187 default: 188 return err 189 } 190 } 191 } 192 193 // Reset implements core.ChainIndexerBackend 194 func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { 195 var root common.Hash 196 if section > 0 { 197 root = GetChtRoot(c.diskdb, section-1, lastSectionHead) 198 } 199 var err error 200 c.trie, err = trie.New(root, c.triedb) 201 202 if err != nil && c.odr != nil { 203 err = c.fetchMissingNodes(ctx, section, root) 204 if err == nil { 205 c.trie, err = trie.New(root, c.triedb) 206 } 207 } 208 209 c.section = section 210 return err 211 } 212 213 // Process implements core.ChainIndexerBackend 214 func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error { 215 hash, num := header.Hash(), header.Number.Uint64() 216 c.lastHash = hash 217 218 var encNumber [8]byte 219 binary.BigEndian.PutUint64(encNumber[:], num) 220 data, _ := rlp.EncodeToBytes(ChtNode{hash}) 221 c.trie.Update(encNumber[:], data) 222 return nil 223 } 224 225 // Commit implements 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-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash 242 BloomTrieTablePrefix = "blt-" 243 ) 244 245 // GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database 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 writes the BloomTrie root assoctiated to the given section into the database 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 implements 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 // NewBloomTrieIndexer creates a BloomTrie chain indexer 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.NewDatabase(trieTable), 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 tries to retrieve the last entries of the latest trusted bloom trie from the 290 // ODR backend in order to be able to add new entries and calculate subsequent root hashes 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 // if there are no peers to serve, retry later 305 select { 306 case <-ctx.Done(): 307 resCh <- res{nil, ctx.Err()} 308 return 309 case <-time.After(time.Second * 10): 310 // stay in the loop and try again 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 // Reset implements 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 // Process implements 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 implements 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 }