core: safe indexer operation when syncing starts before the checkpoint (#17511)
This commit is contained in:
		
							parent
							
								
									b69476b372
								
							
						
					
					
						commit
						63352bf424
					
				| @ -85,6 +85,9 @@ type ChainIndexer struct { | |||||||
| 	knownSections  uint64 // Number of sections known to be complete (block wise)
 | 	knownSections  uint64 // Number of sections known to be complete (block wise)
 | ||||||
| 	cascadedHead   uint64 // Block number of the last completed section cascaded to subindexers
 | 	cascadedHead   uint64 // Block number of the last completed section cascaded to subindexers
 | ||||||
| 
 | 
 | ||||||
|  | 	checkpointSections uint64      // Number of sections covered by the checkpoint
 | ||||||
|  | 	checkpointHead     common.Hash // Section head belonging to the checkpoint
 | ||||||
|  | 
 | ||||||
| 	throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources
 | 	throttling time.Duration // Disk throttling to prevent a heavy upgrade from hogging resources
 | ||||||
| 
 | 
 | ||||||
| 	log  log.Logger | 	log  log.Logger | ||||||
| @ -115,12 +118,19 @@ func NewChainIndexer(chainDb, indexDb ethdb.Database, backend ChainIndexerBacken | |||||||
| 	return c | 	return c | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddKnownSectionHead marks a new section head as known/processed if it is newer
 | // AddCheckpoint adds a checkpoint. Sections are never processed and the chain
 | ||||||
| // than the already known best section head
 | // is not expected to be available before this point. The indexer assumes that
 | ||||||
| func (c *ChainIndexer) AddKnownSectionHead(section uint64, shead common.Hash) { | // the backend has sufficient information available to process subsequent sections.
 | ||||||
|  | //
 | ||||||
|  | // Note: knownSections == 0 and storedSections == checkpointSections until
 | ||||||
|  | // syncing reaches the checkpoint
 | ||||||
|  | func (c *ChainIndexer) AddCheckpoint(section uint64, shead common.Hash) { | ||||||
| 	c.lock.Lock() | 	c.lock.Lock() | ||||||
| 	defer c.lock.Unlock() | 	defer c.lock.Unlock() | ||||||
| 
 | 
 | ||||||
|  | 	c.checkpointSections = section + 1 | ||||||
|  | 	c.checkpointHead = shead | ||||||
|  | 
 | ||||||
| 	if section < c.storedSections { | 	if section < c.storedSections { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -233,16 +243,23 @@ func (c *ChainIndexer) newHead(head uint64, reorg bool) { | |||||||
| 	// If a reorg happened, invalidate all sections until that point
 | 	// If a reorg happened, invalidate all sections until that point
 | ||||||
| 	if reorg { | 	if reorg { | ||||||
| 		// Revert the known section number to the reorg point
 | 		// Revert the known section number to the reorg point
 | ||||||
| 		changed := head / c.sectionSize | 		known := head / c.sectionSize | ||||||
| 		if changed < c.knownSections { | 		stored := known | ||||||
| 			c.knownSections = changed | 		if known < c.checkpointSections { | ||||||
|  | 			known = 0 | ||||||
|  | 		} | ||||||
|  | 		if stored < c.checkpointSections { | ||||||
|  | 			stored = c.checkpointSections | ||||||
|  | 		} | ||||||
|  | 		if known < c.knownSections { | ||||||
|  | 			c.knownSections = known | ||||||
| 		} | 		} | ||||||
| 		// Revert the stored sections from the database to the reorg point
 | 		// Revert the stored sections from the database to the reorg point
 | ||||||
| 		if changed < c.storedSections { | 		if stored < c.storedSections { | ||||||
| 			c.setValidSections(changed) | 			c.setValidSections(stored) | ||||||
| 		} | 		} | ||||||
| 		// Update the new head number to the finalized section end and notify children
 | 		// Update the new head number to the finalized section end and notify children
 | ||||||
| 		head = changed * c.sectionSize | 		head = known * c.sectionSize | ||||||
| 
 | 
 | ||||||
| 		if head < c.cascadedHead { | 		if head < c.cascadedHead { | ||||||
| 			c.cascadedHead = head | 			c.cascadedHead = head | ||||||
| @ -256,7 +273,18 @@ func (c *ChainIndexer) newHead(head uint64, reorg bool) { | |||||||
| 	var sections uint64 | 	var sections uint64 | ||||||
| 	if head >= c.confirmsReq { | 	if head >= c.confirmsReq { | ||||||
| 		sections = (head + 1 - c.confirmsReq) / c.sectionSize | 		sections = (head + 1 - c.confirmsReq) / c.sectionSize | ||||||
|  | 		if sections < c.checkpointSections { | ||||||
|  | 			sections = 0 | ||||||
|  | 		} | ||||||
| 		if sections > c.knownSections { | 		if sections > c.knownSections { | ||||||
|  | 			if c.knownSections < c.checkpointSections { | ||||||
|  | 				// syncing reached the checkpoint, verify section head
 | ||||||
|  | 				syncedHead := rawdb.ReadCanonicalHash(c.chainDb, c.checkpointSections*c.sectionSize-1) | ||||||
|  | 				if syncedHead != c.checkpointHead { | ||||||
|  | 					c.log.Error("Synced chain does not match checkpoint", "number", c.checkpointSections*c.sectionSize-1, "expected", c.checkpointHead, "synced", syncedHead) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			c.knownSections = sections | 			c.knownSections = sections | ||||||
| 
 | 
 | ||||||
| 			select { | 			select { | ||||||
| @ -401,8 +429,14 @@ func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) { | |||||||
| 	c.children = append(c.children, indexer) | 	c.children = append(c.children, indexer) | ||||||
| 
 | 
 | ||||||
| 	// Cascade any pending updates to new children too
 | 	// Cascade any pending updates to new children too
 | ||||||
| 	if c.storedSections > 0 { | 	sections := c.storedSections | ||||||
| 		indexer.newHead(c.storedSections*c.sectionSize-1, false) | 	if c.knownSections < sections { | ||||||
|  | 		// if a section is "stored" but not "known" then it is a checkpoint without
 | ||||||
|  | 		// available chain data so we should not cascade it yet
 | ||||||
|  | 		sections = c.knownSections | ||||||
|  | 	} | ||||||
|  | 	if sections > 0 { | ||||||
|  | 		indexer.newHead(sections*c.sectionSize-1, false) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -121,14 +121,14 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus. | |||||||
| func (self *LightChain) addTrustedCheckpoint(cp TrustedCheckpoint) { | func (self *LightChain) addTrustedCheckpoint(cp TrustedCheckpoint) { | ||||||
| 	if self.odr.ChtIndexer() != nil { | 	if self.odr.ChtIndexer() != nil { | ||||||
| 		StoreChtRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.CHTRoot) | 		StoreChtRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.CHTRoot) | ||||||
| 		self.odr.ChtIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead) | 		self.odr.ChtIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead) | ||||||
| 	} | 	} | ||||||
| 	if self.odr.BloomTrieIndexer() != nil { | 	if self.odr.BloomTrieIndexer() != nil { | ||||||
| 		StoreBloomTrieRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.BloomRoot) | 		StoreBloomTrieRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.BloomRoot) | ||||||
| 		self.odr.BloomTrieIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead) | 		self.odr.BloomTrieIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead) | ||||||
| 	} | 	} | ||||||
| 	if self.odr.BloomIndexer() != nil { | 	if self.odr.BloomIndexer() != nil { | ||||||
| 		self.odr.BloomIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead) | 		self.odr.BloomIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead) | ||||||
| 	} | 	} | ||||||
| 	log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.SectionIdx+1)*self.indexerConfig.ChtSize-1, "hash", cp.SectionHead) | 	log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.SectionIdx+1)*self.indexerConfig.ChtSize-1, "hash", cp.SectionHead) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user