core, eth, les: more efficient hash-based header chain retrieval (#16946)
This commit is contained in:
		
							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 { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user