diff --git a/relayer/relays/beacon/header/header.go b/relayer/relays/beacon/header/header.go index 1eee23c388..120609e111 100644 --- a/relayer/relays/beacon/header/header.go +++ b/relayer/relays/beacon/header/header.go @@ -552,12 +552,22 @@ func (h *Header) findLatestCheckPoint(slot uint64) (state.FinalizedHeader, error return beaconState, fmt.Errorf("GetLastFinalizedStateIndex error: %w", err) } startIndex := uint64(lastIndex) + + fmt.Printf("startIndex: %d\n", startIndex) endIndex := startIndex + 1 + iterations := uint64(0) syncCommitteePeriod := h.protocol.Settings.SlotsInEpoch * h.protocol.Settings.EpochsPerSyncCommitteePeriod totalStates := h.protocol.Settings.EpochsPerSyncCommitteePeriod * h.protocol.HeaderRedundancy // Total size of the circular buffer, // https://github.com/paritytech/polkadot-sdk/blob/master/bridges/snowbridge/pallets/ethereum-client/src/lib.rs#L75 for index := startIndex; index != endIndex; index = (index - 1 + totalStates) % totalStates { + iterations++ + // Sanity check, in case the number of loops are more than the states available in the ring buffer + if iterations > totalStates { + log.WithError(err).Debug("unable to find a relevant on-chain header, max iterations reached") + break + } + beaconRoot, err := h.writer.GetFinalizedBeaconRootByIndex(uint32(index)) if err != nil { return beaconState, fmt.Errorf("GetFinalizedBeaconRootByIndex %d, error: %w", index, err) diff --git a/relayer/relays/beacon/header/header_test.go b/relayer/relays/beacon/header/header_test.go index 358cbe9d09..02e2f3764e 100644 --- a/relayer/relays/beacon/header/header_test.go +++ b/relayer/relays/beacon/header/header_test.go @@ -334,9 +334,9 @@ func TestFindLatestCheckPoint(t *testing.T) { EpochsPerSyncCommitteePeriod: 2, DenebForkEpoch: 0, } - maxRedundancy := uint64(2) + maxRedundancy := uint64(8) p := protocol.New(settings, maxRedundancy) - // Total circular array would be 4 * 2 * 2 = 16 + // Total circular array would be 2 * 8 = 16 client := mock.API{} beaconStore := mock.Store{} @@ -349,60 +349,61 @@ func TestFindLatestCheckPoint(t *testing.T) { headerIndex15 := common.HexToHash("0x416f890494e218d3cb32ce1ef3bd08e3acccf6e112b66db544cfcc6295bbdc2a") headerIndex14 := common.HexToHash("0x74c4e67ca468722a7c3af52c5f96f4bbdd60b4d237ae7693863dca308e3c354c") - h := New( - &mock.Writer{ - LastFinalizedState: state.FinalizedHeader{ - BeaconBlockRoot: common.Hash{}, - BeaconSlot: 50, - InitialCheckpointRoot: common.Hash{}, - InitialCheckpointSlot: 0, + mockWriter := &mock.Writer{ + LastFinalizedState: state.FinalizedHeader{ + BeaconBlockRoot: common.Hash{}, + BeaconSlot: 50, + InitialCheckpointRoot: common.Hash{}, + InitialCheckpointSlot: 0, + }, + LastFinalizedStateIndex: 5, + FinalizedBeaconRootByIndex: map[uint32]types.H256{ + 5: types.H256(headerIndex5), + 4: types.H256(headerIndex4), + 3: types.H256(headerIndex3), + 2: types.H256(headerIndex2), + 1: types.H256(headerIndex1), + 0: types.H256(headerIndex0), + 15: types.H256(headerIndex15), + 14: types.H256(headerIndex14), + }, + FinalizedHeaderStateByBlockRoot: map[types.H256]state.FinalizedHeader{ + types.H256(headerIndex5): { + BeaconBlockRoot: headerIndex5, + BeaconSlot: 50, + }, + types.H256(headerIndex4): { + BeaconBlockRoot: headerIndex4, + BeaconSlot: 46, }, - LastFinalizedStateIndex: 5, - FinalizedBeaconRootByIndex: map[uint32]types.H256{ - 5: types.H256(headerIndex5), - 4: types.H256(headerIndex4), - 3: types.H256(headerIndex3), - 2: types.H256(headerIndex2), - 1: types.H256(headerIndex1), - 0: types.H256(headerIndex0), - 15: types.H256(headerIndex15), - 14: types.H256(headerIndex14), + types.H256(headerIndex3): { + BeaconBlockRoot: headerIndex3, + BeaconSlot: 42, }, - FinalizedHeaderStateByBlockRoot: map[types.H256]state.FinalizedHeader{ - types.H256(headerIndex5): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex5, - BeaconSlot: 50, - }, - types.H256(headerIndex4): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex4, - BeaconSlot: 46, - }, - types.H256(headerIndex3): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex3, - BeaconSlot: 42, - }, - types.H256(headerIndex2): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex2, - BeaconSlot: 38, - }, - types.H256(headerIndex1): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex1, - BeaconSlot: 30, - }, - types.H256(headerIndex0): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex0, - BeaconSlot: 32, - }, - types.H256(headerIndex15): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex15, - BeaconSlot: 20, - }, - types.H256(headerIndex14): state.FinalizedHeader{ - BeaconBlockRoot: headerIndex14, - BeaconSlot: 18, - }, + types.H256(headerIndex2): { + BeaconBlockRoot: headerIndex2, + BeaconSlot: 38, + }, + types.H256(headerIndex1): { + BeaconBlockRoot: headerIndex1, + BeaconSlot: 30, + }, + types.H256(headerIndex0): { + BeaconBlockRoot: headerIndex0, + BeaconSlot: 32, + }, + types.H256(headerIndex15): { + BeaconBlockRoot: headerIndex15, + BeaconSlot: 20, + }, + types.H256(headerIndex14): { + BeaconBlockRoot: headerIndex14, + BeaconSlot: 18, }, }, + } + h := New( + mockWriter, &client, settings, &beaconStore, @@ -425,4 +426,9 @@ func TestFindLatestCheckPoint(t *testing.T) { assert.NoError(t, err) assert.Equal(t, headerIndex4, header.BeaconBlockRoot) assert.Equal(t, uint64(46), header.BeaconSlot) + + // Check last finalized state index outside of the circular array does not cause an infinite loop + mockWriter.LastFinalizedStateIndex = types.U32((maxRedundancy * settings.EpochsPerSyncCommitteePeriod) + 50) + header, err = h.findLatestCheckPoint(40) + assert.Error(t, err) }