core, eth, les: more efficient hash-based header chain retrieval (#16946)

pull/16964/head
Felföldi Zsolt 6 years ago committed by Péter Szilágyi
parent 0255951587
commit 049f5b3572
  1. 12
      core/blockchain.go
  2. 37
      core/headerchain.go
  3. 37
      eth/handler.go
  4. 35
      les/handler.go
  5. 12
      light/lightchain.go

@ -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…
Cancel
Save