core, eth, les: more efficient hash-based header chain retrieval (#16946)
This commit is contained in:
		
				
					committed by
					
						
						Péter Szilágyi
					
				
			
			
				
	
			
			
			
						parent
						
							0255951587
						
					
				
				
					commit
					049f5b3572
				
			@@ -1524,6 +1524,18 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com
 | 
				
			|||||||
	return bc.hc.GetBlockHashesFromHash(hash, max)
 | 
						return bc.hc.GetBlockHashesFromHash(hash, max)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
 | 
				
			||||||
 | 
					// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
 | 
				
			||||||
 | 
					// number of blocks to be individually checked before we reach the canonical chain.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
 | 
				
			||||||
 | 
					func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
 | 
				
			||||||
 | 
						bc.chainmu.Lock()
 | 
				
			||||||
 | 
						defer bc.chainmu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetHeaderByNumber retrieves a block header from the database by number,
 | 
					// GetHeaderByNumber retrieves a block header from the database by number,
 | 
				
			||||||
// caching it (associated with its hash) if found.
 | 
					// caching it (associated with its hash) if found.
 | 
				
			||||||
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
 | 
					func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -307,6 +307,43 @@ func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []co
 | 
				
			|||||||
	return chain
 | 
						return chain
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
 | 
				
			||||||
 | 
					// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
 | 
				
			||||||
 | 
					// number of blocks to be individually checked before we reach the canonical chain.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
 | 
				
			||||||
 | 
					func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
 | 
				
			||||||
 | 
						if ancestor > number {
 | 
				
			||||||
 | 
							return common.Hash{}, 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ancestor == 1 {
 | 
				
			||||||
 | 
							// in this case it is cheaper to just read the header
 | 
				
			||||||
 | 
							if header := hc.GetHeader(hash, number); header != nil {
 | 
				
			||||||
 | 
								return header.ParentHash, number - 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return common.Hash{}, 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for ancestor != 0 {
 | 
				
			||||||
 | 
							if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash {
 | 
				
			||||||
 | 
								number -= ancestor
 | 
				
			||||||
 | 
								return rawdb.ReadCanonicalHash(hc.chainDb, number), number
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if *maxNonCanonical == 0 {
 | 
				
			||||||
 | 
								return common.Hash{}, 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*maxNonCanonical--
 | 
				
			||||||
 | 
							ancestor--
 | 
				
			||||||
 | 
							header := hc.GetHeader(hash, number)
 | 
				
			||||||
 | 
							if header == nil {
 | 
				
			||||||
 | 
								return common.Hash{}, 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							hash = header.ParentHash
 | 
				
			||||||
 | 
							number--
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return hash, number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetTd retrieves a block's total difficulty in the canonical chain from the
 | 
					// GetTd retrieves a block's total difficulty in the canonical chain from the
 | 
				
			||||||
// database by hash and number, caching it if found.
 | 
					// database by hash and number, caching it if found.
 | 
				
			||||||
func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
 | 
					func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -340,6 +340,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
			return errResp(ErrDecode, "%v: %v", msg, err)
 | 
								return errResp(ErrDecode, "%v: %v", msg, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		hashMode := query.Origin.Hash != (common.Hash{})
 | 
							hashMode := query.Origin.Hash != (common.Hash{})
 | 
				
			||||||
 | 
							first := true
 | 
				
			||||||
 | 
							maxNonCanonical := uint64(100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Gather headers until the fetch or network limits is reached
 | 
							// Gather headers until the fetch or network limits is reached
 | 
				
			||||||
		var (
 | 
							var (
 | 
				
			||||||
@@ -351,31 +353,36 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
			// Retrieve the next header satisfying the query
 | 
								// Retrieve the next header satisfying the query
 | 
				
			||||||
			var origin *types.Header
 | 
								var origin *types.Header
 | 
				
			||||||
			if hashMode {
 | 
								if hashMode {
 | 
				
			||||||
				origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
 | 
									if first {
 | 
				
			||||||
 | 
										first = false
 | 
				
			||||||
 | 
										origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
 | 
				
			||||||
 | 
										if origin != nil {
 | 
				
			||||||
 | 
											query.Origin.Number = origin.Number.Uint64()
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
 | 
									origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if origin == nil {
 | 
								if origin == nil {
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			number := origin.Number.Uint64()
 | 
					 | 
				
			||||||
			headers = append(headers, origin)
 | 
								headers = append(headers, origin)
 | 
				
			||||||
			bytes += estHeaderRlpSize
 | 
								bytes += estHeaderRlpSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Advance to the next header of the query
 | 
								// Advance to the next header of the query
 | 
				
			||||||
			switch {
 | 
								switch {
 | 
				
			||||||
			case query.Origin.Hash != (common.Hash{}) && query.Reverse:
 | 
								case hashMode && query.Reverse:
 | 
				
			||||||
				// Hash based traversal towards the genesis block
 | 
									// Hash based traversal towards the genesis block
 | 
				
			||||||
				for i := 0; i < int(query.Skip)+1; i++ {
 | 
									ancestor := query.Skip + 1
 | 
				
			||||||
					if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil {
 | 
									if ancestor == 0 {
 | 
				
			||||||
						query.Origin.Hash = header.ParentHash
 | 
										unknown = true
 | 
				
			||||||
						number--
 | 
									} else {
 | 
				
			||||||
					} else {
 | 
										query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical)
 | 
				
			||||||
						unknown = true
 | 
										unknown = (query.Origin.Hash == common.Hash{})
 | 
				
			||||||
						break
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case query.Origin.Hash != (common.Hash{}) && !query.Reverse:
 | 
								case hashMode && !query.Reverse:
 | 
				
			||||||
				// Hash based traversal towards the leaf block
 | 
									// Hash based traversal towards the leaf block
 | 
				
			||||||
				var (
 | 
									var (
 | 
				
			||||||
					current = origin.Number.Uint64()
 | 
										current = origin.Number.Uint64()
 | 
				
			||||||
@@ -387,8 +394,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
					unknown = true
 | 
										unknown = true
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
 | 
										if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
 | 
				
			||||||
						if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
 | 
											nextHash := header.Hash()
 | 
				
			||||||
							query.Origin.Hash = header.Hash()
 | 
											expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical)
 | 
				
			||||||
 | 
											if expOldHash == query.Origin.Hash {
 | 
				
			||||||
 | 
												query.Origin.Hash, query.Origin.Number = nextHash, next
 | 
				
			||||||
						} else {
 | 
											} else {
 | 
				
			||||||
							unknown = true
 | 
												unknown = true
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,7 @@ type BlockChain interface {
 | 
				
			|||||||
	InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
 | 
						InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
 | 
				
			||||||
	Rollback(chain []common.Hash)
 | 
						Rollback(chain []common.Hash)
 | 
				
			||||||
	GetHeaderByNumber(number uint64) *types.Header
 | 
						GetHeaderByNumber(number uint64) *types.Header
 | 
				
			||||||
	GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash
 | 
						GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64)
 | 
				
			||||||
	Genesis() *types.Block
 | 
						Genesis() *types.Block
 | 
				
			||||||
	SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
 | 
						SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -419,6 +419,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hashMode := query.Origin.Hash != (common.Hash{})
 | 
							hashMode := query.Origin.Hash != (common.Hash{})
 | 
				
			||||||
 | 
							first := true
 | 
				
			||||||
 | 
							maxNonCanonical := uint64(100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Gather headers until the fetch or network limits is reached
 | 
							// Gather headers until the fetch or network limits is reached
 | 
				
			||||||
		var (
 | 
							var (
 | 
				
			||||||
@@ -430,14 +432,21 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
			// Retrieve the next header satisfying the query
 | 
								// Retrieve the next header satisfying the query
 | 
				
			||||||
			var origin *types.Header
 | 
								var origin *types.Header
 | 
				
			||||||
			if hashMode {
 | 
								if hashMode {
 | 
				
			||||||
				origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
 | 
									if first {
 | 
				
			||||||
 | 
										first = false
 | 
				
			||||||
 | 
										origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
 | 
				
			||||||
 | 
										if origin != nil {
 | 
				
			||||||
 | 
											query.Origin.Number = origin.Number.Uint64()
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
 | 
									origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if origin == nil {
 | 
								if origin == nil {
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			number := origin.Number.Uint64()
 | 
					 | 
				
			||||||
			headers = append(headers, origin)
 | 
								headers = append(headers, origin)
 | 
				
			||||||
			bytes += estHeaderRlpSize
 | 
								bytes += estHeaderRlpSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -445,14 +454,12 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
			switch {
 | 
								switch {
 | 
				
			||||||
			case hashMode && query.Reverse:
 | 
								case hashMode && query.Reverse:
 | 
				
			||||||
				// Hash based traversal towards the genesis block
 | 
									// Hash based traversal towards the genesis block
 | 
				
			||||||
				for i := 0; i < int(query.Skip)+1; i++ {
 | 
									ancestor := query.Skip + 1
 | 
				
			||||||
					if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil {
 | 
									if ancestor == 0 {
 | 
				
			||||||
						query.Origin.Hash = header.ParentHash
 | 
										unknown = true
 | 
				
			||||||
						number--
 | 
									} else {
 | 
				
			||||||
					} else {
 | 
										query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical)
 | 
				
			||||||
						unknown = true
 | 
										unknown = (query.Origin.Hash == common.Hash{})
 | 
				
			||||||
						break
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case hashMode && !query.Reverse:
 | 
								case hashMode && !query.Reverse:
 | 
				
			||||||
				// Hash based traversal towards the leaf block
 | 
									// Hash based traversal towards the leaf block
 | 
				
			||||||
@@ -466,8 +473,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
 | 
				
			|||||||
					unknown = true
 | 
										unknown = true
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
 | 
										if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
 | 
				
			||||||
						if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
 | 
											nextHash := header.Hash()
 | 
				
			||||||
							query.Origin.Hash = header.Hash()
 | 
											expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical)
 | 
				
			||||||
 | 
											if expOldHash == query.Origin.Hash {
 | 
				
			||||||
 | 
												query.Origin.Hash, query.Origin.Number = nextHash, next
 | 
				
			||||||
						} else {
 | 
											} else {
 | 
				
			||||||
							unknown = true
 | 
												unknown = true
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -433,6 +433,18 @@ func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
 | 
				
			|||||||
	return self.hc.GetBlockHashesFromHash(hash, max)
 | 
						return self.hc.GetBlockHashesFromHash(hash, max)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
 | 
				
			||||||
 | 
					// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
 | 
				
			||||||
 | 
					// number of blocks to be individually checked before we reach the canonical chain.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
 | 
				
			||||||
 | 
					func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
 | 
				
			||||||
 | 
						bc.chainmu.Lock()
 | 
				
			||||||
 | 
						defer bc.chainmu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetHeaderByNumber retrieves a block header from the database by number,
 | 
					// GetHeaderByNumber retrieves a block header from the database by number,
 | 
				
			||||||
// caching it (associated with its hash) if found.
 | 
					// caching it (associated with its hash) if found.
 | 
				
			||||||
func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {
 | 
					func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user