Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fa70ca8
adding in block rewards to represent consensus payload
james-prysm Sep 22, 2023
d7e6b69
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 22, 2023
5f420be
Update beacon-chain/rpc/eth/validator/handlers_block.go
james-prysm Sep 25, 2023
c47c1db
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 25, 2023
8a16eda
radek's comments
james-prysm Sep 25, 2023
4e4173c
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 25, 2023
951d6d0
more review changes
james-prysm Sep 25, 2023
0691dcb
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 25, 2023
2ee830f
adding more tests for forks
james-prysm Sep 25, 2023
26e6d89
gaz
james-prysm Sep 25, 2023
62ed6e6
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 26, 2023
bcbe594
Merge branch 'develop' into deneb-blockv3-change
james-prysm Sep 26, 2023
414fbd0
Merge branch 'develop' into deneb-blockv3-change
james-prysm Oct 9, 2023
e0d9b7f
updating names
james-prysm Sep 26, 2023
70e01d9
gaz
james-prysm Oct 9, 2023
3ebc9ea
fixing imports
james-prysm Oct 9, 2023
a9e9a45
fixing variable name
james-prysm Oct 9, 2023
bb6f588
gaz
james-prysm Oct 9, 2023
fb72677
Merge branch 'develop' into deneb-blockv3-change
james-prysm Oct 9, 2023
3e4011f
fixing test
james-prysm Oct 9, 2023
c956e13
Merge branch 'develop' into deneb-blockv3-change
james-prysm Oct 9, 2023
c109cb5
renaming variables to match data
james-prysm Oct 9, 2023
a4586f2
Merge branch 'develop' into deneb-blockv3-change
rkapka Oct 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const (
VersionHeader = "Eth-Consensus-Version"
ExecutionPayloadBlindedHeader = "Eth-Execution-Payload-Blinded"
ExecutionPayloadValueHeader = "Eth-Execution-Payload-Value"
ConsensusPayloadValueHeader = "Eth-Consensus-Payload-Value"
JsonMediaType = "application/json"
OctetStreamMediaType = "application/octet-stream"
)
1 change: 1 addition & 0 deletions beacon-chain/rpc/eth/rewards/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ go_library(
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
],
)

Expand Down
150 changes: 75 additions & 75 deletions beacon-chain/rpc/eth/rewards/handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rewards

import (
"context"
"encoding/json"
"fmt"
"net/http"
Expand All @@ -23,10 +24,13 @@ import (
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/wealdtech/go-bytesutil"
"go.opencensus.io/trace"
)

// BlockRewards is an HTTP handler for Beacon API getBlockRewards.
func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.BlockRewards")
defer span.End()
segments := strings.Split(r.URL.Path, "/")
blockId := segments[len(segments)-1]

Expand All @@ -43,136 +47,134 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
http2.WriteError(w, errJson)
return
}

// 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.
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Message: "Could not get optimistic mode info: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
blkRoot, err := blk.Block().HashTreeRoot()
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get block root: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
blockRewards, httpError := s.BlockRewardFetcher.GetBlockRewardsData(ctx, blk)
if httpError != nil {
http2.WriteError(w, httpError)
return
}
response := &BlockRewardsResponse{
Data: blockRewards,
ExecutionOptimistic: optimistic,
Finalized: s.FinalizationFetcher.IsFinalized(ctx, blkRoot),
}
http2.WriteJson(w, response)
}

// GetBlockRewardsData returns the BlockRewards Object which is used for the BlockRewardsResponse and ProduceBlockV3
func (rs *BlockRewardService) GetBlockRewardsData(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*BlockRewards, *http2.DefaultErrorJson) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should place the interface, the service struct and its methods in a new file.


st, httpErr := rs.GetStateForRewards(ctx, blk)
if httpErr != nil {
return nil, httpErr
}

