postprocess.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. package light
  17. import (
  18. "encoding/binary"
  19. "errors"
  20. "math/big"
  21. "time"
  22. "github.com/ethereum/go-ethereum/common"
  23. "github.com/ethereum/go-ethereum/common/bitutil"
  24. "github.com/ethereum/go-ethereum/core"
  25. "github.com/ethereum/go-ethereum/core/types"
  26. "github.com/ethereum/go-ethereum/ethdb"
  27. "github.com/ethereum/go-ethereum/log"
  28. "github.com/ethereum/go-ethereum/params"
  29. "github.com/ethereum/go-ethereum/rlp"
  30. "github.com/ethereum/go-ethereum/trie"
  31. )
  32. const (
  33. // CHTFrequencyClient is the block frequency for creating CHTs on the client side.
  34. CHTFrequencyClient = 32768
  35. // CHTFrequencyServer is the block frequency for creating CHTs on the server side.
  36. // Eventually this can be merged back with the client version, but that requires a
  37. // full database upgrade, so that should be left for a suitable moment.
  38. CHTFrequencyServer = 4096
  39. HelperTrieConfirmations = 2048 // number of confirmations before a server is expected to have the given HelperTrie available
  40. HelperTrieProcessConfirmations = 256 // number of confirmations before a HelperTrie is generated
  41. )
  42. // trustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with
  43. // the appropriate section index and head hash. It is used to start light syncing from this checkpoint
  44. // and avoid downloading the entire header chain while still being able to securely access old headers/logs.
  45. type trustedCheckpoint struct {
  46. name string
  47. sectionIdx uint64
  48. sectionHead, chtRoot, bloomTrieRoot common.Hash
  49. }
  50. var (
  51. mainnetCheckpoint = trustedCheckpoint{
  52. name: "mainnet",
  53. sectionIdx: 153,
  54. sectionHead: common.HexToHash("04c2114a8cbe49ba5c37a03cc4b4b8d3adfc0bd2c78e0e726405dd84afca1d63"),
  55. chtRoot: common.HexToHash("d7ec603e5d30b567a6e894ee7704e4603232f206d3e5a589794cec0c57bf318e"),
  56. bloomTrieRoot: common.HexToHash("0b139b8fb692e21f663ff200da287192201c28ef5813c1ac6ba02a0a4799eef9"),
  57. }
  58. ropstenCheckpoint = trustedCheckpoint{
  59. name: "ropsten",
  60. sectionIdx: 79,
  61. sectionHead: common.HexToHash("1b1ba890510e06411fdee9bb64ca7705c56a1a4ce3559ddb34b3680c526cb419"),
  62. chtRoot: common.HexToHash("71d60207af74e5a22a3e1cfbfc89f9944f91b49aa980c86fba94d568369eaf44"),
  63. bloomTrieRoot: common.HexToHash("70aca4b3b6d08dde8704c95cedb1420394453c1aec390947751e69ff8c436360"),
  64. }
  65. )
  66. // trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to
  67. var trustedCheckpoints = map[common.Hash]trustedCheckpoint{
  68. params.MainnetGenesisHash: mainnetCheckpoint,
  69. params.TestnetGenesisHash: ropstenCheckpoint,
  70. }
  71. var (
  72. ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
  73. ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie")
  74. ErrNoHeader = errors.New("Header not found")
  75. chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
  76. ChtTablePrefix = "cht-"
  77. )
  78. // ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format
  79. type ChtNode struct {
  80. Hash common.Hash
  81. Td *big.Int
  82. }
  83. // GetChtRoot reads the CHT root assoctiated to the given section from the database
  84. // Note that sectionIdx is specified according to LES/1 CHT section size
  85. func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
  86. var encNumber [8]byte
  87. binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
  88. data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...))
  89. return common.BytesToHash(data)
  90. }
  91. // GetChtV2Root reads the CHT root assoctiated to the given section from the database
  92. // Note that sectionIdx is specified according to LES/2 CHT section size
  93. func GetChtV2Root(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
  94. return GetChtRoot(db, (sectionIdx+1)*(CHTFrequencyClient/CHTFrequencyServer)-1, sectionHead)
  95. }
  96. // StoreChtRoot writes the CHT root assoctiated to the given section into the database
  97. // Note that sectionIdx is specified according to LES/1 CHT section size
  98. func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
  99. var encNumber [8]byte
  100. binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
  101. db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
  102. }
  103. // ChtIndexerBackend implements core.ChainIndexerBackend
  104. type ChtIndexerBackend struct {
  105. diskdb ethdb.Database
  106. triedb *trie.Database
  107. section, sectionSize uint64
  108. lastHash common.Hash
  109. trie *trie.Trie
  110. }
  111. // NewBloomTrieIndexer creates a BloomTrie chain indexer
  112. func NewChtIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer {
  113. var sectionSize, confirmReq uint64
  114. if clientMode {
  115. sectionSize = CHTFrequencyClient
  116. confirmReq = HelperTrieConfirmations
  117. } else {
  118. sectionSize = CHTFrequencyServer
  119. confirmReq = HelperTrieProcessConfirmations
  120. }
  121. idb := ethdb.NewTable(db, "chtIndex-")
  122. backend := &ChtIndexerBackend{
  123. diskdb: db,
  124. triedb: trie.NewDatabase(ethdb.NewTable(db, ChtTablePrefix)),
  125. sectionSize: sectionSize,
  126. }
  127. return core.NewChainIndexer(db, idb, backend, sectionSize, confirmReq, time.Millisecond*100, "cht")
  128. }
  129. // Reset implements core.ChainIndexerBackend
  130. func (c *ChtIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) error {
  131. var root common.Hash
  132. if section > 0 {
  133. root = GetChtRoot(c.diskdb, section-1, lastSectionHead)
  134. }
  135. var err error
  136. c.trie, err = trie.New(root, c.triedb)
  137. c.section = section
  138. return err
  139. }
  140. // Process implements core.ChainIndexerBackend
  141. func (c *ChtIndexerBackend) Process(header *types.Header) {
  142. hash, num := header.Hash(), header.Number.Uint64()
  143. c.lastHash = hash
  144. td := core.GetTd(c.diskdb, hash, num)
  145. if td == nil {
  146. panic(nil)
  147. }
  148. var encNumber [8]byte
  149. binary.BigEndian.PutUint64(encNumber[:], num)
  150. data, _ := rlp.EncodeToBytes(ChtNode{hash, td})
  151. c.trie.Update(encNumber[:], data)
  152. }
  153. // Commit implements core.ChainIndexerBackend
  154. func (c *ChtIndexerBackend) Commit() error {
  155. root, err := c.trie.Commit(nil)
  156. if err != nil {
  157. return err
  158. }
  159. c.triedb.Commit(root, false)
  160. if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 {
  161. log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", c.lastHash, "root", root)
  162. }
  163. StoreChtRoot(c.diskdb, c.section, c.lastHash, root)
  164. return nil
  165. }
  166. const (
  167. BloomTrieFrequency = 32768
  168. ethBloomBitsSection = 4096
  169. ethBloomBitsConfirmations = 256
  170. )
  171. var (
  172. bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash
  173. BloomTrieTablePrefix = "blt-"
  174. )
  175. // GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database
  176. func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
  177. var encNumber [8]byte
  178. binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
  179. data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...))
  180. return common.BytesToHash(data)
  181. }
  182. // StoreBloomTrieRoot writes the BloomTrie root assoctiated to the given section into the database
  183. func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
  184. var encNumber [8]byte
  185. binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
  186. db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
  187. }
  188. // BloomTrieIndexerBackend implements core.ChainIndexerBackend
  189. type BloomTrieIndexerBackend struct {
  190. diskdb ethdb.Database
  191. triedb *trie.Database
  192. section, parentSectionSize, bloomTrieRatio uint64
  193. trie *trie.Trie
  194. sectionHeads []common.Hash
  195. }
  196. // NewBloomTrieIndexer creates a BloomTrie chain indexer
  197. func NewBloomTrieIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer {
  198. backend := &BloomTrieIndexerBackend{
  199. diskdb: db,
  200. triedb: trie.NewDatabase(ethdb.NewTable(db, BloomTrieTablePrefix)),
  201. }
  202. idb := ethdb.NewTable(db, "bltIndex-")
  203. var confirmReq uint64
  204. if clientMode {
  205. backend.parentSectionSize = BloomTrieFrequency
  206. confirmReq = HelperTrieConfirmations
  207. } else {
  208. backend.parentSectionSize = ethBloomBitsSection
  209. confirmReq = HelperTrieProcessConfirmations
  210. }
  211. backend.bloomTrieRatio = BloomTrieFrequency / backend.parentSectionSize
  212. backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio)
  213. return core.NewChainIndexer(db, idb, backend, BloomTrieFrequency, confirmReq-ethBloomBitsConfirmations, time.Millisecond*100, "bloomtrie")
  214. }
  215. // Reset implements core.ChainIndexerBackend
  216. func (b *BloomTrieIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) error {
  217. var root common.Hash
  218. if section > 0 {
  219. root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead)
  220. }
  221. var err error
  222. b.trie, err = trie.New(root, b.triedb)
  223. b.section = section
  224. return err
  225. }
  226. // Process implements core.ChainIndexerBackend
  227. func (b *BloomTrieIndexerBackend) Process(header *types.Header) {
  228. num := header.Number.Uint64() - b.section*BloomTrieFrequency
  229. if (num+1)%b.parentSectionSize == 0 {
  230. b.sectionHeads[num/b.parentSectionSize] = header.Hash()
  231. }
  232. }
  233. // Commit implements core.ChainIndexerBackend
  234. func (b *BloomTrieIndexerBackend) Commit() error {
  235. var compSize, decompSize uint64
  236. for i := uint(0); i < types.BloomBitLength; i++ {
  237. var encKey [10]byte
  238. binary.BigEndian.PutUint16(encKey[0:2], uint16(i))
  239. binary.BigEndian.PutUint64(encKey[2:10], b.section)
  240. var decomp []byte
  241. for j := uint64(0); j < b.bloomTrieRatio; j++ {
  242. data, err := core.GetBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j])
  243. if err != nil {
  244. return err
  245. }
  246. decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSectionSize/8))
  247. if err2 != nil {
  248. return err2
  249. }
  250. decomp = append(decomp, decompData...)
  251. }
  252. comp := bitutil.CompressBytes(decomp)
  253. decompSize += uint64(len(decomp))
  254. compSize += uint64(len(comp))
  255. if len(comp) > 0 {
  256. b.trie.Update(encKey[:], comp)
  257. } else {
  258. b.trie.Delete(encKey[:])
  259. }
  260. }
  261. root, err := b.trie.Commit(nil)
  262. if err != nil {
  263. return err
  264. }
  265. b.triedb.Commit(root, false)
  266. sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
  267. log.Info("Storing bloom trie", "section", b.section, "head", sectionHead, "root", root, "compression", float64(compSize)/float64(decompSize))
  268. StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root)
  269. return nil
  270. }