github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/core/chain_indexer.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package core
    26  
    27  import (
    28  	"context"
    29  	"encoding/binary"
    30  	"fmt"
    31  	"sync"
    32  	"sync/atomic"
    33  	"time"
    34  
    35  	"github.com/ethereum/go-ethereum/common"
    36  	"github.com/ethereum/go-ethereum/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/core/types"
    38  	"github.com/ethereum/go-ethereum/ethdb"
    39  	"github.com/ethereum/go-ethereum/event"
    40  	"github.com/ethereum/go-ethereum/log"
    41  )
    42  
    43  //chainindexerbackend定义在中处理链段所需的方法
    44  //并将段结果写入数据库。这些可以
    45  //用于创建筛选器bloom或chts。
    46  type ChainIndexerBackend interface {
    47  //重置启动新链段的处理,可能终止
    48  //任何部分完成的操作(如果是REORG)。
    49  	Reset(ctx context.Context, section uint64, prevHead common.Hash) error
    50  
    51  //在链段的下一个收割台上进行加工。呼叫者
    52  //将确保头的顺序。
    53  	Process(ctx context.Context, header *types.Header) error
    54  
    55  //提交完成节元数据并将其存储到数据库中。
    56  	Commit() error
    57  }
    58  
    59  //ChainIndexerChain接口用于将索引器连接到区块链
    60  type ChainIndexerChain interface {
    61  //当前头检索最新的本地已知头。
    62  	CurrentHeader() *types.Header
    63  
    64  //subscribeChainevent订阅新的头消息头通知。
    65  	SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription
    66  }
    67  
    68  //链式索引器对
    69  //规范链(如blooombits和cht结构)。链索引器是
    70  //通过事件系统通过启动
    71  //Goroutine中的ChainEventLoop。
    72  //
    73  //还可以添加使用父级输出的子链索引器
    74  //节索引器。这些子索引器仅接收新的头通知
    75  //在完成整个部分之后,或者在回滚的情况下,
    76  //影响已完成的节。
    77  type ChainIndexer struct {
    78  chainDb  ethdb.Database      //链接数据库以索引来自的数据
    79  indexDb  ethdb.Database      //要写入索引元数据的数据库的前缀表视图
    80  backend  ChainIndexerBackend //生成索引数据内容的后台处理器
    81  children []*ChainIndexer     //将链更新级联到的子索引器
    82  
    83  active    uint32          //标记事件循环是否已启动
    84  update    chan struct{}   //应处理邮件头的通知通道
    85  quit      chan chan error //退出频道以删除正在运行的Goroutines
    86  	ctx       context.Context
    87  	ctxCancel func()
    88  
    89  sectionSize uint64 //要处理的单个链段中的块数
    90  confirmsReq uint64 //处理已完成段之前的确认数
    91  
    92  storedSections uint64 //成功编入数据库的节数
    93  knownSections  uint64 //已知完整的节数(按块)
    94  cascadedHead   uint64 //层叠到子索引器的上一个已完成节的块号
    95  
    96  throttling time.Duration //磁盘限制以防止大量升级占用资源
    97  
    98  	log  log.Logger
    99  	lock sync.RWMutex
   100  }
   101  
   102  //NewChainIndexer创建一个新的链索引器以在其上进行后台处理
   103  //经过一定数量的确认之后,给定大小的链段。
   104  //Throttling参数可用于防止数据库不稳定。
   105  func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBackend, section, confirm uint64, throttling time.Duration, kind string) *ChainIndexer {
   106  	c := &ChainIndexer{
   107  		chainDb:     chainDb,
   108  		indexDb:     indexDb,
   109  		backend:     backend,
   110  		update:      make(chan struct{}, 1),
   111  		quit:        make(chan chan error),
   112  		sectionSize: section,
   113  		confirmsReq: confirm,
   114  		throttling:  throttling,
   115  		log:         log.New("type", kind),
   116  	}
   117  //初始化与数据库相关的字段并启动更新程序
   118  	c.loadValidSections()
   119  	c.ctx, c.ctxCancel = context.WithCancel(context.Background())
   120  
   121  	go c.updateLoop()
   122  
   123  	return c
   124  }
   125  
   126  //addknownsectionHead将新的分区头标记为已知/已处理(如果更新)
   127  //比已知最好的部门主管
   128  func (c *ChainIndexer) AddKnownSectionHead(section uint64, shead common.Hash) {
   129  	c.lock.Lock()
   130  	defer c.lock.Unlock()
   131  
   132  	if section < c.storedSections {
   133  		return
   134  	}
   135  	c.setSectionHead(section, shead)
   136  	c.setValidSections(section + 1)
   137  }
   138  
   139  //Start创建一个goroutine以将链头事件馈送到索引器中
   140  //级联后台处理。孩子们不需要开始,他们
   141  //父母会通知他们新的活动。
   142  func (c *ChainIndexer) Start(chain ChainIndexerChain) {
   143  	events := make(chan ChainEvent, 10)
   144  	sub := chain.SubscribeChainEvent(events)
   145  
   146  	go c.eventLoop(chain.CurrentHeader(), events, sub)
   147  }
   148  
   149  //关闭索引器的所有goroutine并返回任何错误
   150  //这可能发生在内部。
   151  func (c *ChainIndexer) Close() error {
   152  	var errs []error
   153  
   154  	c.ctxCancel()
   155  
   156  //关闭主更新循环
   157  	errc := make(chan error)
   158  	c.quit <- errc
   159  	if err := <-errc; err != nil {
   160  		errs = append(errs, err)
   161  	}
   162  //如果需要,请关闭辅助事件循环
   163  	if atomic.LoadUint32(&c.active) != 0 {
   164  		c.quit <- errc
   165  		if err := <-errc; err != nil {
   166  			errs = append(errs, err)
   167  		}
   168  	}
   169  //关闭所有子项
   170  	for _, child := range c.children {
   171  		if err := child.Close(); err != nil {
   172  			errs = append(errs, err)
   173  		}
   174  	}
   175  //返回任何失败
   176  	switch {
   177  	case len(errs) == 0:
   178  		return nil
   179  
   180  	case len(errs) == 1:
   181  		return errs[0]
   182  
   183  	default:
   184  		return fmt.Errorf("%v", errs)
   185  	}
   186  }
   187  
   188  //EventLoop是索引器的辅助-可选-事件循环,仅
   189  //已启动,以便最外部的索引器将链头事件推送到处理中
   190  //排队。
   191  func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainEvent, sub event.Subscription) {
   192  //将链索引器标记为活动,需要额外拆卸
   193  	atomic.StoreUint32(&c.active, 1)
   194  
   195  	defer sub.Unsubscribe()
   196  
   197  //启动初始的新head事件以开始任何未完成的处理
   198  	c.newHead(currentHeader.Number.Uint64(), false)
   199  
   200  	var (
   201  		prevHeader = currentHeader
   202  		prevHash   = currentHeader.Hash()
   203  	)
   204  	for {
   205  		select {
   206  		case errc := <-c.quit:
   207  //链索引器终止,报告无故障并中止
   208  			errc <- nil
   209  			return
   210  
   211  		case ev, ok := <-events:
   212  //收到新事件,确保不是零(关闭)并更新
   213  			if !ok {
   214  				errc := <-c.quit
   215  				errc <- nil
   216  				return
   217  			}
   218  			header := ev.Block.Header()
   219  			if header.ParentHash != prevHash {
   220  //REORG到公共祖先(可能不存在于光同步模式中,请跳过REORG)
   221  //托多(卡拉贝尔,兹费尔福迪):这似乎有点脆弱,我们能明确地检测到这个病例吗?
   222  
   223  //TODO(karalabe):此操作成本高昂,可能会阻塞,导致事件系统
   224  //可能也会锁定。我们需要以不同的方式处理。
   225  				if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
   226  					c.newHead(h.Number.Uint64(), true)
   227  				}
   228  			}
   229  			c.newHead(header.Number.Uint64(), false)
   230  
   231  			prevHeader, prevHash = header, header.Hash()
   232  		}
   233  	}
   234  }
   235  
   236  //newhead通知索引器有关新链头和/或重新排序的信息。
   237  func (c *ChainIndexer) newHead(head uint64, reorg bool) {
   238  	c.lock.Lock()
   239  	defer c.lock.Unlock()
   240  
   241  //如果发生了REORG,则在该点之前使所有部分无效
   242  	if reorg {
   243  //将已知的节号恢复到REORG点
   244  		changed := head / c.sectionSize
   245  		if changed < c.knownSections {
   246  			c.knownSections = changed
   247  		}
   248  //将存储的部分从数据库还原到REORG点
   249  		if changed < c.storedSections {
   250  			c.setValidSections(changed)
   251  		}
   252  //将新的头编号更新到最终确定的节结尾并通知子级
   253  		head = changed * c.sectionSize
   254  
   255  		if head < c.cascadedHead {
   256  			c.cascadedHead = head
   257  			for _, child := range c.children {
   258  				child.newHead(c.cascadedHead, true)
   259  			}
   260  		}
   261  		return
   262  	}
   263  //无REORG,计算新已知部分的数量,如果足够高则更新
   264  	var sections uint64
   265  	if head >= c.confirmsReq {
   266  		sections = (head + 1 - c.confirmsReq) / c.sectionSize
   267  		if sections > c.knownSections {
   268  			c.knownSections = sections
   269  
   270  			select {
   271  			case c.update <- struct{}{}:
   272  			default:
   273  			}
   274  		}
   275  	}
   276  }
   277  
   278  //updateLoop是推动链段的索引器的主要事件循环
   279  //进入处理后端。
   280  func (c *ChainIndexer) updateLoop() {
   281  	var (
   282  		updating bool
   283  		updated  time.Time
   284  	)
   285  
   286  	for {
   287  		select {
   288  		case errc := <-c.quit:
   289  //链索引器终止,报告无故障并中止
   290  			errc <- nil
   291  			return
   292  
   293  		case <-c.update:
   294  //节头已完成(或回滚),请更新索引
   295  			c.lock.Lock()
   296  			if c.knownSections > c.storedSections {
   297  //定期向用户打印升级日志消息
   298  				if time.Since(updated) > 8*time.Second {
   299  					if c.knownSections > c.storedSections+1 {
   300  						updating = true
   301  						c.log.Info("Upgrading chain index", "percentage", c.storedSections*100/c.knownSections)
   302  					}
   303  					updated = time.Now()
   304  				}
   305  //缓存当前节计数和头以允许解锁互斥体
   306  				section := c.storedSections
   307  				var oldHead common.Hash
   308  				if section > 0 {
   309  					oldHead = c.SectionHead(section - 1)
   310  				}
   311  //在后台处理新定义的节
   312  				c.lock.Unlock()
   313  				newHead, err := c.processSection(section, oldHead)
   314  				if err != nil {
   315  					select {
   316  					case <-c.ctx.Done():
   317  						<-c.quit <- nil
   318  						return
   319  					default:
   320  					}
   321  					c.log.Error("Section processing failed", "error", err)
   322  				}
   323  				c.lock.Lock()
   324  
   325  //如果处理成功且没有发生重新排序,则标记该节已完成
   326  				if err == nil && oldHead == c.SectionHead(section-1) {
   327  					c.setSectionHead(section, newHead)
   328  					c.setValidSections(section + 1)
   329  					if c.storedSections == c.knownSections && updating {
   330  						updating = false
   331  						c.log.Info("Finished upgrading chain index")
   332  					}
   333  
   334  					c.cascadedHead = c.storedSections*c.sectionSize - 1
   335  					for _, child := range c.children {
   336  						c.log.Trace("Cascading chain index update", "head", c.cascadedHead)
   337  						child.newHead(c.cascadedHead, false)
   338  					}
   339  				} else {
   340  //如果处理失败,在进一步通知之前不要重试
   341  					c.log.Debug("Chain index processing failed", "section", section, "err", err)
   342  					c.knownSections = c.storedSections
   343  				}
   344  			}
   345  //如果还有其他部分需要处理,请重新安排
   346  			if c.knownSections > c.storedSections {
   347  				time.AfterFunc(c.throttling, func() {
   348  					select {
   349  					case c.update <- struct{}{}:
   350  					default:
   351  					}
   352  				})
   353  			}
   354  			c.lock.Unlock()
   355  		}
   356  	}
   357  }
   358  
   359  //processSection通过调用后端函数来处理整个部分,而
   360  //确保通过的收割台的连续性。因为链互斥体不是
   361  //在处理过程中,连续性可以通过一个长的REORG来打破,其中
   362  //case函数返回时出错。
   363  func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (common.Hash, error) {
   364  	c.log.Trace("Processing new chain section", "section", section)
   365  
   366  //复位和部分处理
   367  
   368  	if err := c.backend.Reset(c.ctx, section, lastHead); err != nil {
   369  		c.setValidSections(0)
   370  		return common.Hash{}, err
   371  	}
   372  
   373  	for number := section * c.sectionSize; number < (section+1)*c.sectionSize; number++ {
   374  		hash := rawdb.ReadCanonicalHash(c.chainDb, number)
   375  		if hash == (common.Hash{}) {
   376  			return common.Hash{}, fmt.Errorf("canonical block #%d unknown", number)
   377  		}
   378  		header := rawdb.ReadHeader(c.chainDb, hash, number)
   379  		if header == nil {
   380  			return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4])
   381  		} else if header.ParentHash != lastHead {
   382  			return common.Hash{}, fmt.Errorf("chain reorged during section processing")
   383  		}
   384  		if err := c.backend.Process(c.ctx, header); err != nil {
   385  			return common.Hash{}, err
   386  		}
   387  		lastHead = header.Hash()
   388  	}
   389  	if err := c.backend.Commit(); err != nil {
   390  		return common.Hash{}, err
   391  	}
   392  	return lastHead, nil
   393  }
   394  
   395  //区段返回索引器维护的已处理区段数。
   396  //以及关于最后一个为潜在规范索引的头的信息
   397  //验证。
   398  func (c *ChainIndexer) Sections() (uint64, uint64, common.Hash) {
   399  	c.lock.Lock()
   400  	defer c.lock.Unlock()
   401  
   402  	return c.storedSections, c.storedSections*c.sectionSize - 1, c.SectionHead(c.storedSections - 1)
   403  }
   404  
   405  //AddChildIndexer添加了一个子链索引器,该子链索引器可以使用此子链索引器的输出
   406  func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
   407  	c.lock.Lock()
   408  	defer c.lock.Unlock()
   409  
   410  	c.children = append(c.children, indexer)
   411  
   412  //将所有挂起的更新层叠到新子级
   413  	if c.storedSections > 0 {
   414  		indexer.newHead(c.storedSections*c.sectionSize-1, false)
   415  	}
   416  }
   417  
   418  //loadvalidSections从索引数据库中读取有效节数。
   419  //并且缓存进入本地状态。
   420  func (c *ChainIndexer) loadValidSections() {
   421  	data, _ := c.indexDb.Get([]byte("count"))
   422  	if len(data) == 8 {
   423  		c.storedSections = binary.BigEndian.Uint64(data[:])
   424  	}
   425  }
   426  
   427  //setvalidSections将有效节数写入索引数据库
   428  func (c *ChainIndexer) setValidSections(sections uint64) {
   429  //设置数据库中有效节的当前数目
   430  	var data [8]byte
   431  	binary.BigEndian.PutUint64(data[:], sections)
   432  	c.indexDb.Put([]byte("count"), data[:])
   433  
   434  //删除所有重新排序的部分,同时缓存有效数据
   435  	for c.storedSections > sections {
   436  		c.storedSections--
   437  		c.removeSectionHead(c.storedSections)
   438  	}
   439  c.storedSections = sections //如果新的>旧的,则需要
   440  }
   441  
   442  //sectionhead从
   443  //索引数据库。
   444  func (c *ChainIndexer) SectionHead(section uint64) common.Hash {
   445  	var data [8]byte
   446  	binary.BigEndian.PutUint64(data[:], section)
   447  
   448  	hash, _ := c.indexDb.Get(append([]byte("shead"), data[:]...))
   449  	if len(hash) == len(common.Hash{}) {
   450  		return common.BytesToHash(hash)
   451  	}
   452  	return common.Hash{}
   453  }
   454  
   455  //setSectionHead将已处理节的最后一个块哈希写入索引
   456  //数据库。
   457  func (c *ChainIndexer) setSectionHead(section uint64, hash common.Hash) {
   458  	var data [8]byte
   459  	binary.BigEndian.PutUint64(data[:], section)
   460  
   461  	c.indexDb.Put(append([]byte("shead"), data[:]...), hash.Bytes())
   462  }
   463  
   464  //removeSectionHead从索引中删除对已处理节的引用
   465  //数据库。
   466  func (c *ChainIndexer) removeSectionHead(section uint64) {
   467  	var data [8]byte
   468  	binary.BigEndian.PutUint64(data[:], section)
   469  
   470  	c.indexDb.Delete(append([]byte("shead"), data[:]...))
   471  }