diff --git a/relayer/relays/beacon/header/header.go b/relayer/relays/beacon/header/header.go index 4434fd4967460..f22a013b12e82 100644 --- a/relayer/relays/beacon/header/header.go +++ b/relayer/relays/beacon/header/header.go @@ -265,7 +265,10 @@ func (h *Header) SyncHeaders(ctx context.Context) error { }).Info("starting to back-fill headers") blockRoot := common.HexToHash(finalizedHeader.FinalizedHeader.ParentRoot.Hex()) - prevSyncAggregate := finalizedHeader.SyncAggregate + prevSyncAggregate, err := h.syncer.GetSyncAggregateForSlot(uint64(finalizedHeader.FinalizedHeader.Slot + 1)) + if err != nil { + return err + } headerUpdate, err := h.SyncHeader(ctx, finalizedHeaderBlockRoot, prevSyncAggregate) if err != nil { diff --git a/relayer/relays/beacon/header/syncer/api.go b/relayer/relays/beacon/header/syncer/api.go index 950b2749a13dd..b615d5bc8ce75 100644 --- a/relayer/relays/beacon/header/syncer/api.go +++ b/relayer/relays/beacon/header/syncer/api.go @@ -26,7 +26,8 @@ type BeaconClientTracker interface { GetHeader(id string) (BeaconHeader, error) GetSyncCommitteePeriodUpdate(from uint64) (SyncCommitteePeriodUpdateResponse, error) GetHeadCheckpoint() (FinalizedCheckpointResponse, error) - GetBeaconBlock(slot uint64) (BeaconBlockResponse, error) + GetBeaconBlock(blockID common.Hash) (BeaconBlockResponse, error) + GetBeaconBlockBySlot(slot uint64) (BeaconBlockResponse, error) GetCurrentForkVersion(slot uint64) (string, error) GetLatestFinalizedUpdate() (LatestFinalisedUpdateResponse, error) } @@ -286,6 +287,41 @@ func (b *BeaconClient) GetBeaconBlock(blockID common.Hash) (BeaconBlockResponse, return response, nil } +func (b *BeaconClient) GetBeaconBlockBySlot(slot uint64) (BeaconBlockResponse, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/eth/v2/beacon/blocks/%d", b.endpoint, slot), nil) + if err != nil { + return BeaconBlockResponse{}, fmt.Errorf("%s: %w", ConstructRequestErrorMessage, err) + } + + req.Header.Set("accept", "application/json") + res, err := b.httpClient.Do(req) + if err != nil { + return BeaconBlockResponse{}, fmt.Errorf("%s: %w", DoHTTPRequestErrorMessage, err) + } + + if res.StatusCode != http.StatusOK { + if res.StatusCode == 404 { + return BeaconBlockResponse{}, ErrNotFound + } + + return BeaconBlockResponse{}, fmt.Errorf("%s: %d", HTTPStatusNotOKErrorMessage, res.StatusCode) + } + + bodyBytes, err := io.ReadAll(res.Body) + if err != nil { + return BeaconBlockResponse{}, fmt.Errorf("%s: %w", ReadResponseBodyErrorMessage, err) + } + + var response BeaconBlockResponse + + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + return BeaconBlockResponse{}, fmt.Errorf("%s: %w", UnmarshalBodyErrorMessage, err) + } + + return response, nil +} + func (b *BeaconClient) GetBeaconBlockRoot(slot uint64) (common.Hash, error) { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/eth/v1/beacon/blocks/%d/root", b.endpoint, slot), nil) if err != nil { diff --git a/relayer/relays/beacon/header/syncer/syncer.go b/relayer/relays/beacon/header/syncer/syncer.go index d7dd8ecb17287..dab351aa24337 100644 --- a/relayer/relays/beacon/header/syncer/syncer.go +++ b/relayer/relays/beacon/header/syncer/syncer.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" ssz "github.com/ferranbt/fastssz" + log "github.com/sirupsen/logrus" "github.com/snowfork/go-substrate-rpc-client/v4/types" "github.com/snowfork/snowbridge/relayer/relays/beacon/header/syncer/scale" ) @@ -106,7 +107,7 @@ func (s *Syncer) GetSyncPeriodsToFetch(checkpointSyncPeriod uint64) ([]uint64, e currentSyncPeriod := s.ComputeSyncPeriodAtSlot(slot) - //The current sync period's next sync committee should be synced too. So even + //The current sync period's next sync committee should be synced too. So even // if the syncing is up to date with the current period, we still need to sync the current // period's next sync committee. if checkpointSyncPeriod == currentSyncPeriod { @@ -289,6 +290,32 @@ func (s *Syncer) GetSyncAggregate(blockRoot common.Hash) (scale.SyncAggregate, e return blockScale.Body.SyncAggregate, nil } +func (s *Syncer) GetSyncAggregateForSlot(slot uint64) (scale.SyncAggregate, error) { + err := ErrNotFound + var block BeaconBlockResponse + tries := 0 + maxSlotsMissed := int(s.SlotsInEpoch) + for errors.Is(err, ErrNotFound) && tries < maxSlotsMissed { + log.WithFields(log.Fields{ + "try_number": tries, + "slot": slot, + }).Info("fetching sync aggregate for slot") + block, err = s.Client.GetBeaconBlockBySlot(slot) + if err != nil && !errors.Is(err, ErrNotFound) { + return scale.SyncAggregate{}, fmt.Errorf("fetch block: %w", err) + } + + tries = tries + 1 + slot = slot + 1 + } + + blockScale, err := block.ToScale() + if err != nil { + return scale.SyncAggregate{}, fmt.Errorf("convert block to scale: %w", err) + } + return blockScale.Body.SyncAggregate, nil +} + func (s *Syncer) ComputeSyncPeriodAtSlot(slot uint64) uint64 { return slot / (s.SlotsInEpoch * s.EpochsPerSyncCommitteePeriod) }