Deneb: Produce Block V3 - adding consensus block value (#12948)

* adding in block rewards to represent consensus payload

* Update beacon-chain/rpc/eth/validator/handlers_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* radek's comments

* more review changes

* adding more tests for forks

* gaz

* updating names

* gaz

* fixing imports

* fixing variable name

* gaz

* fixing test

* renaming variables to match data

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
james-prysm
2023-10-10 10:12:20 -05:00
committed by GitHub
parent 8eb82dd378
commit af70677778
17 changed files with 547 additions and 158 deletions

View File

@@ -4,6 +4,7 @@ const (
VersionHeader = "Eth-Consensus-Version" VersionHeader = "Eth-Consensus-Version"
ExecutionPayloadBlindedHeader = "Eth-Execution-Payload-Blinded" ExecutionPayloadBlindedHeader = "Eth-Execution-Payload-Blinded"
ExecutionPayloadValueHeader = "Eth-Execution-Payload-Value" ExecutionPayloadValueHeader = "Eth-Execution-Payload-Value"
ConsensusBlockValueHeader = "Eth-Consensus-Block-Value"
JsonMediaType = "application/json" JsonMediaType = "application/json"
OctetStreamMediaType = "application/octet-stream" OctetStreamMediaType = "application/octet-stream"
) )

View File

@@ -5,6 +5,7 @@ go_library(
srcs = [ srcs = [
"handlers.go", "handlers.go",
"server.go", "server.go",
"service.go",
"structs.go", "structs.go",
], ],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards", importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards",
@@ -21,11 +22,13 @@ go_library(
"//beacon-chain/state/stategen:go_default_library", "//beacon-chain/state/stategen:go_default_library",
"//config/fieldparams:go_default_library", "//config/fieldparams:go_default_library",
"//config/params:go_default_library", "//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library", "//consensus-types/primitives:go_default_library",
"//network/http:go_default_library", "//network/http:go_default_library",
"//runtime/version:go_default_library", "//runtime/version:go_default_library",
"//time/slots:go_default_library", "//time/slots:go_default_library",
"@com_github_wealdtech_go_bytesutil//:go_default_library", "@com_github_wealdtech_go_bytesutil//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
], ],
) )
@@ -54,6 +57,7 @@ go_test(
"//testing/assert:go_default_library", "//testing/assert:go_default_library",
"//testing/require:go_default_library", "//testing/require:go_default_library",
"//testing/util:go_default_library", "//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library",
], ],
) )

View File

@@ -8,9 +8,7 @@ import (
"strings" "strings"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
coreblocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch/precompute" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
@@ -20,10 +18,13 @@ import (
"github.com/prysmaticlabs/prysm/v4/runtime/version" "github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots" "github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/wealdtech/go-bytesutil" "github.com/wealdtech/go-bytesutil"
"go.opencensus.io/trace"
) )
// BlockRewards is an HTTP handler for Beacon API getBlockRewards. // BlockRewards is an HTTP handler for Beacon API getBlockRewards.
func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) { 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, "/") segments := strings.Split(r.URL.Path, "/")
blockId := segments[len(segments)-1] blockId := segments[len(segments)-1]
@@ -36,63 +37,6 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
return 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())
if err != nil {
http2.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError)
return
}
proposerIndex := blk.Block().ProposerIndex()
initBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = altair.ProcessAttestationsNoVerifySignature(r.Context(), st, blk)
if err != nil {
http2.HandleError(w, "Could not get attestation rewards"+err.Error(), http.StatusInternalServerError)
return
}
attBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = coreblocks.ProcessAttesterSlashings(r.Context(), st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
if err != nil {
http2.HandleError(w, "Could not get attester slashing rewards: "+err.Error(), http.StatusInternalServerError)
return
}
attSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
st, err = coreblocks.ProcessProposerSlashings(r.Context(), st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
if err != nil {
http2.HandleError(w, "Could not get proposer slashing rewards"+err.Error(), http.StatusInternalServerError)
return
}
proposerSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
http2.HandleError(w, "Could not get proposer's balance: "+err.Error(), http.StatusInternalServerError)
return
}
sa, err := blk.Block().Body().SyncAggregate()
if err != nil {
http2.HandleError(w, "Could not get sync aggregate: "+err.Error(), http.StatusInternalServerError)
return
}
var syncCommitteeReward uint64
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(r.Context(), st, sa)
if err != nil {
http2.HandleError(w, "Could not get sync aggregate rewards: "+err.Error(), http.StatusInternalServerError)
return
}
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context()) optimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil { if err != nil {
http2.HandleError(w, "Could not get optimistic mode info: "+err.Error(), http.StatusInternalServerError) http2.HandleError(w, "Could not get optimistic mode info: "+err.Error(), http.StatusInternalServerError)
@@ -103,18 +47,15 @@ func (s *Server) BlockRewards(w http.ResponseWriter, r *http.Request) {
http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError) http2.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
return return
} }
blockRewards, httpError := s.BlockRewardFetcher.GetBlockRewardsData(ctx, blk)
if httpError != nil {
http2.WriteError(w, httpError)
return
}
response := &BlockRewardsResponse{ response := &BlockRewardsResponse{
Data: BlockRewards{ 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, ExecutionOptimistic: optimistic,
Finalized: s.FinalizationFetcher.IsFinalized(r.Context(), blkRoot), Finalized: s.FinalizationFetcher.IsFinalized(ctx, blkRoot),
} }
http2.WriteJson(w, response) http2.WriteJson(w, response)
} }
@@ -165,6 +106,8 @@ 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. // 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. // If no array is provided, return reward info for every committee member.
func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) { 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, "/") segments := strings.Split(r.URL.Path, "/")
blockId := segments[len(segments)-1] blockId := segments[len(segments)-1]
@@ -176,9 +119,10 @@ func (s *Server) SyncCommitteeRewards(w http.ResponseWriter, r *http.Request) {
http2.HandleError(w, "Sync committee rewards are not supported for Phase 0", http.StatusBadRequest) http2.HandleError(w, "Sync committee rewards are not supported for Phase 0", http.StatusBadRequest)
return return
} }
st, err := s.ReplayerBuilder.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(r.Context(), blk.Block().Slot())
if err != nil { st, httpErr := s.BlockRewardFetcher.GetStateForRewards(ctx, blk)
http2.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError) if httpErr != nil {
http2.WriteError(w, httpErr)
return return
} }
sa, err := blk.Block().Body().SyncAggregate() sa, err := blk.Block().Body().SyncAggregate()

