diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 25ac3bf15..8f9296c07 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,4 +22,6 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS +- [statesync] \#5516 Check that all heights necessary to rebuild state for a snapshot exist before adding the snapshot to the pool. (@erikgrinaker) + ### BUG FIXES diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index 1e21b0aaf..4b1c75e32 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -94,6 +94,22 @@ func (s *lightClientStateProvider) AppHash(ctx context.Context, height uint64) ( if err != nil { return nil, err } + // We also try to fetch the blocks at height H and H+2, since we need these + // when building the state while restoring the snapshot. This avoids the race + // condition where we try to restore a snapshot before H+2 exists. + // + // FIXME This is a hack, since we can't add new methods to the interface without + // breaking it. We should instead have a Has(ctx, height) method which checks + // that the state provider has access to the necessary data for the height. + // We piggyback on AppHash() since it's called when adding snapshots to the pool. + _, err = s.lc.VerifyLightBlockAtHeight(ctx, int64(height+2), time.Now()) + if err != nil { + return nil, err + } + _, err = s.lc.VerifyLightBlockAtHeight(ctx, int64(height), time.Now()) + if err != nil { + return nil, err + } return header.AppHash, nil }