github.com/luckypickle/go-ethereum-vet@v1.14.2/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 "math/big" 25 "time" 26 27 "github.com/luckypickle/go-ethereum-vet/common" 28 "github.com/luckypickle/go-ethereum-vet/common/bitutil" 29 "github.com/luckypickle/go-ethereum-vet/core" 30 "github.com/luckypickle/go-ethereum-vet/core/rawdb" 31 "github.com/luckypickle/go-ethereum-vet/core/types" 32 "github.com/luckypickle/go-ethereum-vet/ethdb" 33 "github.com/luckypickle/go-ethereum-vet/log" 34 "github.com/luckypickle/go-ethereum-vet/params" 35 "github.com/luckypickle/go-ethereum-vet/rlp" 36 "github.com/luckypickle/go-ethereum-vet/trie" 37 ) 38 39 const ( 40 // CHTFrequencyClient is the block frequency for creating CHTs on the client side. 41 CHTFrequencyClient = 32768 42 43 // CHTFrequencyServer is the block frequency for creating CHTs on the server side. 44 // Eventually this can be merged back with the client version, but that requires a 45 // full database upgrade, so that should be left for a suitable moment. 46 CHTFrequencyServer = 4096 47 48 HelperTrieConfirmations = 2048 // number of confirmations before a server is expected to have the given HelperTrie available 49 HelperTrieProcessConfirmations = 256 // number of confirmations before a HelperTrie is generated 50 ) 51 52 // TrustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with 53 // the appropriate section index and head hash. It is used to start light syncing from this checkpoint 54 // and avoid downloading the entire header chain while still being able to securely access old headers/logs. 55 type TrustedCheckpoint struct { 56 name string 57 SectionIdx uint64 58 SectionHead, CHTRoot, BloomRoot common.Hash 59 } 60 61 // trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to 62 var trustedCheckpoints = map[common.Hash]TrustedCheckpoint{ 63 params.MainnetGenesisHash: { 64 name: "mainnet", 65 SectionIdx: 187, 66 SectionHead: common.HexToHash("e6baa034efa31562d71ff23676512dec6562c1ad0301e08843b907e81958c696"), 67 CHTRoot: common.HexToHash("28001955219719cf06de1b08648969139d123a9835fc760547a1e4dabdabc15a"), 68 BloomRoot: common.HexToHash("395ca2373fc662720ac6b58b3bbe71f68aa0f38b63b2d3553dd32ff3c51eebc4"), 69 }, 70 params.TestnetGenesisHash: { 71 name: "ropsten", 72 SectionIdx: 117, 73 SectionHead: common.HexToHash("9529b38631ae30783f56cbe4c3b9f07575b770ecba4f6e20a274b1e2f40fede1"), 74 CHTRoot: common.HexToHash("6f48e9f101f1fac98e7d74fbbcc4fda138358271ffd974d40d2506f0308bb363"), 75 BloomRoot: common.HexToHash("8242342e66e942c0cd893484e6736b9862ceb88b43ca344bb06a8285ac1b6d64"), 76 }, 77 params.RinkebyGenesisHash: { 78 name: "rinkeby", 79 SectionIdx: 85, 80 SectionHead: common.HexToHash("92cfa67afc4ad8ab0dcbc6fa49efd14b5b19402442e7317e6bc879d85f89d64d"), 81 CHTRoot: common.HexToHash("2802ec92cd7a54a75bca96afdc666ae7b99e5d96cf8192dcfb09588812f51564"), 82 BloomRoot: common.HexToHash("ebefeb31a9a42866d8cf2d2477704b4c3d7c20d0e4e9b5aaa77f396e016a1263"), 83 }, 84 } 85 86 var ( 87 ErrNoTrustedCht = errors.New("No trusted canonical hash trie") 88 ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie") 89 ErrNoHeader = errors.New("Header not found") 90 chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash 91 ChtTablePrefix = "cht-" 92 ) 93 94 // ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format 95 type ChtNode struct { 96 Hash common.Hash 97 Td *big.Int 98 } 99 100 // GetChtRoot reads the CHT root assoctiated to the given section from the database 101 // Note that sectionIdx is specified according to LES/1 CHT section size 102 func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 103 var encNumber [8]byte 104 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 105 data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...)) 106 return common.BytesToHash(data) 107 } 108 109 // GetChtV2Root reads the CHT root assoctiated to the given section from the database 110 // Note that sectionIdx is specified according to LES/2 CHT section size 111 func GetChtV2Root(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 112 return GetChtRoot(db, (sectionIdx+1)*(CHTFrequencyClient/CHTFrequencyServer)-1, sectionHead) 113 } 114 115 // StoreChtRoot writes the CHT root assoctiated to the given section into the database 116 // Note that sectionIdx is specified according to LES/1 CHT section size 117 func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { 118 var encNumber [8]byte 119 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 120 db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) 121 } 122 123 // ChtIndexerBackend implements core.ChainIndexerBackend 124 type ChtIndexerBackend struct { 125 diskdb, trieTable ethdb.Database 126 odr OdrBackend 127 triedb *trie.Database 128 section, sectionSize uint64 129 lastHash common.Hash 130 trie *trie.Trie 131 } 132 133 // NewBloomTrieIndexer creates a BloomTrie chain indexer 134 func NewChtIndexer(db ethdb.Database, clientMode bool, odr OdrBackend) *core.ChainIndexer { 135 var sectionSize, confirmReq uint64 136 if clientMode { 137 sectionSize = CHTFrequencyClient 138 confirmReq = HelperTrieConfirmations 139 } else { 140 sectionSize = CHTFrequencyServer 141 confirmReq = HelperTrieProcessConfirmations 142 } 143 idb := ethdb.NewTable(db, "chtIndex-") 144 trieTable := ethdb.NewTable(db, ChtTablePrefix) 145 backend := &ChtIndexerBackend{ 146 diskdb: db, 147 odr: odr, 148 trieTable: trieTable, 149 triedb: trie.NewDatabase(trieTable), 150 sectionSize: sectionSize, 151 } 152 return core.NewChainIndexer(db, idb, backend, sectionSize, confirmReq, time.Millisecond*100, "cht") 153 } 154 155 // fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the 156 // ODR backend in order to be able to add new entries and calculate subsequent root hashes 157 func (c *ChtIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error { 158 batch := c.trieTable.NewBatch() 159 r := &ChtRequest{ChtRoot: root, ChtNum: section - 1, BlockNum: section*c.sectionSize - 1} 160 for { 161 err := c.odr.Retrieve(ctx, r) 162 switch err { 163 case nil: 164 r.Proof.Store(batch) 165 return batch.Write() 166 case ErrNoPeers: 167 // if there are no peers to serve, retry later 168 select { 169 case <-ctx.Done(): 170 return ctx.Err() 171 case <-time.After(time.Second * 10): 172 // stay in the loop and try again 173 } 174 default: 175 return err 176 } 177 } 178 } 179 180 // Reset implements core.ChainIndexerBackend 181 func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { 182 var root common.Hash 183 if section > 0 { 184 root = GetChtRoot(c.diskdb, section-1, lastSectionHead) 185 } 186 var err error 187 c.trie, err = trie.New(root, c.triedb) 188 189 if err != nil && c.odr != nil { 190 err = c.fetchMissingNodes(ctx, section, root) 191 if err == nil { 192 c.trie, err = trie.New(root, c.triedb) 193 } 194 } 195 196 c.section = section 197 return err 198 } 199 200 // Process implements core.ChainIndexerBackend 201 func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error { 202 hash, num := header.Hash(), header.Number.Uint64() 203 c.lastHash = hash 204 205 td := rawdb.ReadTd(c.diskdb, hash, num) 206 if td == nil { 207 panic(nil) 208 } 209 var encNumber [8]byte 210 binary.BigEndian.PutUint64(encNumber[:], num) 211 data, _ := rlp.EncodeToBytes(ChtNode{hash, td}) 212 c.trie.Update(encNumber[:], data) 213 return nil 214 } 215 216 // Commit implements core.ChainIndexerBackend 217 func (c *ChtIndexerBackend) Commit() error { 218 root, err := c.trie.Commit(nil) 219 if err != nil { 220 return err 221 } 222 c.triedb.Commit(root, false) 223 224 if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 { 225 log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root)) 226 } 227 StoreChtRoot(c.diskdb, c.section, c.lastHash, root) 228 return nil 229 } 230 231 const ( 232 BloomTrieFrequency = 32768 233 ethBloomBitsSection = 4096 234 ) 235 236 var ( 237 bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash 238 BloomTrieTablePrefix = "blt-" 239 ) 240 241 // GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database 242 func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { 243 var encNumber [8]byte 244 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 245 data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...)) 246 return common.BytesToHash(data) 247 } 248 249 // StoreBloomTrieRoot writes the BloomTrie root assoctiated to the given section into the database 250 func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { 251 var encNumber [8]byte 252 binary.BigEndian.PutUint64(encNumber[:], sectionIdx) 253 db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) 254 } 255 256 // BloomTrieIndexerBackend implements core.ChainIndexerBackend 257 type BloomTrieIndexerBackend struct { 258 diskdb, trieTable ethdb.Database 259 odr OdrBackend 260 triedb *trie.Database 261 section, parentSectionSize, bloomTrieRatio uint64 262 trie *trie.Trie 263 sectionHeads []common.Hash 264 } 265 266 // NewBloomTrieIndexer creates a BloomTrie chain indexer 267 func NewBloomTrieIndexer(db ethdb.Database, clientMode bool, odr OdrBackend) *core.ChainIndexer { 268 trieTable := ethdb.NewTable(db, BloomTrieTablePrefix) 269 backend := &BloomTrieIndexerBackend{ 270 diskdb: db, 271 odr: odr, 272 trieTable: trieTable, 273 triedb: trie.NewDatabase(trieTable), 274 } 275 idb := ethdb.NewTable(db, "bltIndex-") 276 277 if clientMode { 278 backend.parentSectionSize = BloomTrieFrequency 279 } else { 280 backend.parentSectionSize = ethBloomBitsSection 281 } 282 backend.bloomTrieRatio = BloomTrieFrequency / backend.parentSectionSize 283 backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio) 284 return core.NewChainIndexer(db, idb, backend, BloomTrieFrequency, 0, time.Millisecond*100, "bloomtrie") 285 } 286 287 // fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the 288 // ODR backend in order to be able to add new entries and calculate subsequent root hashes 289 func (b *BloomTrieIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error { 290 indexCh := make(chan uint, types.BloomBitLength) 291 type res struct { 292 nodes *NodeSet 293 err error 294 } 295 resCh := make(chan res, types.BloomBitLength) 296 for i := 0; i < 20; i++ { 297 go func() { 298 for bitIndex := range indexCh { 299 r := &BloomRequest{BloomTrieRoot: root, BloomTrieNum: section - 1, BitIdx: bitIndex, SectionIdxList: []uint64{section - 1}} 300 for { 301 if err := b.odr.Retrieve(ctx, r); err == ErrNoPeers { 302 // if there are no peers to serve, retry later 303 select { 304 case <-ctx.Done(): 305 resCh <- res{nil, ctx.Err()} 306 return 307 case <-time.After(time.Second * 10): 308 // stay in the loop and try again 309 } 310 } else { 311 resCh <- res{r.Proofs, err} 312 break 313 } 314 } 315 } 316 }() 317 } 318 319 for i := uint(0); i < types.BloomBitLength; i++ { 320 indexCh <- i 321 } 322 close(indexCh) 323 batch := b.trieTable.NewBatch() 324 for i := uint(0); i < types.BloomBitLength; i++ { 325 res := <-resCh 326 if res.err != nil { 327 return res.err 328 } 329 res.nodes.Store(batch) 330 } 331 return batch.Write() 332 } 333 334 // Reset implements core.ChainIndexerBackend 335 func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error { 336 var root common.Hash 337 if section > 0 { 338 root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead) 339 } 340 var err error 341 b.trie, err = trie.New(root, b.triedb) 342 if err != nil && b.odr != nil { 343 err = b.fetchMissingNodes(ctx, section, root) 344 if err == nil { 345 b.trie, err = trie.New(root, b.triedb) 346 } 347 } 348 b.section = section 349 return err 350 } 351 352 // Process implements core.ChainIndexerBackend 353 func (b *BloomTrieIndexerBackend) Process(ctx context.Context, header *types.Header) error { 354 num := header.Number.Uint64() - b.section*BloomTrieFrequency 355 if (num+1)%b.parentSectionSize == 0 { 356 b.sectionHeads[num/b.parentSectionSize] = header.Hash() 357 } 358 return nil 359 } 360 361 // Commit implements core.ChainIndexerBackend 362 func (b *BloomTrieIndexerBackend) Commit() error { 363 var compSize, decompSize uint64 364 365 for i := uint(0); i < types.BloomBitLength; i++ { 366 var encKey [10]byte 367 binary.BigEndian.PutUint16(encKey[0:2], uint16(i)) 368 binary.BigEndian.PutUint64(encKey[2:10], b.section) 369 var decomp []byte 370 for j := uint64(0); j < b.bloomTrieRatio; j++ { 371 data, err := rawdb.ReadBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j]) 372 if err != nil { 373 return err 374 } 375 decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSectionSize/8)) 376 if err2 != nil { 377 return err2 378 } 379 decomp = append(decomp, decompData...) 380 } 381 comp := bitutil.CompressBytes(decomp) 382 383 decompSize += uint64(len(decomp)) 384 compSize += uint64(len(comp)) 385 if len(comp) > 0 { 386 b.trie.Update(encKey[:], comp) 387 } else { 388 b.trie.Delete(encKey[:]) 389 } 390 } 391 root, err := b.trie.Commit(nil) 392 if err != nil { 393 return err 394 } 395 b.triedb.Commit(root, false) 396 397 sectionHead := b.sectionHeads[b.bloomTrieRatio-1] 398 log.Info("Storing bloom trie", "section", b.section, "head", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression", float64(compSize)/float64(decompSize)) 399 StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root) 400 401 return nil 402 }