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