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