Skip to content

Commit 483466f

Browse files
authored
fix(dot/sync): fix "block with unknown header is ready" error (#2191)
1 parent 5fd7d3f commit 483466f

File tree

4 files changed

+56
-22
lines changed

4 files changed

+56
-22
lines changed

dot/sync/chain_sync.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import (
1313
"sync"
1414
"time"
1515

16-
"github.com/ChainSafe/gossamer/dot/peerset"
16+
"github.com/ChainSafe/chaindb"
1717
"github.com/libp2p/go-libp2p-core/peer"
1818

1919
"github.com/ChainSafe/gossamer/dot/network"
20+
"github.com/ChainSafe/gossamer/dot/peerset"
2021
"github.com/ChainSafe/gossamer/dot/types"
2122
"github.com/ChainSafe/gossamer/lib/common"
2223
"github.com/ChainSafe/gossamer/lib/common/variadic"
@@ -522,7 +523,7 @@ func (cs *chainSync) setMode(mode chainSyncState) {
522523
case bootstrap:
523524
cs.handler = newBootstrapSyncer(cs.blockState)
524525
case tip:
525-
cs.handler = newTipSyncer(cs.blockState, cs.pendingBlocks, cs.readyBlocks)
526+
cs.handler = newTipSyncer(cs.blockState, cs.pendingBlocks, cs.readyBlocks, cs.handleReadyBlock)
526527
}
527528

528529
cs.state = mode
@@ -692,41 +693,59 @@ func (cs *chainSync) doSync(req *network.BlockRequestMessage, peersTried map[pee
692693
// response was validated! place into ready block queue
693694
for _, bd := range resp.BlockData {
694695
// block is ready to be processed!
695-
handleReadyBlock(bd, cs.pendingBlocks, cs.readyBlocks)
696+
cs.handleReadyBlock(bd)
696697
}
697698

698699
return nil
699700
}
700701

701-
func handleReadyBlock(bd *types.BlockData, pendingBlocks DisjointBlockSet, readyBlocks *blockQueue) {
702-
// see if there are any descendents in the pending queue that are now ready to be processed,
703-
// as we have just become aware of their parent block
702+
func (cs *chainSync) handleReadyBlock(bd *types.BlockData) {
703+
if cs.readyBlocks.has(bd.Hash) {
704+
logger.Tracef("ignoring block %s in response, already in ready queue", bd.Hash)
705+
return
706+
}
704707

705708
// if header was not requested, get it from the pending set
706709
// if we're expecting headers, validate should ensure we have a header
707710
if bd.Header == nil {
708-
block := pendingBlocks.getBlock(bd.Hash)
711+
block := cs.pendingBlocks.getBlock(bd.Hash)
709712
if block == nil {
710-
logger.Criticalf("block with unknown header is ready: hash=%s", bd.Hash)
713+
// block wasn't in the pending set!
714+
// let's check the db as maybe we already processed it
715+
has, err := cs.blockState.HasHeader(bd.Hash)
716+
if err != nil && !errors.Is(err, chaindb.ErrKeyNotFound) {
717+
logger.Debugf("failed to check if header is known for hash %s: %s", bd.Hash, err)
718+
return
719+
}
720+
721+
if has {
722+
logger.Tracef("ignoring block we've already processed, hash=%s", bd.Hash)
723+
return
724+
}
725+
726+
// this is bad and shouldn't happen
727+
logger.Errorf("block with unknown header is ready: hash=%s", bd.Hash)
711728
return
712729
}
713730

714731
bd.Header = block.header
715732
}
716733

717734
if bd.Header == nil {
718-
logger.Criticalf("new ready block number (unknown) with hash %s", bd.Hash)
735+
logger.Errorf("new ready block number (unknown) with hash %s", bd.Hash)
719736
return
720737
}
721738

722739
logger.Tracef("new ready block number %s with hash %s", bd.Header.Number, bd.Hash)
723740

741+
// see if there are any descendents in the pending queue that are now ready to be processed,
742+
// as we have just become aware of their parent block
724743
ready := []*types.BlockData{bd}
725-
ready = pendingBlocks.getReadyDescendants(bd.Hash, ready)
744+
ready = cs.pendingBlocks.getReadyDescendants(bd.Hash, ready)
726745

727746
for _, rb := range ready {
728-
pendingBlocks.removeBlock(rb.Hash)
729-
readyBlocks.push(rb)
747+
cs.pendingBlocks.removeBlock(rb.Hash)
748+
cs.readyBlocks.push(rb)
730749
}
731750
}
732751

dot/sync/chain_sync_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ func TestChainSync_doSync(t *testing.T) {
662662
resp := &network.BlockResponseMessage{
663663
BlockData: []*types.BlockData{
664664
{
665+
Hash: common.Hash{0x1},
665666
Header: &types.Header{
666667
Number: big.NewInt(1),
667668
},
@@ -687,13 +688,15 @@ func TestChainSync_doSync(t *testing.T) {
687688
resp = &network.BlockResponseMessage{
688689
BlockData: []*types.BlockData{
689690
{
691+
Hash: common.Hash{0x3},
690692
Header: &types.Header{
691693
ParentHash: parent,
692694
Number: big.NewInt(3),
693695
},
694696
Body: &types.Body{},
695697
},
696698
{
699+
Hash: common.Hash{0x2},
697700
Header: &types.Header{
698701
Number: big.NewInt(2),
699702
},
@@ -762,7 +765,7 @@ func TestHandleReadyBlock(t *testing.T) {
762765
}
763766
cs.pendingBlocks.addBlock(block2NotDescendant)
764767

765-
handleReadyBlock(block1.ToBlockData(), cs.pendingBlocks, cs.readyBlocks)
768+
cs.handleReadyBlock(block1.ToBlockData())
766769

767770
require.False(t, cs.pendingBlocks.hasBlock(header1.Hash()))
768771
require.False(t, cs.pendingBlocks.hasBlock(header2.Hash()))

dot/sync/tip_syncer.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,29 @@ import (
88
"math/big"
99

1010
"github.com/ChainSafe/gossamer/dot/network"
11+
"github.com/ChainSafe/gossamer/dot/types"
1112
"github.com/ChainSafe/gossamer/lib/common"
1213
)
1314

1415
var _ workHandler = &tipSyncer{}
1516

17+
type handleReadyBlockFunc func(*types.BlockData)
18+
1619
// tipSyncer handles workers when syncing at the tip of the chain
1720
type tipSyncer struct {
18-
blockState BlockState
19-
pendingBlocks DisjointBlockSet
20-
readyBlocks *blockQueue
21+
blockState BlockState
22+
pendingBlocks DisjointBlockSet
23+
readyBlocks *blockQueue
24+
handleReadyBlock handleReadyBlockFunc
2125
}
2226

23-
func newTipSyncer(blockState BlockState, pendingBlocks DisjointBlockSet, readyBlocks *blockQueue) *tipSyncer {
27+
func newTipSyncer(blockState BlockState, pendingBlocks DisjointBlockSet, readyBlocks *blockQueue,
28+
handleReadyBlock handleReadyBlockFunc) *tipSyncer {
2429
return &tipSyncer{
25-
blockState: blockState,
26-
pendingBlocks: pendingBlocks,
27-
readyBlocks: readyBlocks,
30+
blockState: blockState,
31+
pendingBlocks: pendingBlocks,
32+
readyBlocks: readyBlocks,
33+
handleReadyBlock: handleReadyBlock,
2834
}
2935
}
3036

@@ -210,7 +216,7 @@ func (s *tipSyncer) handleTick() ([]*worker, error) {
210216
if has || s.readyBlocks.has(block.header.ParentHash) {
211217
// block is ready, as parent is known!
212218
// also, move any pendingBlocks that are descendants of this block to the ready blocks queue
213-
handleReadyBlock(block.toBlockData(), s.pendingBlocks, s.readyBlocks)
219+
s.handleReadyBlock(block.toBlockData())
214220
continue
215221
}
216222

dot/sync/tip_syncer_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ func newTestTipSyncer(t *testing.T) *tipSyncer {
2828

2929
readyBlocks := newBlockQueue(maxResponseSize)
3030
pendingBlocks := newDisjointBlockSet(pendingBlocksLimit)
31-
return newTipSyncer(bs, pendingBlocks, readyBlocks)
31+
cs := &chainSync{
32+
blockState: bs,
33+
readyBlocks: readyBlocks,
34+
pendingBlocks: pendingBlocks,
35+
}
36+
37+
return newTipSyncer(bs, pendingBlocks, readyBlocks, cs.handleReadyBlock)
3238
}
3339

3440
func TestTipSyncer_handleNewPeerState(t *testing.T) {

0 commit comments

Comments
 (0)