From 6a264f4189cde6427fed7efa405a1a34e3588adb Mon Sep 17 00:00:00 2001 From: Bastin Date: Tue, 28 Jan 2025 17:47:49 +0100 Subject: [PATCH] bundle handlers test --- .../rpc/eth/light-client/handlers_test.go | 1930 ++++++++--------- 1 file changed, 892 insertions(+), 1038 deletions(-) diff --git a/beacon-chain/rpc/eth/light-client/handlers_test.go b/beacon-chain/rpc/eth/light-client/handlers_test.go index cd210617bd80..422dfcba9d40 100644 --- a/beacon-chain/rpc/eth/light-client/handlers_test.go +++ b/beacon-chain/rpc/eth/light-client/handlers_test.go @@ -453,63 +453,7 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) { }) } -// GetLightClientByRange tests - -func TestLightClientHandler_GetLightClientUpdatesByRangeAltair(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, - }) - defer resetFn() - - helpers.ClearCache() - ctx := context.Background() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - params.OverrideBeaconConfig(config) - - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) - - db := dbtesting.SetupDB(t) - - updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - - update, err := createUpdate(t, version.Altair) - require.NoError(t, err) - err = db.SaveLightClientUpdate(ctx, updatePeriod, update) - require.NoError(t, err) - - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.GetLightClientUpdatesByRange(writer, request) - - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) - require.Equal(t, "altair", resp.Updates[0].Version) - updateJson, err := structs.LightClientUpdateFromConsensus(update) - require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[0].Data) -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeCapella(t *testing.T) { +func TestLightClientHandler_GetLightClientByRange(t *testing.T) { resetFn := features.InitWithReset(&features.Flags{ EnableLightClient: true, }) @@ -517,635 +461,425 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeCapella(t *testing.T) { helpers.ClearCache() ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.CapellaForkEpoch = 1 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) - - db := dbtesting.SetupDB(t) - - updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - - update, err := createUpdate(t, version.Capella) - require.NoError(t, err) - - err = db.SaveLightClientUpdate(ctx, updatePeriod, update) - require.NoError(t, err) - - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.GetLightClientUpdatesByRange(writer, request) - - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) - require.Equal(t, "capella", resp.Updates[0].Version) - updateJson, err := structs.LightClientUpdateFromConsensus(update) - require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[0].Data) -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeDeneb(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, - }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() params.SetupTestConfigCleanup(t) config := params.BeaconConfig() + config.EpochsPerSyncCommitteePeriod = 1 config.AltairForkEpoch = 0 config.CapellaForkEpoch = 1 config.DenebForkEpoch = 2 params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - - st, err := util.NewBeaconStateDeneb() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) - - db := dbtesting.SetupDB(t) - - updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - - update, err := createUpdate(t, version.Deneb) - require.NoError(t, err) - err = db.SaveLightClientUpdate(ctx, updatePeriod, update) - require.NoError(t, err) - - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.GetLightClientUpdatesByRange(writer, request) - - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) - require.Equal(t, "deneb", resp.Updates[0].Version) - updateJson, err := structs.LightClientUpdateFromConsensus(update) - require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[0].Data) -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleAltair(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, - }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("altair", func(t *testing.T) { + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - db := dbtesting.SetupDB(t) + db := dbtesting.SetupDB(t) - updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - updates := make([]interfaces.LightClientUpdate, 0) - for i := 1; i <= 2; i++ { update, err := createUpdate(t, version.Altair) require.NoError(t, err) - updates = append(updates, update) - } - - for _, update := range updates { - err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - updatePeriod++ - } - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - require.Equal(t, "altair", resp.Updates[i].Version) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "altair", resp.Updates[0].Version) updateJson, err := structs.LightClientUpdateFromConsensus(update) require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[i].Data) - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleCapella(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, + require.DeepEqual(t, updateJson, resp.Updates[0].Data) }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.CapellaForkEpoch = 1 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("capella", func(t *testing.T) { + slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - db := dbtesting.SetupDB(t) + db := dbtesting.SetupDB(t) - updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - updates := make([]interfaces.LightClientUpdate, 0) - for i := 0; i < 2; i++ { update, err := createUpdate(t, version.Capella) require.NoError(t, err) - updates = append(updates, update) - } - for _, update := range updates { - err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - updatePeriod++ - } - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - require.Equal(t, "capella", resp.Updates[i].Version) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "capella", resp.Updates[0].Version) updateJson, err := structs.LightClientUpdateFromConsensus(update) require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[i].Data) - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleDeneb(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, + require.DeepEqual(t, updateJson, resp.Updates[0].Data) }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.CapellaForkEpoch = 1 - config.DenebForkEpoch = 2 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("deneb", func(t *testing.T) { + slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateDeneb() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - db := dbtesting.SetupDB(t) + db := dbtesting.SetupDB(t) - updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := uint64(slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch))) - updates := make([]interfaces.LightClientUpdate, 0) - for i := 0; i < 2; i++ { update, err := createUpdate(t, version.Deneb) require.NoError(t, err) - updates = append(updates, update) - } - - for _, update := range updates { - err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + err = db.SaveLightClientUpdate(ctx, updatePeriod, update) require.NoError(t, err) - updatePeriod++ - } - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=1&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - require.Equal(t, "deneb", resp.Updates[i].Version) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "deneb", resp.Updates[0].Version) updateJson, err := structs.LightClientUpdateFromConsensus(update) require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[i].Data) - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleForksAltairCapella(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, + require.DeepEqual(t, updateJson, resp.Updates[0].Data) }) - defer resetFn() - - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.CapellaForkEpoch = 1 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - slotAltair := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slotCapella.Add(1) - err = st.SetSlot(headSlot) - require.NoError(t, err) + t.Run("altair Multiple", func(t *testing.T) { + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - db := dbtesting.SetupDB(t) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods + err = st.SetSlot(headSlot) + require.NoError(t, err) - updates := make([]interfaces.LightClientUpdate, 2) + db := dbtesting.SetupDB(t) - updatePeriod := slotAltair.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - updates[0], err = createUpdate(t, version.Altair) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 0) + for i := 1; i <= 2; i++ { + update, err := createUpdate(t, version.Altair) + require.NoError(t, err) + updates = append(updates, update) + } - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) - require.NoError(t, err) + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ + } - updatePeriod = slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - - updates[1], err = createUpdate(t, version.Capella) - require.NoError(t, err) - - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) - require.NoError(t, err) - - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := 0 - url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - if i < 1 { + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { require.Equal(t, "altair", resp.Updates[i].Version) - } else { - require.Equal(t, "capella", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) } - updateJson, err := structs.LightClientUpdateFromConsensus(update) + }) + + t.Run("capella Multiple", func(t *testing.T) { + slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 2 periods + err = st.SetSlot(headSlot) require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[i].Data) - } -} -func TestLightClientHandler_GetLightClientUpdatesByRangeMultipleForksCapellaDeneb(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, - }) - defer resetFn() + db := dbtesting.SetupDB(t) - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.CapellaForkEpoch = 1 - config.DenebForkEpoch = 2 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slotDeneb := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slotDeneb.Add(1) - err = st.SetSlot(headSlot) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 0) + for i := 0; i < 2; i++ { + update, err := createUpdate(t, version.Capella) + require.NoError(t, err) + updates = append(updates, update) + } + + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ + } - db := dbtesting.SetupDB(t) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - updates := make([]interfaces.LightClientUpdate, 2) + s.GetLightClientUpdatesByRange(writer, request) - updatePeriod := slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + require.Equal(t, "capella", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } + }) - updates[0], err = createUpdate(t, version.Capella) - require.NoError(t, err) + t.Run("deneb Multiple", func(t *testing.T) { + slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(2 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) + err = st.SetSlot(headSlot) + require.NoError(t, err) - updatePeriod = slotDeneb.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + db := dbtesting.SetupDB(t) - updates[1], err = createUpdate(t, version.Deneb) - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 0) + for i := 0; i < 2; i++ { + update, err := createUpdate(t, version.Deneb) + require.NoError(t, err) + updates = append(updates, update) + } - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := 1 - url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + for _, update := range updates { + err := db.SaveLightClientUpdate(ctx, uint64(updatePeriod), update) + require.NoError(t, err) + updatePeriod++ + } + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := slot.Sub(1).Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - if i < 1 { - require.Equal(t, "capella", resp.Updates[i].Version) - } else { + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { require.Equal(t, "deneb", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) } - updateJson, err := structs.LightClientUpdateFromConsensus(update) - require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[i].Data) - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeCountBiggerThanLimit(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.EpochsPerSyncCommitteePeriod = 1 - config.MaxRequestLightClientUpdates = 2 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("multiple forks - altair, capella", func(t *testing.T) { + slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + slotAltair := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slotCapella.Add(1) + err = st.SetSlot(headSlot) + require.NoError(t, err) - db := dbtesting.SetupDB(t) + db := dbtesting.SetupDB(t) - updates := make([]interfaces.LightClientUpdate, 3) + updates := make([]interfaces.LightClientUpdate, 2) - updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := slotAltair.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - for i := 0; i < 3; i++ { + updates[0], err = createUpdate(t, version.Altair) + require.NoError(t, err) - updates[i], err = createUpdate(t, version.Altair) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) require.NoError(t, err) - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + updatePeriod = slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + + updates[1], err = createUpdate(t, version.Capella) require.NoError(t, err) - updatePeriod++ - } + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) + require.NoError(t, err) - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := 0 - url := fmt.Sprintf("http://foo.com/?count=4&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientUpdatesByRange(writer, request) + s.GetLightClientUpdatesByRange(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - if i < 2 { - require.Equal(t, "altair", resp.Updates[i].Version) + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 1 { + require.Equal(t, "altair", resp.Updates[i].Version) + } else { + require.Equal(t, "capella", resp.Updates[i].Version) + } updateJson, err := structs.LightClientUpdateFromConsensus(update) require.NoError(t, err) require.DeepEqual(t, updateJson, resp.Updates[i].Data) } - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeCountBiggerThanMax(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.EpochsPerSyncCommitteePeriod = 1 - config.MaxRequestLightClientUpdates = 2 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("multiple forks - capella, deneb", func(t *testing.T) { + slotDeneb := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + slotCapella := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slotDeneb.Add(1) + err = st.SetSlot(headSlot) + require.NoError(t, err) - db := dbtesting.SetupDB(t) + db := dbtesting.SetupDB(t) - updates := make([]interfaces.LightClientUpdate, 3) + updates := make([]interfaces.LightClientUpdate, 2) - updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) + updatePeriod := slotCapella.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - for i := 0; i < 3; i++ { - updates[i], err = createUpdate(t, version.Altair) + updates[0], err = createUpdate(t, version.Capella) require.NoError(t, err) - err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[0]) require.NoError(t, err) - updatePeriod++ - } + updatePeriod = slotDeneb.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - mockChainService := &mock.ChainService{State: st} - s := &Server{ - HeadFetcher: mockChainService, - BeaconDB: db, - } - startPeriod := 0 - url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + updates[1], err = createUpdate(t, version.Deneb) + require.NoError(t, err) - s.GetLightClientUpdatesByRange(writer, request) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[1]) + require.NoError(t, err) - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 2, len(resp.Updates)) - for i, update := range updates { - if i < 2 { - require.Equal(t, "altair", resp.Updates[i].Version) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 1 + url := fmt.Sprintf("http://foo.com/?count=100&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientUpdatesByRange(writer, request) + + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 1 { + require.Equal(t, "capella", resp.Updates[i].Version) + } else { + require.Equal(t, "deneb", resp.Updates[i].Version) + } updateJson, err := structs.LightClientUpdateFromConsensus(update) require.NoError(t, err) require.DeepEqual(t, updateJson, resp.Updates[i].Data) } - } -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeStartPeriodBeforeAltair(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, - }) - defer resetFn() - - helpers.ClearCache() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - - db := dbtesting.SetupDB(t) - - s := &Server{ - BeaconDB: db, - } - startPeriod := 0 - url := fmt.Sprintf("http://foo.com/?count=2&start_period=%d", startPeriod) - request := httptest.NewRequest("GET", url, nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.GetLightClientUpdatesByRange(writer, request) - - require.Equal(t, http.StatusOK, writer.Code) - var resp structs.LightClientUpdatesByRangeResponse - err := json.Unmarshal(writer.Body.Bytes(), &resp.Updates) - require.NoError(t, err) - require.Equal(t, 0, len(resp.Updates)) -} - -func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 0 - config.EpochsPerSyncCommitteePeriod = 1 - params.OverrideBeaconConfig(config) - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("count bigger than limit", func(t *testing.T) { + config.MaxRequestLightClientUpdates = 2 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods - err = st.SetSlot(headSlot) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) + require.NoError(t, err) - t.Run("missing update in the middle", func(t *testing.T) { db := dbtesting.SetupDB(t) updates := make([]interfaces.LightClientUpdate, 3) @@ -1153,10 +887,6 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testin updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) for i := 0; i < 3; i++ { - if i == 1 { // skip this update - updatePeriod++ - continue - } updates[i], err = createUpdate(t, version.Altair) require.NoError(t, err) @@ -1173,7 +903,7 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testin BeaconDB: db, } startPeriod := 0 - url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + url := fmt.Sprintf("http://foo.com/?count=4&start_period=%d", startPeriod) request := httptest.NewRequest("GET", url, nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -1184,14 +914,28 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testin var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - require.Equal(t, 1, len(resp.Updates)) - require.Equal(t, "altair", resp.Updates[0].Version) - updateJson, err := structs.LightClientUpdateFromConsensus(updates[0]) - require.NoError(t, err) - require.DeepEqual(t, updateJson, resp.Updates[0].Data) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 2 { + require.Equal(t, "altair", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } + } }) - t.Run("missing update at the beginning", func(t *testing.T) { + t.Run("count bigger than max", func(t *testing.T) { + config.MaxRequestLightClientUpdates = 2 + params.OverrideBeaconConfig(config) + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) + require.NoError(t, err) + db := dbtesting.SetupDB(t) updates := make([]interfaces.LightClientUpdate, 3) @@ -1199,11 +943,6 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testin updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) for i := 0; i < 3; i++ { - if i == 0 { // skip this update - updatePeriod++ - continue - } - updates[i], err = createUpdate(t, version.Altair) require.NoError(t, err) @@ -1230,129 +969,138 @@ func TestLightClientHandler_GetLightClientUpdatesByRangeMissingUpdates(t *testin var resp structs.LightClientUpdatesByRangeResponse err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) require.NoError(t, err) - require.Equal(t, 0, len(resp.Updates)) + require.Equal(t, 2, len(resp.Updates)) + for i, update := range updates { + if i < 2 { + require.Equal(t, "altair", resp.Updates[i].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(update) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[i].Data) + } + } }) -} + t.Run("start period before altair", func(t *testing.T) { + db := dbtesting.SetupDB(t) -func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, + s := &Server{ + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=128&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientUpdatesByRange(writer, request) + + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err := json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 0, len(resp.Updates)) }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - config := params.BeaconConfig() - slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("missing updates", func(t *testing.T) { + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + headSlot := slot.Add(4 * uint64(config.SlotsPerEpoch) * uint64(config.EpochsPerSyncCommitteePeriod)) // 4 periods + err = st.SetSlot(headSlot) + require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ - Epoch: config.AltairForkEpoch - 10, - Root: make([]byte, 32), - })) + t.Run("missing update in the middle", func(t *testing.T) { + db := dbtesting.SetupDB(t) - parent := util.NewBeaconBlockAltair() - parent.Block.Slot = slot.Sub(1) + updates := make([]interfaces.LightClientUpdate, 3) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + for i := 0; i < 3; i++ { + if i == 1 { // skip this update + updatePeriod++ + continue + } - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) - st, err := util.NewBeaconStateAltair() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + updatePeriod++ + } - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - block := util.NewBeaconBlockAltair() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + s.GetLightClientUpdatesByRange(writer, request) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 1, len(resp.Updates)) + require.Equal(t, "altair", resp.Updates[0].Version) + updateJson, err := structs.LightClientUpdateFromConsensus(updates[0]) + require.NoError(t, err) + require.DeepEqual(t, updateJson, resp.Updates[0].Data) + }) - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + t.Run("missing update at the beginning", func(t *testing.T) { + db := dbtesting.SetupDB(t) - h, err := signedBlock.Header() - require.NoError(t, err) + updates := make([]interfaces.LightClientUpdate, 3) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + updatePeriod := slot.Div(uint64(config.EpochsPerSyncCommitteePeriod)).Div(uint64(config.SlotsPerEpoch)) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + for i := 0; i < 3; i++ { + if i == 0 { // skip this update + updatePeriod++ + continue + } - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + updates[i], err = createUpdate(t, version.Altair) + require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ - root: true, - }} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, - } - request := httptest.NewRequest("GET", "http://foo.com", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + err = db.SaveLightClientUpdate(ctx, uint64(updatePeriod), updates[i]) + require.NoError(t, err) - s.GetLightClientFinalityUpdate(writer, request) + updatePeriod++ + } - require.Equal(t, http.StatusOK, writer.Code) - var resp *structs.LightClientUpdateResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp) - require.NoError(t, err) - var respHeader structs.LightClientHeader - err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, "altair", resp.Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp.Data) + mockChainService := &mock.ChainService{State: st} + s := &Server{ + HeadFetcher: mockChainService, + BeaconDB: db, + } + startPeriod := 0 + url := fmt.Sprintf("http://foo.com/?count=10&start_period=%d", startPeriod) + request := httptest.NewRequest("GET", url, nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientUpdatesByRange(writer, request) + + require.Equal(t, http.StatusOK, writer.Code) + var resp structs.LightClientUpdatesByRangeResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp.Updates) + require.NoError(t, err) + require.Equal(t, 0, len(resp.Updates)) + }) + }) } -func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) { +func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) { resetFn := features.InitWithReset(&features.Flags{ EnableLightClient: true, }) @@ -1455,7 +1203,7 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.GetLightClientOptimisticUpdate(writer, request) + s.GetLightClientFinalityUpdate(writer, request) require.Equal(t, http.StatusOK, writer.Code) var resp *structs.LightClientUpdateResponse @@ -1469,453 +1217,559 @@ func TestLightClientHandler_GetLightClientOptimisticUpdateAltair(t *testing.T) { require.NotNil(t, resp.Data) } -func TestLightClientHandler_GetLightClientOptimisticUpdateCapella(t *testing.T) { +func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) { resetFn := features.InitWithReset(&features.Flags{ EnableLightClient: true, }) defer resetFn() - helpers.ClearCache() - ctx := context.Background() config := params.BeaconConfig() - slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) + t.Run("altair", func(t *testing.T) { + ctx := context.Background() + slot := primitives.Slot(config.AltairForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ - Epoch: config.AltairForkEpoch - 10, - Root: make([]byte, 32), - })) + attestedState, err := util.NewBeaconStateAltair() + require.NoError(t, err) + err = attestedState.SetSlot(slot.Sub(1)) + require.NoError(t, err) - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ + Epoch: config.AltairForkEpoch - 10, + Root: make([]byte, 32), + })) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + parent := util.NewBeaconBlockAltair() + parent.Block.Slot = slot.Sub(1) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + parentHeader, err := signedParent.Header() + require.NoError(t, err) + attestedHeader := parentHeader.Header - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(t, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(t, err) - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + st, err := util.NewBeaconStateAltair() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(t, err) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + block := util.NewBeaconBlockAltair() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + for i := uint64(0); i < config.SyncCommitteeSize; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } - h, err := signedBlock.Header() - require.NoError(t, err) + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + h, err := signedBlock.Header() + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + err = st.SetLatestBlockHeader(h.Header) + require.NoError(t, err) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ - root: true, - }} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, - } - request := httptest.NewRequest("GET", "http://foo.com", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + root, err := block.Block.HashTreeRoot() + require.NoError(t, err) - s.GetLightClientOptimisticUpdate(writer, request) + mockBlocker := &testutil.MockBlocker{ + RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ + parentRoot: signedParent, + root: signedBlock, + }, + SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ + slot.Sub(1): signedParent, + slot: signedBlock, + }, + } + mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ + root: true, + }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + s := &Server{ + Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ + slot.Sub(1): attestedState, + slot: st, + }}, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, + } + request := httptest.NewRequest("GET", "http://foo.com", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - require.Equal(t, http.StatusOK, writer.Code) - var resp *structs.LightClientUpdateResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp) - require.NoError(t, err) - var respHeader structs.LightClientHeaderCapella - err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, "capella", resp.Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp.Data) -} + s.GetLightClientOptimisticUpdate(writer, request) -func TestLightClientHandler_GetLightClientOptimisticUpdateDeneb(t *testing.T) { - resetFn := features.InitWithReset(&features.Flags{ - EnableLightClient: true, + require.Equal(t, http.StatusOK, writer.Code) + var resp *structs.LightClientUpdateResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp) + require.NoError(t, err) + var respHeader structs.LightClientHeader + err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) + require.NoError(t, err) + require.Equal(t, "altair", resp.Version) + require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) + require.NotNil(t, resp.Data) }) - defer resetFn() - helpers.ClearCache() - ctx := context.Background() - config := params.BeaconConfig() - slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + t.Run("capella", func(t *testing.T) { + ctx := context.Background() + slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateDeneb() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) + attestedState, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = attestedState.SetSlot(slot.Sub(1)) + require.NoError(t, err) - require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ - Epoch: config.AltairForkEpoch - 10, - Root: make([]byte, 32), - })) + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ + Epoch: config.AltairForkEpoch - 10, + Root: make([]byte, 32), + })) - parent := util.NewBeaconBlockDeneb() - parent.Block.Slot = slot.Sub(1) + parent := util.NewBeaconBlockCapella() + parent.Block.Slot = slot.Sub(1) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + parentHeader, err := signedParent.Header() + require.NoError(t, err) + attestedHeader := parentHeader.Header - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(t, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - st, err := util.NewBeaconStateDeneb() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + st, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(t, err) - block := util.NewBeaconBlockDeneb() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + block := util.NewBeaconBlockCapella() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + for i := uint64(0); i < config.SyncCommitteeSize; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - h, err := signedBlock.Header() - require.NoError(t, err) + h, err := signedBlock.Header() + require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + err = st.SetLatestBlockHeader(h.Header) + require.NoError(t, err) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + root, err := block.Block.HashTreeRoot() + require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ - root: true, - }} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, - } - request := httptest.NewRequest("GET", "http://foo.com", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + mockBlocker := &testutil.MockBlocker{ + RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ + parentRoot: signedParent, + root: signedBlock, + }, + SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ + slot.Sub(1): signedParent, + slot: signedBlock, + }, + } + mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ + root: true, + }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + s := &Server{ + Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ + slot.Sub(1): attestedState, + slot: st, + }}, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, + } + request := httptest.NewRequest("GET", "http://foo.com", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetLightClientOptimisticUpdate(writer, request) + s.GetLightClientOptimisticUpdate(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - var resp *structs.LightClientUpdateResponse - err = json.Unmarshal(writer.Body.Bytes(), &resp) - require.NoError(t, err) - var respHeader structs.LightClientHeaderDeneb - err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) - require.NoError(t, err) - require.Equal(t, "deneb", resp.Version) - require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) - require.NotNil(t, resp.Data) + require.Equal(t, http.StatusOK, writer.Code) + var resp *structs.LightClientUpdateResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp) + require.NoError(t, err) + var respHeader structs.LightClientHeaderCapella + err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) + require.NoError(t, err) + require.Equal(t, "capella", resp.Version) + require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) + require.NotNil(t, resp.Data) + }) + + t.Run("deneb", func(t *testing.T) { + ctx := context.Background() + slot := primitives.Slot(config.DenebForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + + attestedState, err := util.NewBeaconStateDeneb() + require.NoError(t, err) + err = attestedState.SetSlot(slot.Sub(1)) + require.NoError(t, err) + + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ + Epoch: config.AltairForkEpoch - 10, + Root: make([]byte, 32), + })) + + parent := util.NewBeaconBlockDeneb() + parent.Block.Slot = slot.Sub(1) + + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) + + parentHeader, err := signedParent.Header() + require.NoError(t, err) + attestedHeader := parentHeader.Header + + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(t, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(t, err) + + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) + + st, err := util.NewBeaconStateDeneb() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) + + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(t, err) + + block := util.NewBeaconBlockDeneb() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] + + for i := uint64(0); i < config.SyncCommitteeSize; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } + + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) + + h, err := signedBlock.Header() + require.NoError(t, err) + + err = st.SetLatestBlockHeader(h.Header) + require.NoError(t, err) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err) + + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) + + root, err := block.Block.HashTreeRoot() + require.NoError(t, err) + + mockBlocker := &testutil.MockBlocker{ + RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ + parentRoot: signedParent, + root: signedBlock, + }, + SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ + slot.Sub(1): signedParent, + slot: signedBlock, + }, + } + mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ + root: true, + }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + s := &Server{ + Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ + slot.Sub(1): attestedState, + slot: st, + }}, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, + } + request := httptest.NewRequest("GET", "http://foo.com", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetLightClientOptimisticUpdate(writer, request) + + require.Equal(t, http.StatusOK, writer.Code) + var resp *structs.LightClientUpdateResponse + err = json.Unmarshal(writer.Body.Bytes(), &resp) + require.NoError(t, err) + var respHeader structs.LightClientHeaderDeneb + err = json.Unmarshal(resp.Data.AttestedHeader, &respHeader) + require.NoError(t, err) + require.Equal(t, "deneb", resp.Version) + require.Equal(t, hexutil.Encode(attestedHeader.BodyRoot), respHeader.Beacon.BodyRoot) + require.NotNil(t, resp.Data) + }) } func TestLightClientHandler_GetLightClientEventBlock(t *testing.T) { helpers.ClearCache() - ctx := context.Background() config := params.BeaconConfig() - slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - attestedState, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) + t.Run("head suitable", func(t *testing.T) { + ctx := context.Background() + slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ - Epoch: config.AltairForkEpoch - 10, - Root: make([]byte, 32), - })) + attestedState, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = attestedState.SetSlot(slot.Sub(1)) + require.NoError(t, err) - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ + Epoch: config.AltairForkEpoch - 10, + Root: make([]byte, 32), + })) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + parent := util.NewBeaconBlockCapella() + parent.Block.Slot = slot.Sub(1) - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + parentHeader, err := signedParent.Header() + require.NoError(t, err) + attestedHeader := parentHeader.Header - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(t, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(t, err) - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + st, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(t, err) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + block := util.NewBeaconBlockCapella() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + for i := uint64(0); i < config.SyncCommitteeSize; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } - h, err := signedBlock.Header() - require.NoError(t, err) + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + h, err := signedBlock.Header() + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + err = st.SetLatestBlockHeader(h.Header) + require.NoError(t, err) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ - root: true, - }} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, - } + root, err := block.Block.HashTreeRoot() + require.NoError(t, err) - minSignaturesRequired := uint64(100) - eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired) + mockBlocker := &testutil.MockBlocker{ + RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ + parentRoot: signedParent, + root: signedBlock, + }, + SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ + slot.Sub(1): signedParent, + slot: signedBlock, + }, + } + mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ + root: true, + }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + s := &Server{ + Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ + slot.Sub(1): attestedState, + slot: st, + }}, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, + } - require.NoError(t, err) - require.NotNil(t, eventBlock) - require.Equal(t, slot, eventBlock.Block().Slot()) - syncAggregate, err := eventBlock.Block().Body().SyncAggregate() - require.NoError(t, err) - require.Equal(t, true, syncAggregate.SyncCommitteeBits.Count() >= minSignaturesRequired) -} + minSignaturesRequired := uint64(100) + eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired) -func TestLightClientHandler_GetLightClientEventBlock_NeedFetchParent(t *testing.T) { - helpers.ClearCache() - ctx := context.Background() - config := params.BeaconConfig() - slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) + require.NoError(t, err) + require.NotNil(t, eventBlock) + require.Equal(t, slot, eventBlock.Block().Slot()) + syncAggregate, err := eventBlock.Block().Body().SyncAggregate() + require.NoError(t, err) + require.Equal(t, true, syncAggregate.SyncCommitteeBits.Count() >= minSignaturesRequired) + }) - attestedState, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = attestedState.SetSlot(slot.Sub(1)) - require.NoError(t, err) + t.Run("head not suitable, parent suitable", func(t *testing.T) { + ctx := context.Background() + slot := primitives.Slot(config.CapellaForkEpoch * primitives.Epoch(config.SlotsPerEpoch)).Add(1) - require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ - Epoch: config.AltairForkEpoch - 10, - Root: make([]byte, 32), - })) + attestedState, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = attestedState.SetSlot(slot.Sub(1)) + require.NoError(t, err) - parent := util.NewBeaconBlockCapella() - parent.Block.Slot = slot.Sub(1) - for i := uint64(0); i < config.SyncCommitteeSize; i++ { - parent.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + require.NoError(t, attestedState.SetFinalizedCheckpoint(&pb.Checkpoint{ + Epoch: config.AltairForkEpoch - 10, + Root: make([]byte, 32), + })) - signedParent, err := blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + parent := util.NewBeaconBlockCapella() + parent.Block.Slot = slot.Sub(1) + for i := uint64(0); i < config.SyncCommitteeSize; i++ { + parent.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } - parentHeader, err := signedParent.Header() - require.NoError(t, err) - attestedHeader := parentHeader.Header + signedParent, err := blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - err = attestedState.SetLatestBlockHeader(attestedHeader) - require.NoError(t, err) - attestedStateRoot, err := attestedState.HashTreeRoot(ctx) - require.NoError(t, err) + parentHeader, err := signedParent.Header() + require.NoError(t, err) + attestedHeader := parentHeader.Header - // get a new signed block so the root is updated with the new state root - parent.Block.StateRoot = attestedStateRoot[:] - signedParent, err = blocks.NewSignedBeaconBlock(parent) - require.NoError(t, err) + err = attestedState.SetLatestBlockHeader(attestedHeader) + require.NoError(t, err) + attestedStateRoot, err := attestedState.HashTreeRoot(ctx) + require.NoError(t, err) - st, err := util.NewBeaconStateCapella() - require.NoError(t, err) - err = st.SetSlot(slot) - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + parent.Block.StateRoot = attestedStateRoot[:] + signedParent, err = blocks.NewSignedBeaconBlock(parent) + require.NoError(t, err) - parentRoot, err := signedParent.Block().HashTreeRoot() - require.NoError(t, err) + st, err := util.NewBeaconStateCapella() + require.NoError(t, err) + err = st.SetSlot(slot) + require.NoError(t, err) - block := util.NewBeaconBlockCapella() - block.Block.Slot = slot - block.Block.ParentRoot = parentRoot[:] + parentRoot, err := signedParent.Block().HashTreeRoot() + require.NoError(t, err) - for i := uint64(0); i < 10; i++ { - block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) - } + block := util.NewBeaconBlockCapella() + block.Block.Slot = slot + block.Block.ParentRoot = parentRoot[:] - signedBlock, err := blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + for i := uint64(0); i < 10; i++ { + block.Block.Body.SyncAggregate.SyncCommitteeBits.SetBitAt(i, true) + } - h, err := signedBlock.Header() - require.NoError(t, err) + signedBlock, err := blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - err = st.SetLatestBlockHeader(h.Header) - require.NoError(t, err) - stateRoot, err := st.HashTreeRoot(ctx) - require.NoError(t, err) + h, err := signedBlock.Header() + require.NoError(t, err) - // get a new signed block so the root is updated with the new state root - block.Block.StateRoot = stateRoot[:] - signedBlock, err = blocks.NewSignedBeaconBlock(block) - require.NoError(t, err) + err = st.SetLatestBlockHeader(h.Header) + require.NoError(t, err) + stateRoot, err := st.HashTreeRoot(ctx) + require.NoError(t, err) - root, err := block.Block.HashTreeRoot() - require.NoError(t, err) + // get a new signed block so the root is updated with the new state root + block.Block.StateRoot = stateRoot[:] + signedBlock, err = blocks.NewSignedBeaconBlock(block) + require.NoError(t, err) - mockBlocker := &testutil.MockBlocker{ - RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ - parentRoot: signedParent, - root: signedBlock, - }, - SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ - slot.Sub(1): signedParent, - slot: signedBlock, - }, - } - mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ - root: true, - }} - mockChainInfoFetcher := &mock.ChainService{Slot: &slot} - s := &Server{ - Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ - slot.Sub(1): attestedState, - slot: st, - }}, - Blocker: mockBlocker, - HeadFetcher: mockChainService, - ChainInfoFetcher: mockChainInfoFetcher, - } + root, err := block.Block.HashTreeRoot() + require.NoError(t, err) - minSignaturesRequired := uint64(100) - eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired) + mockBlocker := &testutil.MockBlocker{ + RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{ + parentRoot: signedParent, + root: signedBlock, + }, + SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ + slot.Sub(1): signedParent, + slot: signedBlock, + }, + } + mockChainService := &mock.ChainService{Optimistic: true, Slot: &slot, State: st, FinalizedRoots: map[[32]byte]bool{ + root: true, + }} + mockChainInfoFetcher := &mock.ChainService{Slot: &slot} + s := &Server{ + Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{ + slot.Sub(1): attestedState, + slot: st, + }}, + Blocker: mockBlocker, + HeadFetcher: mockChainService, + ChainInfoFetcher: mockChainInfoFetcher, + } - require.NoError(t, err) - require.NotNil(t, eventBlock) - syncAggregate, err := eventBlock.Block().Body().SyncAggregate() - require.NoError(t, err) - require.Equal(t, true, syncAggregate.SyncCommitteeBits.Count() >= minSignaturesRequired) - require.Equal(t, slot-1, eventBlock.Block().Slot()) + minSignaturesRequired := uint64(100) + eventBlock, err := s.suitableBlock(ctx, minSignaturesRequired) + + require.NoError(t, err) + require.NotNil(t, eventBlock) + syncAggregate, err := eventBlock.Block().Body().SyncAggregate() + require.NoError(t, err) + require.Equal(t, true, syncAggregate.SyncCommitteeBits.Count() >= minSignaturesRequired) + require.Equal(t, slot-1, eventBlock.Block().Slot()) + }) } func createUpdate(t *testing.T, v int) (interfaces.LightClientUpdate, error) {