View File

@@ -11,6 +11,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-bitfield"
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing" mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
@@ -34,12 +35,42 @@ import (
"github.com/prysmaticlabs/prysm/v4/testing/util" "github.com/prysmaticlabs/prysm/v4/testing/util"
) )
func TestBlockRewards(t *testing.T) { func BlockRewardTestSetup(t *testing.T, forkName string) (state.BeaconState, interfaces.SignedBeaconBlock, error) {
helpers.ClearCache() helpers.ClearCache()
var sbb interfaces.SignedBeaconBlock
var st state.BeaconState
var err error
switch forkName {
case "phase0":
return nil, nil, errors.New("phase0 not supported")
case "altair":
st, err = util.NewBeaconStateAltair()
require.NoError(t, err)
b := util.HydrateSignedBeaconBlockAltair(util.NewBeaconBlockAltair())
sbb, err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
case "bellatrix":
st, err = util.NewBeaconStateBellatrix()
require.NoError(t, err)
b := util.HydrateSignedBeaconBlockBellatrix(util.NewBeaconBlockBellatrix())
sbb, err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
case "capella":
st, err = util.NewBeaconStateCapella()
require.NoError(t, err)
b := util.HydrateSignedBeaconBlockCapella(util.NewBeaconBlockCapella())
sbb, err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
case "deneb":
st, err = util.NewBeaconStateDeneb()
require.NoError(t, err)
b := util.HydrateSignedBeaconBlockDeneb(util.NewBeaconBlockDeneb())
sbb, err = blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
default:
return nil, nil, errors.New("fork is not supported")
}
valCount := 64 valCount := 64
st, err := util.NewBeaconStateCapella()
require.NoError(t, st.SetSlot(1)) require.NoError(t, st.SetSlot(1))
require.NoError(t, err) require.NoError(t, err)
validators := make([]*eth.Validator, 0, valCount) validators := make([]*eth.Validator, 0, valCount)
@@ -68,11 +99,10 @@ func TestBlockRewards(t *testing.T) {
bRoots[0] = slot0bRoot bRoots[0] = slot0bRoot
require.NoError(t, st.SetBlockRoots(bRoots)) require.NoError(t, st.SetBlockRoots(bRoots))
b := util.HydrateSignedBeaconBlockCapella(util.NewBeaconBlockCapella()) sbb.SetSlot(2)
b.Block.Slot = 2
// we have to set the proposer index to the value that will be randomly chosen (fortunately it's deterministic) // we have to set the proposer index to the value that will be randomly chosen (fortunately it's deterministic)
b.Block.ProposerIndex = 12 sbb.SetProposerIndex(12)
b.Block.Body.Attestations = []*eth.Attestation{ sbb.SetAttestations([]*eth.Attestation{
{ {
AggregationBits: bitfield.Bitlist{0b00000111}, AggregationBits: bitfield.Bitlist{0b00000111},
Data: util.HydrateAttestationData(&eth.AttestationData{}), Data: util.HydrateAttestationData(&eth.AttestationData{}),
@@ -83,7 +113,8 @@ func TestBlockRewards(t *testing.T) {
Data: util.HydrateAttestationData(&eth.AttestationData{}), Data: util.HydrateAttestationData(&eth.AttestationData{}),
Signature: make([]byte, fieldparams.BLSSignatureLength), Signature: make([]byte, fieldparams.BLSSignatureLength),
}, },
} })
attData1 := util.HydrateAttestationData(&eth.AttestationData{BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32)}) attData1 := util.HydrateAttestationData(&eth.AttestationData{BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32)})
attData2 := util.HydrateAttestationData(&eth.AttestationData{BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32)}) attData2 := util.HydrateAttestationData(&eth.AttestationData{BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32)})
domain, err := signing.Domain(st.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorsRoot()) domain, err := signing.Domain(st.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, st.GenesisValidatorsRoot())
@@ -92,7 +123,7 @@ func TestBlockRewards(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
sigRoot2, err := signing.ComputeSigningRoot(attData2, domain) sigRoot2, err := signing.ComputeSigningRoot(attData2, domain)
require.NoError(t, err) require.NoError(t, err)
b.Block.Body.AttesterSlashings = []*eth.AttesterSlashing{ sbb.SetAttesterSlashings([]*eth.AttesterSlashing{
{ {
Attestation_1: &eth.IndexedAttestation{ Attestation_1: &eth.IndexedAttestation{
AttestingIndices: []uint64{0}, AttestingIndices: []uint64{0},
@@ -105,7 +136,7 @@ func TestBlockRewards(t *testing.T) {
Signature: secretKeys[0].Sign(sigRoot2[:]).Marshal(), Signature: secretKeys[0].Sign(sigRoot2[:]).Marshal(),
}, },
}, },
} })
header1 := &eth.BeaconBlockHeader{ header1 := &eth.BeaconBlockHeader{
Slot: 0, Slot: 0,
ProposerIndex: 1, ProposerIndex: 1,
@@ -126,7 +157,7 @@ func TestBlockRewards(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
sigRoot2, err = signing.ComputeSigningRoot(header2, domain) sigRoot2, err = signing.ComputeSigningRoot(header2, domain)
require.NoError(t, err) require.NoError(t, err)
b.Block.Body.ProposerSlashings = []*eth.ProposerSlashing{ sbb.SetProposerSlashings([]*eth.ProposerSlashing{
{ {
Header_1: &eth.SignedBeaconBlockHeader{ Header_1: &eth.SignedBeaconBlockHeader{
Header: header1, Header: header1,
@@ -137,7 +168,7 @@ func TestBlockRewards(t *testing.T) {
Signature: secretKeys[1].Sign(sigRoot2[:]).Marshal(), Signature: secretKeys[1].Sign(sigRoot2[:]).Marshal(),
}, },
}, },
} })
scBits := bitfield.NewBitvector512() scBits := bitfield.NewBitvector512()
scBits.SetBitAt(10, true) scBits.SetBitAt(10, true)
scBits.SetBitAt(100, true) scBits.SetBitAt(100, true)
@@ -153,24 +184,51 @@ func TestBlockRewards(t *testing.T) {
sig2, err := blst.SignatureFromBytes(secretKeys[19].Sign(r[:]).Marshal()) sig2, err := blst.SignatureFromBytes(secretKeys[19].Sign(r[:]).Marshal())
require.NoError(t, err) require.NoError(t, err)
aggSig := bls.AggregateSignatures([]bls.Signature{sig1, sig2}).Marshal() aggSig := bls.AggregateSignatures([]bls.Signature{sig1, sig2}).Marshal()
b.Block.Body.SyncAggregate = &eth.SyncAggregate{SyncCommitteeBits: scBits, SyncCommitteeSignature: aggSig} err = sbb.SetSyncAggregate(&eth.SyncAggregate{SyncCommitteeBits: scBits, SyncCommitteeSignature: aggSig})
sbb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err) require.NoError(t, err)
return st, sbb, nil
}
func TestBlockRewards(t *testing.T) {
phase0block, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock()) phase0block, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err) require.NoError(t, err)
mockChainService := &mock.ChainService{Optimistic: true} t.Run("phase 0", func(t *testing.T) {
s := &Server{ mockChainService := &mock.ChainService{Optimistic: true}
Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{ s := &Server{
0: phase0block, Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
2: sbb, 0: phase0block,
}}, }},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService, FinalizationFetcher: mockChainService,
ReplayerBuilder: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st)), }
} url := "http://only.the.slot.number.at.the.end.is.important/0"
request := httptest.NewRequest("GET", url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.BlockRewards(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.Equal(t, "Block rewards are not supported for Phase 0 blocks", e.Message)
})
t.Run("altair", func(t *testing.T) {
st, sbb, err := BlockRewardTestSetup(t, "altair")
require.NoError(t, err)
mockChainService := &mock.ChainService{Optimistic: true}
s := &Server{
Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
0: phase0block,
2: sbb,
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}
t.Run("ok", func(t *testing.T) {
url := "http://only.the.slot.number.at.the.end.is.important/2" url := "http://only.the.slot.number.at.the.end.is.important/2"
request := httptest.NewRequest("GET", url, nil) request := httptest.NewRequest("GET", url, nil)
writer := httptest.NewRecorder() writer := httptest.NewRecorder()
@@ -189,18 +247,104 @@ func TestBlockRewards(t *testing.T) {
assert.Equal(t, true, resp.ExecutionOptimistic) assert.Equal(t, true, resp.ExecutionOptimistic)
assert.Equal(t, false, resp.Finalized) assert.Equal(t, false, resp.Finalized)
}) })
t.Run("phase 0", func(t *testing.T) { t.Run("bellatrix", func(t *testing.T) {
url := "http://only.the.slot.number.at.the.end.is.important/0" st, sbb, err := BlockRewardTestSetup(t, "bellatrix")
require.NoError(t, err)
mockChainService := &mock.ChainService{Optimistic: true}
s := &Server{
Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
0: phase0block,
2: sbb,
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}
url := "http://only.the.slot.number.at.the.end.is.important/2"
request := httptest.NewRequest("GET", url, nil) request := httptest.NewRequest("GET", url, nil)
writer := httptest.NewRecorder() writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
s.BlockRewards(writer, request) s.BlockRewards(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
e := &http2.DefaultErrorJson{} resp := &BlockRewardsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, http.StatusBadRequest, e.Code) assert.Equal(t, "12", resp.Data.ProposerIndex)
assert.Equal(t, "Block rewards are not supported for Phase 0 blocks", e.Message) assert.Equal(t, "125089490", resp.Data.Total)
assert.Equal(t, "89442", resp.Data.Attestations)
assert.Equal(t, "48", resp.Data.SyncAggregate)
assert.Equal(t, "62500000", resp.Data.AttesterSlashings)
assert.Equal(t, "62500000", resp.Data.ProposerSlashings)
assert.Equal(t, true, resp.ExecutionOptimistic)
assert.Equal(t, false, resp.Finalized)
})
t.Run("capella", func(t *testing.T) {
st, sbb, err := BlockRewardTestSetup(t, "capella")
require.NoError(t, err)
mockChainService := &mock.ChainService{Optimistic: true}
s := &Server{
Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
0: phase0block,
2: sbb,
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}
url := "http://only.the.slot.number.at.the.end.is.important/2"
request := httptest.NewRequest("GET", url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.BlockRewards(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &BlockRewardsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, "12", resp.Data.ProposerIndex)
assert.Equal(t, "125089490", resp.Data.Total)
assert.Equal(t, "89442", resp.Data.Attestations)
assert.Equal(t, "48", resp.Data.SyncAggregate)
assert.Equal(t, "62500000", resp.Data.AttesterSlashings)
assert.Equal(t, "62500000", resp.Data.ProposerSlashings)
assert.Equal(t, true, resp.ExecutionOptimistic)
assert.Equal(t, false, resp.Finalized)
})
t.Run("deneb", func(t *testing.T) {
st, sbb, err := BlockRewardTestSetup(t, "deneb")
require.NoError(t, err)
mockChainService := &mock.ChainService{Optimistic: true}
s := &Server{
Blocker: &testutil.MockBlocker{SlotBlockMap: map[primitives.Slot]interfaces.ReadOnlySignedBeaconBlock{
0: phase0block,
2: sbb,
}},
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
BlockRewardFetcher: &BlockRewardService{Replayer: mockstategen.NewMockReplayerBuilder(mockstategen.WithMockState(st))},
}
url := "http://only.the.slot.number.at.the.end.is.important/2"
request := httptest.NewRequest("GET", url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.BlockRewards(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &BlockRewardsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, "12", resp.Data.ProposerIndex)
assert.Equal(t, "125089490", resp.Data.Total)
assert.Equal(t, "89442", resp.Data.Attestations)
assert.Equal(t, "48", resp.Data.SyncAggregate)
assert.Equal(t, "62500000", resp.Data.AttesterSlashings)
assert.Equal(t, "62500000", resp.Data.ProposerSlashings)
assert.Equal(t, true, resp.ExecutionOptimistic)
assert.Equal(t, false, resp.Finalized)
}) })
} }
@@ -560,7 +704,7 @@ func TestSyncCommiteeRewards(t *testing.T) {
}}, }},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
FinalizationFetcher: 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) { t.Run("ok - filtered vals", func(t *testing.T) {

View File

@@ -3,15 +3,14 @@ package rewards
import ( import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen"
) )
type Server struct { type Server struct {
Blocker lookup.Blocker Blocker lookup.Blocker
OptimisticModeFetcher blockchain.OptimisticModeFetcher OptimisticModeFetcher blockchain.OptimisticModeFetcher
FinalizationFetcher blockchain.FinalizationFetcher FinalizationFetcher blockchain.FinalizationFetcher
ReplayerBuilder stategen.ReplayerBuilder
TimeFetcher blockchain.TimeFetcher TimeFetcher blockchain.TimeFetcher
Stater lookup.Stater Stater lookup.Stater
HeadFetcher blockchain.HeadFetcher HeadFetcher blockchain.HeadFetcher
BlockRewardFetcher BlockRewardsFetcher
} }

View File

@@ -0,0 +1,124 @@
package rewards
import (
"context"
"net/http"
"strconv"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/altair"
coreblocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/validators"
"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 is a interface that provides access to reward related responses
type BlockRewardsFetcher interface {
GetBlockRewardsData(context.Context, interfaces.ReadOnlySignedBeaconBlock) (*BlockRewards, *http2.DefaultErrorJson)
GetStateForRewards(context.Context, interfaces.ReadOnlySignedBeaconBlock) (state.BeaconState, *http2.DefaultErrorJson)
}
// BlockRewardService implements BlockRewardsFetcher and can be declared to access the underlying functions
type BlockRewardService struct {
Replayer stategen.ReplayerBuilder
}
// 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) {
st, httpErr := rs.GetStateForRewards(ctx, blk)
if httpErr != nil {
return nil, httpErr
}
proposerIndex := blk.Block().ProposerIndex()
initBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
st, err = altair.ProcessAttestationsNoVerifySignature(ctx, st, blk)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get attestation rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
attBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
st, err = coreblocks.ProcessAttesterSlashings(ctx, st, blk.Block().Body().AttesterSlashings(), validators.SlashValidator)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get attester slashing rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
attSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
st, err = coreblocks.ProcessProposerSlashings(ctx, st, blk.Block().Body().ProposerSlashings(), validators.SlashValidator)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer slashing rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
proposerSlashingsBalance, err := st.BalanceAtIndex(proposerIndex)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get proposer's balance: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
sa, err := blk.Block().Body().SyncAggregate()
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get sync aggregate: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
var syncCommitteeReward uint64
_, syncCommitteeReward, err = altair.ProcessSyncAggregate(ctx, st, sa)
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get sync aggregate rewards: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
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.
st, err := rs.Replayer.ReplayerForSlot(blk.Block().Slot()-1).ReplayToSlot(ctx, blk.Block().Slot())
if err != nil {
return nil, &http2.DefaultErrorJson{
Message: "Could not get state: " + err.Error(),
Code: http.StatusInternalServerError,
}
}
return st, nil
}

View File

@@ -1,9 +1,9 @@
package rewards package rewards
type BlockRewardsResponse struct { type BlockRewardsResponse struct {
Data BlockRewards `json:"data"` Data *BlockRewards `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"` ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"` Finalized bool `json:"finalized"`
} }
type BlockRewards struct { type BlockRewards struct {

View File

@@ -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",
],
)

View File

@@ -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
}

View File

@@ -162,7 +162,7 @@ const (
}` }`
AltairBlock = `{ AltairBlock = `{
"message": { "message": {
"slot": "1", "slot": "2",
"proposer_index": "1", "proposer_index": "1",
"parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
@@ -204,7 +204,7 @@ const (
"attesting_indices": [ "attesting_indices": [
"1" "1"
], ],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", "signature": "0xabb0124c7574f281a293f4185cad3cb22681d520917ce46665243eacb051000d8bacf75e1451870ca6b3b9e6c9d41a7b02ead2685a84188a4fafd3825daf6a989625d719ccd2d83a40101f4a453fca62878c890eca622363f9ddb8f367a91e84",
"data": { "data": {
"slot": "1", "slot": "1",
"index": "1", "index": "1",
@@ -223,18 +223,18 @@ const (
"attesting_indices": [ "attesting_indices": [
"1" "1"
], ],
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", "signature": "0xabb0124c7574f281a293f4185cad3cb22681d520917ce46665243eacb051000d8bacf75e1451870ca6b3b9e6c9d41a7b02ead2685a84188a4fafd3825daf6a989625d719ccd2d83a40101f4a453fca62878c890eca622363f9ddb8f367a91e84",
"data": { "data": {
"slot": "1", "slot": "1",
"index": "1", "index": "1",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": { "source": {
"epoch": "1", "epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f3"
}, },
"target": { "target": {
"epoch": "1", "epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f3"
} }
} }
} }
@@ -242,18 +242,18 @@ const (
], ],
"attestations": [ "attestations": [
{ {
"aggregation_bits": "0xffffffffffffffffffffffffffffffffff3f", "aggregation_bits": "0x07",
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", "signature": "0xabb0124c7574f281a293f4185cad3cb22681d520917ce46665243eacb051000d8bacf75e1451870ca6b3b9e6c9d41a7b02ead2685a84188a4fafd3825daf6a989625d719ccd2d83a40101f4a453fca62878c890eca622363f9ddb8f367a91e84",
"data": { "data": {
"slot": "1", "slot": "0",
"index": "1", "index": "0",
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"source": { "source": {
"epoch": "1", "epoch": "1",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
}, },
"target": { "target": {
"epoch": "1", "epoch": "0",
"root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
} }
} }

View File

@@ -26,6 +26,7 @@ go_library(
"//beacon-chain/p2p:go_default_library", "//beacon-chain/p2p:go_default_library",
"//beacon-chain/rpc/core:go_default_library", "//beacon-chain/rpc/core:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library", "//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/rpc/eth/rewards:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library", "//beacon-chain/rpc/lookup:go_default_library",
"//beacon-chain/state:go_default_library", "//beacon-chain/state:go_default_library",
@@ -33,6 +34,8 @@ go_library(
"//config/fieldparams:go_default_library", "//config/fieldparams:go_default_library",
"//config/params:go_default_library", "//config/params:go_default_library",
"//consensus-types:go_default_library", "//consensus-types:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library", "//consensus-types/primitives:go_default_library",
"//consensus-types/validator:go_default_library", "//consensus-types/validator:go_default_library",
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
@@ -75,6 +78,8 @@ go_test(
"//beacon-chain/operations/synccommittee:go_default_library", "//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/rpc/core:go_default_library", "//beacon-chain/rpc/core:go_default_library",
"//beacon-chain/rpc/eth/rewards:go_default_library",
"//beacon-chain/rpc/eth/rewards/testing:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library",
"//beacon-chain/rpc/eth/shared/testing:go_default_library", "//beacon-chain/rpc/eth/shared/testing:go_default_library",
"//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/rpc/testutil:go_default_library",

View File

@@ -9,8 +9,11 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api" "github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
http2 "github.com/prysmaticlabs/prysm/v4/network/http" http2 "github.com/prysmaticlabs/prysm/v4/network/http"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -84,16 +87,26 @@ func (s *Server) produceBlockV3(ctx context.Context, w http.ResponseWriter, r *h
http2.HandleError(w, err.Error(), http.StatusInternalServerError) http2.HandleError(w, err.Error(), http.StatusInternalServerError)
return return
} }
consensusBlockValue, httpError := getConsensusBlockValue(ctx, s.BlockRewardFetcher, v1alpha1resp.Block)
if httpError != nil {
http2.WriteError(w, httpError)
return
}
w.Header().Set(api.ExecutionPayloadBlindedHeader, fmt.Sprintf("%v", v1alpha1resp.IsBlinded)) w.Header().Set(api.ExecutionPayloadBlindedHeader, fmt.Sprintf("%v", v1alpha1resp.IsBlinded))
w.Header().Set(api.ExecutionPayloadValueHeader, fmt.Sprintf("%d", v1alpha1resp.PayloadValue)) w.Header().Set(api.ExecutionPayloadValueHeader, fmt.Sprintf("%d", v1alpha1resp.PayloadValue))
w.Header().Set(api.ConsensusBlockValueHeader, consensusBlockValue)
phase0Block, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Phase0) phase0Block, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Phase0)
if ok { if ok {
// rewards aren't used in phase 0
handleProducePhase0V3(ctx, w, isSSZ, phase0Block, v1alpha1resp.PayloadValue) handleProducePhase0V3(ctx, w, isSSZ, phase0Block, v1alpha1resp.PayloadValue)
return return
} }
altairBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Altair) altairBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Altair)
if ok { if ok {
handleProduceAltairV3(ctx, w, isSSZ, altairBlock, v1alpha1resp.PayloadValue) handleProduceAltairV3(ctx, w, isSSZ, altairBlock, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
optimistic, err := s.OptimisticModeFetcher.IsOptimistic(ctx) optimistic, err := s.OptimisticModeFetcher.IsOptimistic(ctx)
@@ -107,36 +120,82 @@ func (s *Server) produceBlockV3(ctx context.Context, w http.ResponseWriter, r *h
} }
blindedBellatrixBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedBellatrix) blindedBellatrixBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedBellatrix)
if ok { if ok {
handleProduceBlindedBellatrixV3(ctx, w, isSSZ, blindedBellatrixBlock, v1alpha1resp.PayloadValue) handleProduceBlindedBellatrixV3(ctx, w, isSSZ, blindedBellatrixBlock, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
bellatrixBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Bellatrix) bellatrixBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Bellatrix)
if ok { if ok {
handleProduceBellatrixV3(ctx, w, isSSZ, bellatrixBlock, v1alpha1resp.PayloadValue) handleProduceBellatrixV3(ctx, w, isSSZ, bellatrixBlock, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
blindedCapellaBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedCapella) blindedCapellaBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedCapella)
if ok { if ok {
handleProduceBlindedCapellaV3(ctx, w, isSSZ, blindedCapellaBlock, v1alpha1resp.PayloadValue) handleProduceBlindedCapellaV3(ctx, w, isSSZ, blindedCapellaBlock, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
capellaBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Capella) capellaBlock, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Capella)
if ok { if ok {
handleProduceCapellaV3(ctx, w, isSSZ, capellaBlock, v1alpha1resp.PayloadValue) handleProduceCapellaV3(ctx, w, isSSZ, capellaBlock, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
blindedDenebBlockContents, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedDeneb) blindedDenebBlockContents, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_BlindedDeneb)
if ok { if ok {
handleProduceBlindedDenebV3(ctx, w, isSSZ, blindedDenebBlockContents, v1alpha1resp.PayloadValue) handleProduceBlindedDenebV3(ctx, w, isSSZ, blindedDenebBlockContents, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
denebBlockContents, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Deneb) denebBlockContents, ok := v1alpha1resp.Block.(*eth.GenericBeaconBlock_Deneb)
if ok { if ok {
handleProduceDenebV3(ctx, w, isSSZ, denebBlockContents, v1alpha1resp.PayloadValue) handleProduceDenebV3(ctx, w, isSSZ, denebBlockContents, v1alpha1resp.PayloadValue, consensusBlockValue)
return return
} }
} }
func getConsensusBlockValue(ctx context.Context, blockRewardsFetcher rewards.BlockRewardsFetcher, i interface{} /* block as argument */) (string, *http2.DefaultErrorJson) {
var wrapper interfaces.ReadOnlySignedBeaconBlock
var err error
// TODO: we should not require this fake signed wrapper and fix associated functions in the future.
switch b := i.(type) {
case *eth.GenericBeaconBlock_Phase0:
//ignore for phase0
return "", nil
case *eth.GenericBeaconBlock_Altair:
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_Altair{Altair: &eth.SignedBeaconBlockAltair{Block: b.Altair}})
case *eth.GenericBeaconBlock_Bellatrix:
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_Bellatrix{Bellatrix: &eth.SignedBeaconBlockBellatrix{Block: b.Bellatrix}})
case *eth.GenericBeaconBlock_BlindedBellatrix:
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_BlindedBellatrix{BlindedBellatrix: &eth.SignedBlindedBeaconBlockBellatrix{Block: b.BlindedBellatrix}})
case *eth.GenericBeaconBlock_Capella:
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_Capella{Capella: &eth.SignedBeaconBlockCapella{Block: b.Capella}})
case *eth.GenericBeaconBlock_BlindedCapella:
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: &eth.SignedBlindedBeaconBlockCapella{Block: b.BlindedCapella}})
case *eth.GenericBeaconBlock_Deneb:
// no need for sidecar
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_Deneb{Deneb: &eth.SignedBeaconBlockAndBlobsDeneb{Block: &eth.SignedBeaconBlockDeneb{Block: b.Deneb.Block}}})
case *eth.GenericBeaconBlock_BlindedDeneb:
// no need for sidecar
wrapper, err = blocks.NewSignedBeaconBlock(&eth.GenericSignedBeaconBlock_BlindedDeneb{BlindedDeneb: &eth.SignedBlindedBeaconBlockAndBlobsDeneb{SignedBlindedBlock: &eth.SignedBlindedBeaconBlockDeneb{Message: b.BlindedDeneb.Block}}})
default:
return "", &http2.DefaultErrorJson{
Message: fmt.Errorf("type %T is not supported", b).Error(),
Code: http.StatusInternalServerError,
}
}
if err != nil {
return "", &http2.DefaultErrorJson{
Message: err.Error(),
Code: http.StatusInternalServerError,
}
}
//get consensus payload value which is the same as the total from the block rewards api
blockRewards, httpError := blockRewardsFetcher.GetBlockRewardsData(ctx, wrapper)
if httpError != nil {
return "", httpError
}
return blockRewards.Total, nil
}
func handleProducePhase0V3( func handleProducePhase0V3(
ctx context.Context, ctx context.Context,
w http.ResponseWriter, w http.ResponseWriter,
@@ -169,6 +228,7 @@ func handleProducePhase0V3(
Version: version.String(version.Phase0), Version: version.String(version.Phase0),
ExecutionPayloadBlinded: false, ExecutionPayloadBlinded: false,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point
ConsensusBlockValue: "", // rewards not applicable before altair
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -178,10 +238,12 @@ func handleProduceAltairV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_Altair, blk *eth.GenericBeaconBlock_Altair,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceAltairV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceAltairV3")
defer span.End() defer span.End()
if isSSZ { if isSSZ {
sszResp, err := blk.Altair.MarshalSSZ() sszResp, err := blk.Altair.MarshalSSZ()
if err != nil { if err != nil {
@@ -204,7 +266,8 @@ func handleProduceAltairV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Altair), Version: version.String(version.Altair),
ExecutionPayloadBlinded: false, ExecutionPayloadBlinded: false,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue), // mev not available at this point
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -214,7 +277,8 @@ func handleProduceBellatrixV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_Bellatrix, blk *eth.GenericBeaconBlock_Bellatrix,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBellatrixV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBellatrixV3")
defer span.End() defer span.End()
@@ -240,7 +304,8 @@ func handleProduceBellatrixV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Bellatrix), Version: version.String(version.Bellatrix),
ExecutionPayloadBlinded: false, ExecutionPayloadBlinded: false,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue), // mev not available at this point
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -250,7 +315,8 @@ func handleProduceBlindedBellatrixV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_BlindedBellatrix, blk *eth.GenericBeaconBlock_BlindedBellatrix,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedBellatrixV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedBellatrixV3")
defer span.End() defer span.End()
@@ -276,7 +342,8 @@ func handleProduceBlindedBellatrixV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Bellatrix), Version: version.String(version.Bellatrix),
ExecutionPayloadBlinded: true, ExecutionPayloadBlinded: true,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue),
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -286,7 +353,8 @@ func handleProduceBlindedCapellaV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_BlindedCapella, blk *eth.GenericBeaconBlock_BlindedCapella,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedCapellaV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedCapellaV3")
defer span.End() defer span.End()
@@ -312,7 +380,8 @@ func handleProduceBlindedCapellaV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Capella), Version: version.String(version.Capella),
ExecutionPayloadBlinded: true, ExecutionPayloadBlinded: true,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue),
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -322,7 +391,8 @@ func handleProduceCapellaV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_Capella, blk *eth.GenericBeaconBlock_Capella,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceCapellaV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceCapellaV3")
defer span.End() defer span.End()
@@ -348,7 +418,8 @@ func handleProduceCapellaV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Capella), Version: version.String(version.Capella),
ExecutionPayloadBlinded: false, ExecutionPayloadBlinded: false,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue), // mev not available at this point
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -358,7 +429,8 @@ func handleProduceBlindedDenebV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_BlindedDeneb, blk *eth.GenericBeaconBlock_BlindedDeneb,
payloadValue uint64, executionPayloadValue uint64,
consensusPayloadValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedDenebV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceBlindedDenebV3")
defer span.End() defer span.End()
@@ -384,7 +456,8 @@ func handleProduceBlindedDenebV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Deneb), Version: version.String(version.Deneb),
ExecutionPayloadBlinded: true, ExecutionPayloadBlinded: true,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue),
ConsensusBlockValue: consensusPayloadValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }
@@ -394,7 +467,8 @@ func handleProduceDenebV3(
w http.ResponseWriter, w http.ResponseWriter,
isSSZ bool, isSSZ bool,
blk *eth.GenericBeaconBlock_Deneb, blk *eth.GenericBeaconBlock_Deneb,
payloadValue uint64, executionPayloadValue uint64,
consensusBlockValue string,
) { ) {
_, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceDenebV3") _, span := trace.StartSpan(ctx, "validator.ProduceBlockV3.internal.handleProduceDenebV3")
defer span.End() defer span.End()
@@ -420,7 +494,8 @@ func handleProduceDenebV3(
http2.WriteJson(w, &ProduceBlockV3Response{ http2.WriteJson(w, &ProduceBlockV3Response{
Version: version.String(version.Deneb), Version: version.String(version.Deneb),
ExecutionPayloadBlinded: false, ExecutionPayloadBlinded: false,
ExecutionPayloadValue: fmt.Sprintf("%d", payloadValue), // mev not available at this point ExecutionPayloadValue: fmt.Sprintf("%d", executionPayloadValue), // mev not available at this point
ConsensusBlockValue: consensusBlockValue,
Data: jsonBytes, Data: jsonBytes,
}) })
} }

