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