proposerIndex := blk.Block().ProposerIndex()
initBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
st, err = altair.ProcessAttestationsNoVerifySignature(r.Context(), st, blk)
st, err = altair.ProcessAttestationsNoVerifySignature(ctx, st, blk)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get attestation rewards" + err.Error(),
return nil, &http2.DefaultErrorJson{
Message: "Could not get attestation rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
attBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
st, err = coreblocks.ProcessAttesterSlashings(r.Context(), st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get attester slashing rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
attSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
st, err = coreblocks.ProcessProposerSlashings(r.Context(), st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get proposer slashing rewards" + err.Error(),
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer slashing rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
proposerSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
sa, err := blk.Block().Body().SyncAggregate()
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get sync aggregate: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
var syncCommitteeReward uint64
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(r.Context(), st, sa)
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(ctx, st, sa)
if err != nil {
errJson := &http2.DefaultErrorJson{
return nil, &http2.DefaultErrorJson{
Message: "Could not get sync aggregate rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}

optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get optimistic mode info: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}
blkRoot, err := blk.Block().HashTreeRoot()
return &BlockRewards{
ProposerIndex: strconv.FormatUint(uint64(proposerIndex), 10),
Total: strconv.FormatUint(proposerSlashingsBalance-initBalance+syncCommitteeReward, 10),
Attestations: strconv.FormatUint(attBalance-initBalance, 10),
SyncAggregate: strconv.FormatUint(syncCommitteeReward, 10),
ProposerSlashings: strconv.FormatUint(proposerSlashingsBalance-attSlashingsBalance, 10),
AttesterSlashings: strconv.FormatUint(attSlashingsBalance-attBalance, 10),
}, nil
}

// GetStateForRewards returns the state replayed up to the block's slot
func (rs *BlockRewardService) GetStateForRewards(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, *http2.DefaultErrorJson) {
// 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should be placed before the call to this function in BlockRewards because it is accurate only in that function context.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the produceblockv3 needs to do the same , it should be accurate to the context of the block passed in as an argument right?

st, err := rs.Replayer.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(ctx, blk.Block().Slot())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get block root: " + err.Error(),
return nil, &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
return
}

response := &BlockRewardsResponse{
Data: BlockRewards{
ProposerIndex: strconv.FormatUint(uint64(proposerIndex), 10),
Total: strconv.FormatUint(proposerSlashingsBalance-initBalance+syncCommitteeReward, 10),
Attestations: strconv.FormatUint(attBalance-initBalance, 10),
SyncAggregate: strconv.FormatUint(syncCommitteeReward, 10),
ProposerSlashings: strconv.FormatUint(proposerSlashingsBalance-attSlashingsBalance, 10),
AttesterSlashings: strconv.FormatUint(attSlashingsBalance-attBalance, 10),
},
ExecutionOptimistic: optimistic,
Finalized: s.FinalizationFetcher.IsFinalized(r.Context(), blkRoot),
}
http2.WriteJson(w, response)
return st, nil
}

// AttestationRewards retrieves attestation reward info for validators specified by array of public keys or validator index.
Expand Down Expand Up @@ -229,10 +231,12 @@ func (s *Server) AttestationRewards(w http.ResponseWriter, r *http.Request) {
// SyncCommitteeRewards retrieves rewards info for sync committee members specified by array of public keys or validator index.
// If no array is provided, return reward info for every committee member.
func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SyncCommitteeRewards")
defer span.End()
segments := strings.Split(r.URL.Path, "/")
blockId := segments[len(segments)-1]

blk, err := s.Blocker.Block(r.Context(), []byte(blockId))
blk, err := s.Blocker.Block(ctx, []byte(blockId))
if errJson := handleGetBlockError(blk, err); errJson != nil {
http2.WriteError(w, errJson)
return
Expand All @@ -245,13 +249,9 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
http2.WriteError(w, errJson)
return
}
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
if err != nil {
errJson := &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Code: http.StatusInternalServerError,
}
http2.WriteError(w, errJson)
st, httpErr := s.BlockRewardFetcher.GetStateForRewards(ctx, blk)
if httpErr != nil {
http2.WriteError(w, httpErr)
return
}
sa, err := blk.Block().Body().SyncAggregate()
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/rpc/eth/rewards/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func TestBlockRewards(t *testing.T) {
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st)),
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}

t.Run("ok", func(t *testing.T) {
Expand Down Expand Up @@ -560,7 +560,7 @@ func TestSyncCommiteeRewards(t *testing.T) {
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st)),
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}

t.Run("ok - filtered vals", func(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions beacon-chain/rpc/eth/rewards/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ package rewards
import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
)

type Server struct {
Blocker lookup.Blocker
OptimisticModeFetcher blockchain.OptimisticModeFetcher
FinalizationFetcher blockchain.FinalizationFetcher
ReplayerBuilder stategen.ReplayerBuilder
TimeFetcher blockchain.TimeFetcher
Stater lookup.Stater
HeadFetcher blockchain.HeadFetcher
BlockRewardFetcher BlockRewardsFetcher
}
25 changes: 22 additions & 3 deletions beacon-chain/rpc/eth/rewards/structs.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package rewards

import (
"context"

"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
)

// BlockRewardsFetcher retrieves the Consensus Payload ( aka block rewards) of the passed in block
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest making this comment more generic because GetStateForRewards doesn't retrieve the consensus payload

type BlockRewardsFetcher interface {
GetBlockRewardsData(context.Context, interfaces.ReadOnlySignedBeaconBlock) (*BlockRewards, *http2.DefaultErrorJson)
GetStateForRewards(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, *http2.DefaultErrorJson)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: one function has named params, the other one doesn't


type BlockRewardService struct {
Replayer stategen.ReplayerBuilder
}

type BlockRewardsResponse struct {
Data BlockRewards `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
Data *BlockRewards `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}

type BlockRewards struct {
Expand Down
14 changes: 14 additions & 0 deletions beacon-chain/rpc/eth/rewards/testing/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@prysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["mock.go"],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards/testing",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/rpc/eth/rewards:go_default_library",
"//beacon-chain/state:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//network/http:go_default_library",
],
)
30 changes: 30 additions & 0 deletions beacon-chain/rpc/eth/rewards/testing/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package testing

import (
"context"

"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
)

type MockBlockRewardFetcher struct {
Rewards *rewards.BlockRewards
Error *http2.DefaultErrorJson
State state.BeaconState
}

func (m *MockBlockRewardFetcher) GetBlockRewardsData(_ context.Context, _ interfaces.ReadOnlySignedBeaconBlock) (*rewards.BlockRewards, *http2.DefaultErrorJson) {
if m.Error != nil {
return nil, m.Error
}
return m.Rewards, nil
}

func (m *MockBlockRewardFetcher) GetStateForRewards(_ context.Context, _ interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, *http2.DefaultErrorJson) {
if m.Error != nil {
return nil, m.Error
}
return m.State, nil
}
Loading