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 }