Skip to content

Commit 049f5b3

Browse files
zsfelfoldikaralabe
authored andcommitted
core, eth, les: more efficient hash-based header chain retrieval (#16946)
1 parent 0255951 commit 049f5b3

File tree

5 files changed

+106
-27
lines changed

5 files changed

+106
-27
lines changed

core/blockchain.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,6 +1524,18 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com
15241524
return bc.hc.GetBlockHashesFromHash(hash, max)
15251525
}
15261526

1527+
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
1528+
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
1529+
// number of blocks to be individually checked before we reach the canonical chain.
1530+
//
1531+
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
1532+
func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
1533+
bc.chainmu.Lock()
1534+
defer bc.chainmu.Unlock()
1535+
1536+
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
1537+
}
1538+
15271539
// GetHeaderByNumber retrieves a block header from the database by number,
15281540
// caching it (associated with its hash) if found.
15291541
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {

core/headerchain.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,43 @@ func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []co
307307
return chain
308308
}
309309

310+
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
311+
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
312+
// number of blocks to be individually checked before we reach the canonical chain.
313+
//
314+
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
315+
func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
316+
if ancestor > number {
317+
return common.Hash{}, 0
318+
}
319+
if ancestor == 1 {
320+
// in this case it is cheaper to just read the header
321+
if header := hc.GetHeader(hash, number); header != nil {
322+
return header.ParentHash, number - 1
323+
} else {
324+
return common.Hash{}, 0
325+
}
326+
}
327+
for ancestor != 0 {
328+
if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash {
329+
number -= ancestor
330+
return rawdb.ReadCanonicalHash(hc.chainDb, number), number
331+
}
332+
if *maxNonCanonical == 0 {
333+
return common.Hash{}, 0
334+
}
335+
*maxNonCanonical--
336+
ancestor--
337+
header := hc.GetHeader(hash, number)
338+
if header == nil {
339+
return common.Hash{}, 0
340+
}
341+
hash = header.ParentHash
342+
number--
343+
}
344+
return hash, number
345+
}
346+
310347
// GetTd retrieves a block's total difficulty in the canonical chain from the
311348
// database by hash and number, caching it if found.
312349
func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {

eth/handler.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
340340
return errResp(ErrDecode, "%v: %v", msg, err)
341341
}
342342
hashMode := query.Origin.Hash != (common.Hash{})
343+
first := true
344+
maxNonCanonical := uint64(100)
343345

344346
// Gather headers until the fetch or network limits is reached
345347
var (
@@ -351,31 +353,36 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
351353
// Retrieve the next header satisfying the query
352354
var origin *types.Header
353355
if hashMode {
354-
origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
356+
if first {
357+
first = false
358+
origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
359+
if origin != nil {
360+
query.Origin.Number = origin.Number.Uint64()
361+
}
362+
} else {
363+
origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number)
364+
}
355365
} else {
356366
origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
357367
}
358368
if origin == nil {
359369
break
360370
}
361-
number := origin.Number.Uint64()
362371
headers = append(headers, origin)
363372
bytes += estHeaderRlpSize
364373

365374
// Advance to the next header of the query
366375
switch {
367-
case query.Origin.Hash != (common.Hash{}) && query.Reverse:
376+
case hashMode && query.Reverse:
368377
// Hash based traversal towards the genesis block
369-
for i := 0; i < int(query.Skip)+1; i++ {
370-
if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil {
371-
query.Origin.Hash = header.ParentHash
372-
number--
373-
} else {
374-
unknown = true
375-
break
376-
}
378+
ancestor := query.Skip + 1
379+
if ancestor == 0 {
380+
unknown = true
381+
} else {
382+
query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical)
383+
unknown = (query.Origin.Hash == common.Hash{})
377384
}
378-
case query.Origin.Hash != (common.Hash{}) && !query.Reverse:
385+
case hashMode && !query.Reverse:
379386
// Hash based traversal towards the leaf block
380387
var (
381388
current = origin.Number.Uint64()
@@ -387,8 +394,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
387394
unknown = true
388395
} else {
389396
if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
390-
if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
391-
query.Origin.Hash = header.Hash()
397+
nextHash := header.Hash()
398+
expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical)
399+
if expOldHash == query.Origin.Hash {
400+
query.Origin.Hash, query.Origin.Number = nextHash, next
392401
} else {
393402
unknown = true
394403
}

les/handler.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ type BlockChain interface {
8383
InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
8484
Rollback(chain []common.Hash)
8585
GetHeaderByNumber(number uint64) *types.Header
86-
GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash
86+
GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64)
8787
Genesis() *types.Block
8888
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
8989
}
@@ -419,6 +419,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
419419
}
420420

421421
hashMode := query.Origin.Hash != (common.Hash{})
422+
first := true
423+
maxNonCanonical := uint64(100)
422424

423425
// Gather headers until the fetch or network limits is reached
424426
var (
@@ -430,29 +432,34 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
430432
// Retrieve the next header satisfying the query
431433
var origin *types.Header
432434
if hashMode {
433-
origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
435+
if first {
436+
first = false
437+
origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash)
438+
if origin != nil {
439+
query.Origin.Number = origin.Number.Uint64()
440+
}
441+
} else {
442+
origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number)
443+
}
434444
} else {
435445
origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
436446
}
437447
if origin == nil {
438448
break
439449
}
440-
number := origin.Number.Uint64()
441450
headers = append(headers, origin)
442451
bytes += estHeaderRlpSize
443452

444453
// Advance to the next header of the query
445454
switch {
446455
case hashMode && query.Reverse:
447456
// Hash based traversal towards the genesis block
448-
for i := 0; i < int(query.Skip)+1; i++ {
449-
if header := pm.blockchain.GetHeader(query.Origin.Hash, number); header != nil {
450-
query.Origin.Hash = header.ParentHash
451-
number--
452-
} else {
453-
unknown = true
454-
break
455-
}
457+
ancestor := query.Skip + 1
458+
if ancestor == 0 {
459+
unknown = true
460+
} else {
461+
query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical)
462+
unknown = (query.Origin.Hash == common.Hash{})
456463
}
457464
case hashMode && !query.Reverse:
458465
// Hash based traversal towards the leaf block
@@ -466,8 +473,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
466473
unknown = true
467474
} else {
468475
if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
469-
if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
470-
query.Origin.Hash = header.Hash()
476+
nextHash := header.Hash()
477+
expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical)
478+
if expOldHash == query.Origin.Hash {
479+
query.Origin.Hash, query.Origin.Number = nextHash, next
471480
} else {
472481
unknown = true
473482
}

light/lightchain.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,18 @@ func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
433433
return self.hc.GetBlockHashesFromHash(hash, max)
434434
}
435435

436+
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
437+
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
438+
// number of blocks to be individually checked before we reach the canonical chain.
439+
//
440+
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
441+
func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
442+
bc.chainmu.Lock()
443+
defer bc.chainmu.Unlock()
444+
445+
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
446+
}
447+
436448
// GetHeaderByNumber retrieves a block header from the database by number,
437449
// caching it (associated with its hash) if found.
438450
func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {

0 commit comments

Comments
 (0)