diff --git a/chain/sync.go b/chain/sync.go index 61c1d2094e7..23c591c5010 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -1449,7 +1449,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know return nil, ErrForkCheckpoint } - // TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? + // TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? Yes. // Would it not be better to ask in smaller chunks, given that an ~ForkLengthThreshold is very rare? tips, err := syncer.Exchange.GetBlocks(ctx, incoming.Parents(), int(build.ForkLengthThreshold)) if err != nil { @@ -1460,6 +1460,10 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know if err != nil { return nil, xerrors.Errorf("failed to load next local tipset: %w", err) } + // Track the fork length on our side of the synced chain to enforce + // `ForkLengthThreshold`. Initialized to 1 because we already walked back + // one tipset from `known` (our synced head). + forkLengthInHead := 1 for cur := 0; cur < len(tips); { if nts.Height() == 0 { @@ -1476,6 +1480,13 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know if nts.Height() < tips[cur].Height() { cur++ } else { + // Walk back one block in our synced chain to try to meet the fork's + // height. + forkLengthInHead++ + if forkLengthInHead > int(build.ForkLengthThreshold) { + return nil, ErrForkTooLong + } + // We will be forking away from nts, check that it isn't checkpointed if nts.Key() == chkpt { return nil, ErrForkCheckpoint