diff --git a/beacon-chain/rpc/eth/rewards/BUILD.bazel b/beacon-chain/rpc/eth/rewards/BUILD.bazel index f9c9d2f76..a41bb4e7d 100644 --- a/beacon-chain/rpc/eth/rewards/BUILD.bazel +++ b/beacon-chain/rpc/eth/rewards/BUILD.bazel @@ -15,7 +15,9 @@ go_library( "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/epoch/precompute:go_default_library", + "//beacon-chain/core/transition:go_default_library", "//beacon-chain/core/validators:go_default_library", + "//beacon-chain/db:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/lookup:go_default_library", "//beacon-chain/state:go_default_library", @@ -35,7 +37,10 @@ go_library( go_test( name = "go_default_test", - srcs = ["handlers_test.go"], + srcs = [ + "handlers_test.go", + "service_test.go", + ], embed = [":go_default_library"], deps = [ "//api/server/structs:go_default_library", @@ -43,6 +48,8 @@ go_test( "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/signing:go_default_library", + "//beacon-chain/core/transition:go_default_library", + "//beacon-chain/db/testing:go_default_library", "//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/stategen/mock:go_default_library", diff --git a/beacon-chain/rpc/eth/rewards/handlers_test.go b/beacon-chain/rpc/eth/rewards/handlers_test.go index c94a1ec03..aa41b308a 100644 --- a/beacon-chain/rpc/eth/rewards/handlers_test.go +++ b/beacon-chain/rpc/eth/rewards/handlers_test.go @@ -18,6 +18,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" + dbutil "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/testutil" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" mockstategen "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen/mock" @@ -192,6 +193,7 @@ func BlockRewardTestSetup(t *testing.T, forkName string) (state.BeaconState, int } func TestBlockRewards(t *testing.T) { + db := dbutil.SetupDB(t) phase0block, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock()) require.NoError(t, err) t.Run("phase 0", func(t *testing.T) { @@ -227,7 +229,10 @@ func TestBlockRewards(t *testing.T) { }}, OptimisticModeFetcher: mockChainService, FinalizationFetcher: mockChainService, - BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st))}, + BlockRewardFetcher: &BlockRewardService{ + Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st)), + DB: db, + }, } url := "http://only.the.slot.number.at.the.end.is.important/2" @@ -260,7 +265,10 @@ func TestBlockRewards(t *testing.T) { }}, OptimisticModeFetcher: mockChainService, FinalizationFetcher: mockChainService, - BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st))}, + BlockRewardFetcher: &BlockRewardService{ + Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st)), + DB: db, + }, } url := "http://only.the.slot.number.at.the.end.is.important/2" @@ -293,7 +301,10 @@ func TestBlockRewards(t *testing.T) { }}, OptimisticModeFetcher: mockChainService, FinalizationFetcher: mockChainService, - BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st))}, + BlockRewardFetcher: &BlockRewardService{ + Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st)), + DB: db, + }, } url := "http://only.the.slot.number.at.the.end.is.important/2" @@ -326,7 +337,10 @@ func TestBlockRewards(t *testing.T) { }}, OptimisticModeFetcher: mockChainService, FinalizationFetcher: mockChainService, - BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st))}, + BlockRewardFetcher: &BlockRewardService{ + Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st)), + DB: db, + }, } url := "http://only.the.slot.number.at.the.end.is.important/2" @@ -715,7 +729,9 @@ func TestSyncCommiteeRewards(t *testing.T) { }}, OptimisticModeFetcher: mockChainService, FinalizationFetcher: mockChainService, - BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st))}, + BlockRewardFetcher: &BlockRewardService{ + Replayer: mockstategen.NewReplayerBuilder(mockstategen.WithMockState(st)), + DB: dbutil.SetupDB(t)}, } t.Run("ok - filtered vals", func(t *testing.T) { diff --git a/beacon-chain/rpc/eth/rewards/service.go b/beacon-chain/rpc/eth/rewards/service.go index 85f586be6..1e6c12a3f 100644 --- a/beacon-chain/rpc/eth/rewards/service.go +++ b/beacon-chain/rpc/eth/rewards/service.go @@ -8,7 +8,9 @@ import ( "github.com/prysmaticlabs/prysm/v5/api/server/structs" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair" coreblocks "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/validators" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stategen" consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" @@ -26,6 +28,7 @@ type BlockRewardsFetcher interface { // BlockRewardService implements BlockRewardsFetcher and can be declared to access the underlying functions type BlockRewardService struct { Replayer stategen.ReplayerBuilder + DB db.HeadAccessDatabase } // GetBlockRewardsData returns the BlockRewards object which is used for the BlockRewardsResponse and ProduceBlockV3. @@ -124,6 +127,22 @@ func (rs *BlockRewardService) GetStateForRewards(ctx context.Context, blk interf // We want to run several block processing functions that update the proposer's balance. // This will allow us to calculate proposer rewards for each operation (atts, slashings etc). // To do this, we replay the state up to the block's slot, but before processing the block. + + // Try getting the state from the next slot cache first. + _, prevSlotRoots, err := rs.DB.BlockRootsBySlot(ctx, slots.PrevSlot(blk.Slot())) + if err != nil { + return nil, &httputil.DefaultJsonError{ + Message: "Could not get roots for previous slot: " + err.Error(), + Code: http.StatusInternalServerError, + } + } + for _, r := range prevSlotRoots { + s := transition.NextSlotState(r[:], blk.Slot()) + if s != nil { + return s, nil + } + } + st, err := rs.Replayer.ReplayerForSlot(slots.PrevSlot(blk.Slot())).ReplayToSlot(ctx, blk.Slot()) if err != nil { return nil, &httputil.DefaultJsonError{ diff --git a/beacon-chain/rpc/eth/rewards/service_test.go b/beacon-chain/rpc/eth/rewards/service_test.go new file mode 100644 index 000000000..e7202d49a --- /dev/null +++ b/beacon-chain/rpc/eth/rewards/service_test.go @@ -0,0 +1,46 @@ +package rewards + +import ( + "context" + "testing" + + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" + dbutil "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v5/testing/assert" + "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/testing/util" +) + +func TestGetStateForRewards_NextSlotCacheHit(t *testing.T) { + ctx := context.Background() + db := dbutil.SetupDB(t) + + st, err := util.NewBeaconStateDeneb() + require.NoError(t, err) + b := util.HydrateSignedBeaconBlockDeneb(util.NewBeaconBlockDeneb()) + parent, err := blocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + require.NoError(t, db.SaveBlock(ctx, parent)) + + r, err := parent.Block().HashTreeRoot() + require.NoError(t, err) + require.NoError(t, transition.UpdateNextSlotCache(ctx, r[:], st)) + + s := &BlockRewardService{ + Replayer: nil, // setting to nil because replayer must not be invoked + DB: db, + } + b = util.HydrateSignedBeaconBlockDeneb(util.NewBeaconBlockDeneb()) + sbb, err := blocks.NewSignedBeaconBlock(b) + require.NoError(t, err) + sbb.SetSlot(parent.Block().Slot() + 1) + result, err := s.GetStateForRewards(ctx, sbb.Block()) + require.NoError(t, err) + _, lcs := transition.LastCachedState() + expected, err := lcs.HashTreeRoot(ctx) + require.NoError(t, err) + actual, err := result.HashTreeRoot(ctx) + require.NoError(t, err) + assert.DeepEqual(t, expected, actual) +} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 16b3150b2..49890335a 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -215,7 +215,7 @@ func NewService(ctx context.Context, cfg *Config) *Service { GenesisTimeFetcher: s.cfg.GenesisTimeFetcher, BlobStorage: s.cfg.BlobStorage, } - rewardFetcher := &rewards.BlockRewardService{Replayer: ch} + rewardFetcher := &rewards.BlockRewardService{Replayer: ch, DB: s.cfg.BeaconDB} coreService := &core.Service{ HeadFetcher: s.cfg.HeadFetcher, GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,