View File

@@ -12,6 +12,8 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/prysmaticlabs/prysm/v4/api" "github.com/prysmaticlabs/prysm/v4/api"
blockchainTesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing" blockchainTesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards"
rewardtesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing" rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing"
mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing" mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing"
@@ -46,26 +48,31 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"phase0","execution_payload_blinded":false,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"phase0","execution_payload_blinded":false,"execution_payload_value":"0","consensus_block_value":"","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "", true)
}) })
t.Run("Altair", func(t *testing.T) { t.Run("Altair", func(t *testing.T) {
var block *shared.SignedBeaconBlockAltair var block *shared.SignedBeaconBlockAltair
err := json.Unmarshal([]byte(rpctesting.AltairBlock), &block) err := json.Unmarshal([]byte(rpctesting.AltairBlock), &block)
require.NoError(t, err) require.NoError(t, err)
jsonBytes, err := json.Marshal(block.Message) jsonBytes, err := json.Marshal(block.Message)
require.NoError(t, err) require.NoError(t, err)
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), gomock.Any()).Return( v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), gomock.Any()).Return(
func() (*eth.GenericBeaconBlock, error) { func() (*eth.GenericBeaconBlock, error) {
return block.Message.ToGeneric() return block.Message.ToGeneric()
}()) }())
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -74,11 +81,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"altair","execution_payload_blinded":false,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"altair","execution_payload_blinded":false,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Bellatrix", func(t *testing.T) { t.Run("Bellatrix", func(t *testing.T) {
var block *shared.SignedBeaconBlockBellatrix var block *shared.SignedBeaconBlockBellatrix
@@ -92,10 +100,12 @@ func TestProduceBlockV3(t *testing.T) {
return block.Message.ToGeneric() return block.Message.ToGeneric()
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -104,11 +114,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"bellatrix","execution_payload_blinded":false,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"bellatrix","execution_payload_blinded":false,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("BlindedBellatrix", func(t *testing.T) { t.Run("BlindedBellatrix", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockBellatrix var block *shared.SignedBlindedBeaconBlockBellatrix
@@ -122,10 +133,12 @@ func TestProduceBlockV3(t *testing.T) {
return block.Message.ToGeneric() return block.Message.ToGeneric()
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -134,11 +147,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"bellatrix","execution_payload_blinded":true,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"bellatrix","execution_payload_blinded":true,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Capella", func(t *testing.T) { t.Run("Capella", func(t *testing.T) {
var block *shared.SignedBeaconBlockCapella var block *shared.SignedBeaconBlockCapella
@@ -152,10 +166,12 @@ func TestProduceBlockV3(t *testing.T) {
return block.Message.ToGeneric() return block.Message.ToGeneric()
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -164,11 +180,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"capella","execution_payload_blinded":false,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"capella","execution_payload_blinded":false,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Blinded Capella", func(t *testing.T) { t.Run("Blinded Capella", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockCapella var block *shared.SignedBlindedBeaconBlockCapella
@@ -185,10 +202,12 @@ func TestProduceBlockV3(t *testing.T) {
return g, err return g, err
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -197,11 +216,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"capella","execution_payload_blinded":true,"execution_payload_value":"2000","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"capella","execution_payload_blinded":true,"execution_payload_value":"2000","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "2000", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "2000", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Deneb", func(t *testing.T) { t.Run("Deneb", func(t *testing.T) {
var block *shared.SignedBeaconBlockContentsDeneb var block *shared.SignedBeaconBlockContentsDeneb
@@ -215,10 +235,12 @@ func TestProduceBlockV3(t *testing.T) {
return block.ToUnsigned().ToGeneric() return block.ToUnsigned().ToGeneric()
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -227,11 +249,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"deneb","execution_payload_blinded":false,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"deneb","execution_payload_blinded":false,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Blinded Deneb", func(t *testing.T) { t.Run("Blinded Deneb", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockContentsDeneb var block *shared.SignedBlindedBeaconBlockContentsDeneb
@@ -245,10 +268,12 @@ func TestProduceBlockV3(t *testing.T) {
return block.ToUnsigned().ToGeneric() return block.ToUnsigned().ToGeneric()
}()) }())
mockChainService := &blockchainTesting.ChainService{} mockChainService := &blockchainTesting.ChainService{}
mockRewards := &rewards.BlockRewards{Total: "10"}
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -257,11 +282,12 @@ func TestProduceBlockV3(t *testing.T) {
writer.Body = &bytes.Buffer{} writer.Body = &bytes.Buffer{}
server.ProduceBlockV3(writer, request) server.ProduceBlockV3(writer, request)
assert.Equal(t, http.StatusOK, writer.Code) assert.Equal(t, http.StatusOK, writer.Code)
want := fmt.Sprintf(`{"version":"deneb","execution_payload_blinded":true,"execution_payload_value":"0","data":%s}`, string(jsonBytes)) want := fmt.Sprintf(`{"version":"deneb","execution_payload_blinded":true,"execution_payload_value":"0","consensus_block_value":"10","data":%s}`, string(jsonBytes))
body := strings.ReplaceAll(writer.Body.String(), "\n", "") body := strings.ReplaceAll(writer.Body.String(), "\n", "")
require.Equal(t, want, body) require.Equal(t, want, body)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("invalid query parameter slot empty", func(t *testing.T) { t.Run("invalid query parameter slot empty", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
@@ -321,6 +347,7 @@ func TestProduceBlockV3(t *testing.T) {
func TestProduceBlockV3SSZ(t *testing.T) { func TestProduceBlockV3SSZ(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
mockRewards := &rewards.BlockRewards{Total: "10"}
t.Run("Phase 0", func(t *testing.T) { t.Run("Phase 0", func(t *testing.T) {
var block *shared.SignedBeaconBlock var block *shared.SignedBeaconBlock
err := json.Unmarshal([]byte(rpctesting.Phase0Block), &block) err := json.Unmarshal([]byte(rpctesting.Phase0Block), &block)
@@ -351,6 +378,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "", true)
}) })
t.Run("Altair", func(t *testing.T) { t.Run("Altair", func(t *testing.T) {
var block *shared.SignedBeaconBlockAltair var block *shared.SignedBeaconBlockAltair
@@ -361,9 +389,11 @@ func TestProduceBlockV3SSZ(t *testing.T) {
func() (*eth.GenericBeaconBlock, error) { func() (*eth.GenericBeaconBlock, error) {
return block.Message.ToGeneric() return block.Message.ToGeneric()
}()) }())
server := &Server{ server := &Server{
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -382,6 +412,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Bellatrix", func(t *testing.T) { t.Run("Bellatrix", func(t *testing.T) {
var block *shared.SignedBeaconBlockBellatrix var block *shared.SignedBeaconBlockBellatrix
@@ -397,6 +428,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -415,6 +447,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("BlindedBellatrix", func(t *testing.T) { t.Run("BlindedBellatrix", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockBellatrix var block *shared.SignedBlindedBeaconBlockBellatrix
@@ -430,6 +463,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -448,6 +482,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Capella", func(t *testing.T) { t.Run("Capella", func(t *testing.T) {
var block *shared.SignedBeaconBlockCapella var block *shared.SignedBeaconBlockCapella
@@ -463,6 +498,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -481,6 +517,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Blinded Capella", func(t *testing.T) { t.Run("Blinded Capella", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockCapella var block *shared.SignedBlindedBeaconBlockCapella
@@ -499,6 +536,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -517,6 +555,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "2000", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "2000", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Deneb", func(t *testing.T) { t.Run("Deneb", func(t *testing.T) {
var block *shared.SignedBeaconBlockContentsDeneb var block *shared.SignedBeaconBlockContentsDeneb
@@ -532,6 +571,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -550,6 +590,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "false", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
t.Run("Blinded Deneb", func(t *testing.T) { t.Run("Blinded Deneb", func(t *testing.T) {
var block *shared.SignedBlindedBeaconBlockContentsDeneb var block *shared.SignedBlindedBeaconBlockContentsDeneb
@@ -565,6 +606,7 @@ func TestProduceBlockV3SSZ(t *testing.T) {
V1Alpha1Server: v1alpha1Server, V1Alpha1Server: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false}, SyncChecker: &mockSync.Sync{IsSyncing: false},
OptimisticModeFetcher: mockChainService, OptimisticModeFetcher: mockChainService,
BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: mockRewards},
} }
rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + rr := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +
"&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" "&graffiti=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -583,5 +625,6 @@ func TestProduceBlockV3SSZ(t *testing.T) {
require.Equal(t, string(ssz), writer.Body.String()) require.Equal(t, string(ssz), writer.Body.String())
require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadBlindedHeader) == "true", true)
require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true) require.Equal(t, writer.Header().Get(api.ExecutionPayloadValueHeader) == "0", true)
require.Equal(t, writer.Header().Get(api.ConsensusBlockValueHeader) == "10", true)
}) })
} }

View File

@@ -10,6 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/synccommittee" "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/rewards"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/sync" "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -34,4 +35,5 @@ type Server struct {
BlockBuilder builder.BlockBuilder BlockBuilder builder.BlockBuilder
OperationNotifier operation.Notifier OperationNotifier operation.Notifier
CoreService *core.Service CoreService *core.Service
BlockRewardFetcher rewards.BlockRewardsFetcher
} }

View File

@@ -78,6 +78,7 @@ type ProduceBlockV3Response struct {
Version string `json:"version"` Version string `json:"version"`
ExecutionPayloadBlinded bool `json:"execution_payload_blinded"` ExecutionPayloadBlinded bool `json:"execution_payload_blinded"`
ExecutionPayloadValue string `json:"execution_payload_value"` ExecutionPayloadValue string `json:"execution_payload_value"`
ConsensusBlockValue string `json:"consensus_block_value"`
Data json.RawMessage `json:"data"` // represents the block values based on the version Data json.RawMessage `json:"data"` // represents the block values based on the version
} }

View File

@@ -211,15 +211,15 @@ func (s *Service) Start() {
BeaconDB: s.cfg.BeaconDB, BeaconDB: s.cfg.BeaconDB,
ChainInfoFetcher: s.cfg.ChainInfoFetcher, ChainInfoFetcher: s.cfg.ChainInfoFetcher,
} }
rewardFetcher := &rewards.BlockRewardService{Replayer: ch}
rewardsServer := &rewards.Server{ rewardsServer := &rewards.Server{
Blocker: blocker, Blocker: blocker,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
FinalizationFetcher: s.cfg.FinalizationFetcher, FinalizationFetcher: s.cfg.FinalizationFetcher,
ReplayerBuilder: ch,
TimeFetcher: s.cfg.GenesisTimeFetcher, TimeFetcher: s.cfg.GenesisTimeFetcher,
Stater: stater, Stater: stater,
HeadFetcher: s.cfg.HeadFetcher, HeadFetcher: s.cfg.HeadFetcher,
BlockRewardFetcher: rewardFetcher,
} }
s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/blocks/{block_id}", rewardsServer.BlockRewards).Methods(http.MethodGet) s.cfg.Router.HandleFunc("/eth/v1/beacon/rewards/blocks/{block_id}", rewardsServer.BlockRewards).Methods(http.MethodGet)
@@ -304,6 +304,7 @@ func (s *Service) Start() {
BlockBuilder: s.cfg.BlockBuilder, BlockBuilder: s.cfg.BlockBuilder,
OperationNotifier: s.cfg.OperationNotifier, OperationNotifier: s.cfg.OperationNotifier,
CoreService: coreService, CoreService: coreService,
BlockRewardFetcher: rewardFetcher,
} }
s.cfg.Router.HandleFunc("/eth/v1/validator/aggregate_attestation", validatorServerV1.GetAggregateAttestation).Methods(http.MethodGet) s.cfg.Router.HandleFunc("/eth/v1/validator/aggregate_attestation", validatorServerV1.GetAggregateAttestation).Methods(http.MethodGet)

View File

@@ -73,7 +73,8 @@ func AttestingIndices(bf bitfield.Bitfield, committee []primitives.ValidatorInde
return nil, fmt.Errorf("bitfield length %d is not equal to committee length %d", bf.Len(), len(committee)) return nil, fmt.Errorf("bitfield length %d is not equal to committee length %d", bf.Len(), len(committee))
} }
indices := make([]uint64, 0, bf.Count()) indices := make([]uint64, 0, bf.Count())
for _, idx := range bf.BitIndices() { p := bf.BitIndices()
for _, idx := range p {
if idx < len(committee) { if idx < len(committee) {
indices = append(indices, uint64(committee[idx])) indices = append(indices, uint64(committee[idx]))
} }
@@ -104,6 +105,7 @@ func VerifyIndexedAttestationSig(ctx context.Context, indexedAtt *ethpb.IndexedA
ctx, span := trace.StartSpan(ctx, "attestationutil.VerifyIndexedAttestationSig") ctx, span := trace.StartSpan(ctx, "attestationutil.VerifyIndexedAttestationSig")
defer span.End() defer span.End()
indices := indexedAtt.AttestingIndices indices := indexedAtt.AttestingIndices
messageHash, err := signing.ComputeSigningRoot(indexedAtt.Data, domain) messageHash, err := signing.ComputeSigningRoot(indexedAtt.Data, domain)
if err != nil { if err != nil {
return errors.Wrap(err, "could not get signing root of object") return errors.Wrap(err, "could not get signing root of object")