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