Compare commits

...

12 Commits

Author SHA1 Message Date
Kasey Kirkham
817d30844b wip 2024-05-07 23:20:37 -05:00
Kasey Kirkham
59ce7542df reshuffling 2024-05-07 22:59:50 -05:00
Kasey Kirkham
0a7acc88a5 wip 2024-05-07 22:48:11 -05:00
Kasey Kirkham
71ab24806b rm cruft 2024-05-07 17:02:31 -05:00
Kasey Kirkham
eed9e61297 WIP 2024-05-07 16:41:02 -05:00
Kasey Kirkham
371b6435a4 unbundle bid from payload 2024-05-03 18:08:06 -05:00
Kasey Kirkham
74e49b4062 wip 2024-05-03 12:17:15 -05:00
Kasey Kirkham
d5d6e9e1f1 rm pb getters from electra payload 2024-05-02 18:09:28 -05:00
Kasey Kirkham
5b2f60f706 deepsource 2024-05-02 17:39:01 -05:00
Kasey Kirkham
ff10f906ad use electra body in block factory 2024-05-02 17:17:58 -05:00
Kasey Kirkham
3610879b56 add electra to wrapped payload switch 2024-05-02 17:06:21 -05:00
Kasey Kirkham
e1620c96f6 fork-specific interface for electra 2024-05-02 17:05:20 -05:00
40 changed files with 936 additions and 1236 deletions

View File

@@ -6,6 +6,7 @@ import (
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
@@ -165,7 +166,7 @@ func WrappedBuilderBidCapella(p *ethpb.BuilderBidCapella) (Bid, error) {
// Header returns the execution data interface.
func (b builderBidCapella) Header() (interfaces.ExecutionData, error) {
// We have to convert big endian to little endian because the value is coming from the execution layer.
return blocks.WrappedExecutionPayloadHeaderCapella(b.p.Header, blocks.PayloadValueToWei(b.p.Value))
return blocks.WrappedExecutionPayloadHeaderCapella(b.p.Header, math.BigEndianBytesToWei(b.p.Value))
}
// BlobKzgCommitments --
@@ -249,7 +250,7 @@ func (b builderBidDeneb) HashTreeRootWith(hh *ssz.Hasher) error {
// Header --
func (b builderBidDeneb) Header() (interfaces.ExecutionData, error) {
// We have to convert big endian to little endian because the value is coming from the execution layer.
return blocks.WrappedExecutionPayloadHeaderDeneb(b.p.Header, blocks.PayloadValueToWei(b.p.Value))
return blocks.WrappedExecutionPayloadHeaderDeneb(b.p.Header, math.BigEndianBytesToWei(b.p.Value))
}
// BlobKzgCommitments --

View File

@@ -330,7 +330,7 @@ func (c *Client) SubmitBlindedBlock(ctx context.Context, sb interfaces.ReadOnlyS
if err != nil {
return nil, nil, err
}
ed, err := blocks.NewWrappedExecutionData(pb, nil)
ed, err := blocks.NewWrappedExecutionData(pb)
if err != nil {
return nil, nil, err
}

View File

@@ -47,6 +47,7 @@ go_library(
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//io/logs:go_default_library",
"//math:go_default_library",
"//monitoring/clientstats:go_default_library",
"//monitoring/tracing:go_default_library",
"//network:go_default_library",
@@ -115,6 +116,7 @@ go_test(
"//contracts/deposit/mock:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//monitoring/clientstats:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -22,6 +22,7 @@ import (
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
@@ -101,7 +102,7 @@ type EngineCaller interface {
ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs payloadattribute.Attributer,
) (*pb.PayloadIDBytes, []byte, error)
GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (interfaces.ExecutionData, *pb.BlobsBundle, bool, error)
GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (interfaces.ExecutionData, math.Wei, *pb.BlobsBundle, bool, error)
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
}
@@ -242,7 +243,7 @@ func (s *Service) ForkchoiceUpdated(
// GetPayload calls the engine_getPayloadVX method via JSON-RPC.
// It returns the execution data as well as the blobs bundle.
func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (interfaces.ExecutionData, *pb.BlobsBundle, bool, error) {
func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (interfaces.ExecutionData, math.Wei, *pb.BlobsBundle, bool, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetPayload")
defer span.End()
start := time.Now()
@@ -258,38 +259,38 @@ func (s *Service) GetPayload(ctx context.Context, payloadId [8]byte, slot primit
result := &pb.ExecutionPayloadDenebWithValueAndBlobsBundle{}
err := s.rpcClient.CallContext(ctx, result, GetPayloadMethodV3, pb.PayloadIDBytes(payloadId))
if err != nil {
return nil, nil, false, handleRPCError(err)
return nil, math.ZeroWei, nil, false, handleRPCError(err)
}
ed, err := blocks.WrappedExecutionPayloadDeneb(result.Payload, blocks.PayloadValueToWei(result.Value))
ed, err := blocks.WrappedExecutionPayloadDeneb(result.Payload, math.ZeroWei)
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return ed, result.BlobsBundle, result.ShouldOverrideBuilder, nil
return ed, math.BigEndianBytesToWei(result.Value), result.BlobsBundle, result.ShouldOverrideBuilder, nil
}
if slots.ToEpoch(slot) >= params.BeaconConfig().CapellaForkEpoch {
result := &pb.ExecutionPayloadCapellaWithValue{}
err := s.rpcClient.CallContext(ctx, result, GetPayloadMethodV2, pb.PayloadIDBytes(payloadId))
if err != nil {
return nil, nil, false, handleRPCError(err)
return nil, math.ZeroWei, nil, false, handleRPCError(err)
}
ed, err := blocks.WrappedExecutionPayloadCapella(result.Payload, blocks.PayloadValueToWei(result.Value))
ed, err := blocks.WrappedExecutionPayloadCapella(result.Payload, math.ZeroWei)
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return ed, nil, false, nil
return ed, math.BigEndianBytesToWei(result.Value), nil, false, nil
}
result := &pb.ExecutionPayload{}
err := s.rpcClient.CallContext(ctx, result, GetPayloadMethod, pb.PayloadIDBytes(payloadId))
if err != nil {
return nil, nil, false, handleRPCError(err)
return nil, math.ZeroWei, nil, false, handleRPCError(err)
}
ed, err := blocks.WrappedExecutionPayload(result)
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return ed, nil, false, nil
return ed, math.ZeroWei, nil, false, nil
}
func (s *Service) ExchangeCapabilities(ctx context.Context) ([]string, error) {

View File

@@ -25,6 +25,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
@@ -72,7 +73,7 @@ func TestClient_IPC(t *testing.T) {
want, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
payloadId := [8]byte{1}
resp, _, override, err := srv.GetPayload(ctx, payloadId, 1)
resp, _, _, override, err := srv.GetPayload(ctx, payloadId, 1)
require.NoError(t, err)
require.Equal(t, false, override)
pbs := resp.Proto()
@@ -85,7 +86,7 @@ func TestClient_IPC(t *testing.T) {
want, ok := fix["ExecutionPayloadCapellaWithValue"].(*pb.ExecutionPayloadCapellaWithValue)
require.Equal(t, true, ok)
payloadId := [8]byte{1}
resp, _, override, err := srv.GetPayload(ctx, payloadId, params.BeaconConfig().SlotsPerEpoch)
resp, _, _, override, err := srv.GetPayload(ctx, payloadId, params.BeaconConfig().SlotsPerEpoch)
require.NoError(t, err)
require.Equal(t, false, override)
pbs := resp.Proto()
@@ -200,7 +201,7 @@ func TestClient_HTTP(t *testing.T) {
client.rpcClient = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, _, override, err := client.GetPayload(ctx, payloadId, 1)
resp, _, _, override, err := client.GetPayload(ctx, payloadId, 1)
require.NoError(t, err)
require.Equal(t, false, override)
pbs := resp.Proto()
@@ -246,7 +247,7 @@ func TestClient_HTTP(t *testing.T) {
client.rpcClient = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, _, override, err := client.GetPayload(ctx, payloadId, params.BeaconConfig().SlotsPerEpoch)
resp, bid, _, override, err := client.GetPayload(ctx, payloadId, params.BeaconConfig().SlotsPerEpoch)
require.NoError(t, err)
require.Equal(t, false, override)
pbs := resp.Proto()
@@ -259,9 +260,7 @@ func TestClient_HTTP(t *testing.T) {
require.DeepEqual(t, want.ExecutionPayload.PrevRandao.Bytes(), ep.PrevRandao)
require.DeepEqual(t, want.ExecutionPayload.ParentHash.Bytes(), ep.ParentHash)
v, err := resp.ValueInGwei()
require.NoError(t, err)
require.Equal(t, uint64(1236), v)
require.Equal(t, math.Gwei(1236), math.WeiToGwei(bid))
})
t.Run(GetPayloadMethodV3, func(t *testing.T) {
payloadId := [8]byte{1}
@@ -301,7 +300,7 @@ func TestClient_HTTP(t *testing.T) {
client.rpcClient = rpcClient
// We call the RPC method via HTTP and expect a proper result.
resp, blobsBundle, override, err := client.GetPayload(ctx, payloadId, 2*params.BeaconConfig().SlotsPerEpoch)
resp, _, blobsBundle, override, err := client.GetPayload(ctx, payloadId, 2*params.BeaconConfig().SlotsPerEpoch)
require.NoError(t, err)
require.Equal(t, true, override)
g, err := resp.ExcessBlobGas()

View File

@@ -23,6 +23,7 @@ go_library(
"//consensus-types/payload-attribute:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",

View File

@@ -14,6 +14,7 @@ import (
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
@@ -60,26 +61,26 @@ func (e *EngineClient) ForkchoiceUpdated(
}
// GetPayload --
func (e *EngineClient) GetPayload(_ context.Context, _ [8]byte, s primitives.Slot) (interfaces.ExecutionData, *pb.BlobsBundle, bool, error) {
func (e *EngineClient) GetPayload(_ context.Context, _ [8]byte, s primitives.Slot) (interfaces.ExecutionData, math.Wei, *pb.BlobsBundle, bool, error) {
if slots.ToEpoch(s) >= params.BeaconConfig().DenebForkEpoch {
ed, err := blocks.WrappedExecutionPayloadDeneb(e.ExecutionPayloadDeneb, big.NewInt(int64(e.BlockValue)))
ed, err := blocks.WrappedExecutionPayloadDeneb(e.ExecutionPayloadDeneb, big.NewInt(0))
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return ed, e.BlobsBundle, e.BuilderOverride, nil
return ed, math.Uint64ToWei(e.BlockValue), e.BlobsBundle, e.BuilderOverride, nil
}
if slots.ToEpoch(s) >= params.BeaconConfig().CapellaForkEpoch {
ed, err := blocks.WrappedExecutionPayloadCapella(e.ExecutionPayloadCapella, big.NewInt(int64(e.BlockValue)))
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return ed, nil, e.BuilderOverride, nil
return ed, math.Uint64ToWei(e.BlockValue), e.BlobsBundle, e.BuilderOverride, nil
}
p, err := blocks.WrappedExecutionPayload(e.ExecutionPayload)
if err != nil {
return nil, nil, false, err
return nil, math.ZeroWei, nil, false, err
}
return p, nil, e.BuilderOverride, e.ErrGetPayload
return p, math.ZeroWei, nil, e.BuilderOverride, e.ErrGetPayload
}
// LatestExecutionBlock --

View File

@@ -23,6 +23,7 @@ go_library(
"proposer_execution_payload.go",
"proposer_exits.go",
"proposer_slashings.go",
"proposer_response_construct.go",
"proposer_sync_aggregate.go",
"server.go",
"status.go",

View File

@@ -1,77 +1 @@
package validator
import (
"fmt"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"google.golang.org/protobuf/proto"
)
// constructGenericBeaconBlock constructs a `GenericBeaconBlock` based on the block version and other parameters.
func (vs *Server) constructGenericBeaconBlock(sBlk interfaces.SignedBeaconBlock, blobsBundle *enginev1.BlobsBundle) (*ethpb.GenericBeaconBlock, error) {
if sBlk == nil || sBlk.Block() == nil {
return nil, fmt.Errorf("block cannot be nil")
}
blockProto, err := sBlk.Block().Proto()
if err != nil {
return nil, err
}
isBlinded := sBlk.IsBlinded()
payloadValue := sBlk.ValueInWei()
switch sBlk.Version() {
case version.Deneb:
return vs.constructDenebBlock(blockProto, isBlinded, payloadValue, blobsBundle), nil
case version.Capella:
return vs.constructCapellaBlock(blockProto, isBlinded, payloadValue), nil
case version.Bellatrix:
return vs.constructBellatrixBlock(blockProto, isBlinded, payloadValue), nil
case version.Altair:
return vs.constructAltairBlock(blockProto), nil
case version.Phase0:
return vs.constructPhase0Block(blockProto), nil
default:
return nil, fmt.Errorf("unknown block version: %d", sBlk.Version())
}
}
// Helper functions for constructing blocks for each version
func (vs *Server) constructDenebBlock(blockProto proto.Message, isBlinded bool, payloadValue math.Wei, bundle *enginev1.BlobsBundle) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blockProto.(*ethpb.BlindedBeaconBlockDeneb)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
}
denebContents := &ethpb.BeaconBlockContentsDeneb{Block: blockProto.(*ethpb.BeaconBlockDeneb)}
if bundle != nil {
denebContents.KzgProofs = bundle.Proofs
denebContents.Blobs = bundle.Blobs
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: denebContents}, IsBlinded: false, PayloadValue: (*payloadValue).String()}
}
func (vs *Server) constructCapellaBlock(pb proto.Message, isBlinded bool, payloadValue math.Wei) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: pb.(*ethpb.BeaconBlockCapella)}, IsBlinded: false, PayloadValue: (*payloadValue).String()}
}
func (vs *Server) constructBellatrixBlock(pb proto.Message, isBlinded bool, payloadValue math.Wei) *ethpb.GenericBeaconBlock {
if isBlinded {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb.(*ethpb.BeaconBlockBellatrix)}, IsBlinded: false, PayloadValue: (*payloadValue).String()}
}
func (vs *Server) constructAltairBlock(pb proto.Message) *ethpb.GenericBeaconBlock {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: pb.(*ethpb.BeaconBlockAltair)}}
}
func (vs *Server) constructPhase0Block(pb proto.Message) *ethpb.GenericBeaconBlock {
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Phase0{Phase0: pb.(*ethpb.BeaconBlock)}}
}

View File

@@ -1,23 +1,11 @@
package validator
import (
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
/*
func TestConstructGenericBeaconBlock(t *testing.T) {
vs := &Server{}
// Test when sBlk or sBlk.Block() is nil
t.Run("NilBlock", func(t *testing.T) {
_, err := vs.constructGenericBeaconBlock(nil, nil)
_, err := constructGenericBeaconBlock(nil, nil, nil)
require.ErrorIs(t, err, blocks.ErrIsNil)
require.ErrorContains(t, "block cannot be nil", err)
})
@@ -45,7 +33,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
contents := &eth.BeaconBlockContentsDeneb{Block: eb.Block, KzgProofs: bundle.Proofs, Blobs: bundle.Blobs}
r1, err := contents.HashTreeRoot()
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, bundle)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, bundle)
require.NoError(t, err)
r2, err := result.GetDeneb().HashTreeRoot()
require.NoError(t, err)
@@ -60,7 +48,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
r1, err := b.Block().HashTreeRoot()
require.NoError(t, err)
scs := &enginev1.BlobsBundle{}
result, err := vs.constructGenericBeaconBlock(b, scs)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, scs)
require.NoError(t, err)
r2, err := result.GetBlindedDeneb().HashTreeRoot()
require.NoError(t, err)
@@ -73,7 +61,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
t.Run("capella block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, nil)
require.NoError(t, err)
r1, err := result.GetCapella().HashTreeRoot()
require.NoError(t, err)
@@ -87,7 +75,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
t.Run("blind capella block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, nil)
require.NoError(t, err)
r1, err := result.GetBlindedCapella().HashTreeRoot()
require.NoError(t, err)
@@ -101,7 +89,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
t.Run("bellatrix block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, nil)
require.NoError(t, err)
r1, err := result.GetBellatrix().HashTreeRoot()
require.NoError(t, err)
@@ -115,7 +103,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
t.Run("altair block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockAltair())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, nil)
require.NoError(t, err)
r1, err := result.GetAltair().HashTreeRoot()
require.NoError(t, err)
@@ -129,7 +117,7 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
t.Run("phase0 block", func(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
result, err := vs.constructGenericBeaconBlock(b, nil)
result, err := constructGenericBeaconBlock(b, math.ZeroWei, nil)
require.NoError(t, err)
r1, err := result.GetPhase0().HashTreeRoot()
require.NoError(t, err)
@@ -139,3 +127,6 @@ func TestConstructGenericBeaconBlock(t *testing.T) {
require.Equal(t, result.IsBlinded, false)
})
}
*/

View File

@@ -73,46 +73,52 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
if err != nil {
return nil, err
}
sBlk, err := getEmptyBlock(req.Slot)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare block: %v", err)
}
// Set slot, graffiti, randao reveal, and parent root.
sBlk.SetSlot(req.Slot)
sBlk.SetGraffiti(req.Graffiti)
sBlk.SetRandaoReveal(req.RandaoReveal)
sBlk.SetParentRoot(parentRoot[:])
// Set proposer index.
idx, err := helpers.BeaconProposerIndex(ctx, head)
if err != nil {
return nil, fmt.Errorf("could not calculate proposer index %v", err)
}
val, tracked := vs.TrackedValidatorsCache.Validator(idx)
if !tracked {
logrus.WithFields(logrus.Fields{
"validatorIndex": idx,
"slot": req.Slot,
"headRoot": fmt.Sprintf("%#x", parentRoot),
}).Warn("could not find tracked proposer index")
}
sBlk, err := getEmptyBlock(req.Slot)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not completeWithBest block: %v", err)
}
// Set values from request & head: slot, graffiti, randao reveal, and parent root.
sBlk.SetSlot(req.Slot)
sBlk.SetProposerIndex(idx)
sBlk.SetGraffiti(req.Graffiti)
sBlk.SetRandaoReveal(req.RandaoReveal)
sBlk.SetParentRoot(parentRoot[:])
defer func() {
log.WithFields(logrus.Fields{
"slot": req.Slot,
"sinceSlotStartTime": time.Since(t),
"validator": sBlk.Block().ProposerIndex(),
}).Info("Finished building block")
}()
builderBoostFactor := defaultBuilderBoostFactor
if req.BuilderBoostFactor != nil {
builderBoostFactor = req.BuilderBoostFactor.Value
}
return newProposalResponseConstructor(sBlk, head, defaultIfBurnAddress(val)).buildBlockParallel(ctx, vs, req.SkipMevBoost, builderBoostFactor)
}
if err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost, builderBoostFactor); err != nil {
return nil, errors.Wrap(err, "could not build block in parallel")
func defaultIfBurnAddress(val cache.TrackedValidator) primitives.ExecutionAddress {
if val.FeeRecipient == [20]byte{} && val.Index == 0 {
return primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
}
sr, err := vs.computeStateRoot(ctx, sBlk)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not compute state root: %v", err)
}
sBlk.SetStateRoot(sr)
log.WithFields(logrus.Fields{
"slot": req.Slot,
"sinceSlotStartTime": time.Since(t),
"validator": sBlk.Block().ProposerIndex(),
}).Info("Finished building block")
// Blob cache is updated after BuildBlockParallel
return vs.constructGenericBeaconBlock(sBlk, bundleCache.get(req.Slot))
return val.FeeRecipient
}
func (vs *Server) handleSuccesfulReorgAttempt(ctx context.Context, slot primitives.Slot, parentRoot, headRoot [32]byte) (state.BeaconState, error) {
@@ -183,79 +189,6 @@ func (vs *Server) getParentState(ctx context.Context, slot primitives.Slot) (sta
return head, parentRoot, err
}
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool, builderBoostFactor uint64) error {
// Build consensus fields in background
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// Set eth1 data.
eth1Data, err := vs.eth1DataMajorityVote(ctx, head)
if err != nil {
eth1Data = &ethpb.Eth1Data{DepositRoot: params.BeaconConfig().ZeroHash[:], BlockHash: params.BeaconConfig().ZeroHash[:]}
log.WithError(err).Error("Could not get eth1data")
}
sBlk.SetEth1Data(eth1Data)
// Set deposit and attestation.
deposits, atts, err := vs.packDepositsAndAttestations(ctx, head, eth1Data) // TODO: split attestations and deposits
if err != nil {
sBlk.SetDeposits([]*ethpb.Deposit{})
if err := sBlk.SetAttestations([]interfaces.Attestation{}); err != nil {
log.WithError(err).Error("Could not set attestations on block")
}
log.WithError(err).Error("Could not pack deposits and attestations")
} else {
sBlk.SetDeposits(deposits)
if err := sBlk.SetAttestations(atts); err != nil {
log.WithError(err).Error("Could not set attestations on block")
}
}
// Set slashings.
validProposerSlashings, validAttSlashings := vs.getSlashings(ctx, head)
sBlk.SetProposerSlashings(validProposerSlashings)
if err := sBlk.SetAttesterSlashings(validAttSlashings); err != nil {
log.WithError(err).Error("Could not set attester slashings on block")
}
// Set exits.
sBlk.SetVoluntaryExits(vs.getExits(head, sBlk.Block().Slot()))
// Set sync aggregate. New in Altair.
vs.setSyncAggregate(ctx, sBlk)
// Set bls to execution change. New in Capella.
vs.setBlsToExecData(sBlk, head)
}()
localPayload, overrideBuilder, err := vs.getLocalPayload(ctx, sBlk.Block(), head)
if err != nil {
return status.Errorf(codes.Internal, "Could not get local payload: %v", err)
}
// There's no reason to try to get a builder bid if local override is true.
var builderPayload interfaces.ExecutionData
var builderKzgCommitments [][]byte
overrideBuilder = overrideBuilder || skipMevBoost // Skip using mev-boost if requested by the caller.
if !overrideBuilder {
builderPayload, builderKzgCommitments, err = vs.getBuilderPayloadAndBlobs(ctx, sBlk.Block().Slot(), sBlk.Block().ProposerIndex())
if err != nil {
builderGetPayloadMissCount.Inc()
log.WithError(err).Error("Could not get builder payload")
}
}
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload, builderKzgCommitments, builderBoostFactor); err != nil {
return status.Errorf(codes.Internal, "Could not set execution data: %v", err)
}
wg.Wait() // Wait until block is built via consensus and execution fields.
return nil
}
// ProposeBeaconBlock handles the proposal of beacon blocks.
func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeBeaconBlock")
@@ -459,26 +392,6 @@ func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.Fe
}, nil
}
// computeStateRoot computes the state root after a block has been processed through a state transition and
// returns it to the validator client.
func (vs *Server) computeStateRoot(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) {
beaconState, err := vs.StateGen.StateByRoot(ctx, block.Block().ParentRoot())
if err != nil {
return nil, errors.Wrap(err, "could not retrieve beacon state")
}
root, err := transition.CalculateStateRoot(
ctx,
beaconState,
block,
)
if err != nil {
return nil, errors.Wrapf(err, "could not calculate state root at slot %d", beaconState.Slot())
}
log.WithField("beaconStateRoot", fmt.Sprintf("%#x", root)).Debugf("Computed state root")
return root[:], nil
}
// SubmitValidatorRegistrations submits validator registrations.
func (vs *Server) SubmitValidatorRegistrations(ctx context.Context, reg *ethpb.SignedValidatorRegistrationsV1) (*emptypb.Empty, error) {
if vs.BlockBuilder == nil || !vs.BlockBuilder.Configured() {

View File

@@ -41,7 +41,7 @@ func (vs *Server) setSyncAggregate(ctx context.Context, blk interfaces.SignedBea
}
}
// getSyncAggregate retrieves the sync contributions from the pool to construct the sync aggregate object.
// getSyncAggregate retrieves the sync contributions from the pool to constructGenericBeaconBlock the sync aggregate object.
// The contributions are filtered based on matching of the input root and slot then profitability.
func (vs *Server) getSyncAggregate(ctx context.Context, slot primitives.Slot, root [32]byte) (*ethpb.SyncAggregate, error) {
_, span := trace.StartSpan(ctx, "ProposerServer.getSyncAggregate")

View File

@@ -2,268 +2,18 @@ package validator
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v5/api/client/builder"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v5/network/forks"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
var (
builderValueGweiGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "builder_value_gwei",
Help: "Builder payload value in gwei",
})
localValueGweiGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "local_value_gwei",
Help: "Local payload value in gwei",
})
builderGetPayloadMissCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "builder_get_payload_miss_count",
Help: "The number of get payload misses for validator requests to builder",
})
)
// emptyTransactionsRoot represents the returned value of ssz.TransactionsRoot([][]byte{}) and
// can be used as a constant to avoid recomputing this value in every call.
var emptyTransactionsRoot = [32]byte{127, 254, 36, 30, 166, 1, 135, 253, 176, 24, 123, 250, 34, 222, 53, 209, 249, 190, 215, 171, 6, 29, 148, 1, 253, 71, 227, 74, 84, 251, 237, 225}
// blockBuilderTimeout is the maximum amount of time allowed for a block builder to respond to a
// block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec.
const blockBuilderTimeout = 1 * time.Second
// Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions.
func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, localPayload, builderPayload interfaces.ExecutionData, builderKzgCommitments [][]byte, builderBoostFactor uint64) error {
_, span := trace.StartSpan(ctx, "ProposerServer.setExecutionData")
defer span.End()
slot := blk.Block().Slot()
if slots.ToEpoch(slot) < params.BeaconConfig().BellatrixForkEpoch {
return nil
}
if localPayload == nil {
return errors.New("local payload is nil")
}
// Use local payload if builder payload is nil.
if builderPayload == nil {
return setLocalExecution(blk, localPayload)
}
switch {
case blk.Version() >= version.Capella:
// Compare payload values between local and builder. Default to the local value if it is higher.
localValueGwei, err := localPayload.ValueInGwei()
if err != nil {
return errors.Wrap(err, "failed to get local payload value")
}
builderValueGwei, err := builderPayload.ValueInGwei()
if err != nil {
log.WithError(err).Warn("Proposer: failed to get builder payload value") // Default to local if can't get builder value.
return setLocalExecution(blk, localPayload)
}
withdrawalsMatched, err := matchingWithdrawalsRoot(localPayload, builderPayload)
if err != nil {
tracing.AnnotateError(span, err)
log.WithError(err).Warn("Proposer: failed to match withdrawals root")
return setLocalExecution(blk, localPayload)
}
// Use builder payload if the following in true:
// builder_bid_value * builderBoostFactor(default 100) > local_block_value * (local-block-value-boost + 100)
boost := params.BeaconConfig().LocalBlockValueBoost
higherValueBuilder := builderValueGwei*builderBoostFactor > localValueGwei*(100+boost)
if boost > 0 && builderBoostFactor != defaultBuilderBoostFactor {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: both local boost and builder boost are using non default values")
}
builderValueGweiGauge.Set(float64(builderValueGwei))
localValueGweiGauge.Set(float64(localValueGwei))
// If we can't get the builder value, just use local block.
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
return setLocalExecution(blk, localPayload)
} else {
return nil
}
}
if !higherValueBuilder {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: using local execution payload because higher value")
}
span.AddAttributes(
trace.BoolAttribute("higherValueBuilder", higherValueBuilder),
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderBoostFactor", int64(builderBoostFactor)), // lint:ignore uintcast -- This is OK for tracing.
)
return setLocalExecution(blk, localPayload)
default: // Bellatrix case.
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
return setLocalExecution(blk, localPayload)
} else {
return nil
}
}
}
// This function retrieves the payload header and kzg commitments given the slot number and the validator index.
// It's a no-op if the latest head block is not versioned bellatrix.
func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot primitives.Slot, idx primitives.ValidatorIndex) (interfaces.ExecutionData, [][]byte, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.getPayloadHeaderFromBuilder")
defer span.End()
if slots.ToEpoch(slot) < params.BeaconConfig().BellatrixForkEpoch {
return nil, nil, errors.New("can't get payload header from builder before bellatrix epoch")
}
b, err := vs.HeadFetcher.HeadBlock(ctx)
if err != nil {
return nil, nil, err
}
h, err := b.Block().Body().Execution()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to get execution header")
}
pk, err := vs.HeadFetcher.HeadValidatorIndexToPublicKey(ctx, idx)
if err != nil {
return nil, nil, err
}
ctx, cancel := context.WithTimeout(ctx, blockBuilderTimeout)
defer cancel()
signedBid, err := vs.BlockBuilder.GetHeader(ctx, slot, bytesutil.ToBytes32(h.BlockHash()), pk)
if err != nil {
return nil, nil, err
}
if signedBid.IsNil() {
return nil, nil, errors.New("builder returned nil bid")
}
fork, err := forks.Fork(slots.ToEpoch(slot))
if err != nil {
return nil, nil, errors.Wrap(err, "unable to get fork information")
}
forkName, ok := params.BeaconConfig().ForkVersionNames[bytesutil.ToBytes4(fork.CurrentVersion)]
if !ok {
return nil, nil, errors.New("unable to find current fork in schedule")
}
if !strings.EqualFold(version.String(signedBid.Version()), forkName) {
return nil, nil, fmt.Errorf("builder bid response version: %d is different from head block version: %d for epoch %d", signedBid.Version(), b.Version(), slots.ToEpoch(slot))
}
bid, err := signedBid.Message()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get bid")
}
if bid.IsNil() {
return nil, nil, errors.New("builder returned nil bid")
}
v := bytesutil.LittleEndianBytesToBigInt(bid.Value())
if v.String() == "0" {
return nil, nil, errors.New("builder returned header with 0 bid amount")
}
header, err := bid.Header()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get bid header")
}
txRoot, err := header.TransactionsRoot()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get transaction root")
}
if bytesutil.ToBytes32(txRoot) == emptyTransactionsRoot {
return nil, nil, errors.New("builder returned header with an empty tx root")
}
if !bytes.Equal(header.ParentHash(), h.BlockHash()) {
return nil, nil, fmt.Errorf("incorrect parent hash %#x != %#x", header.ParentHash(), h.BlockHash())
}
t, err := slots.ToTime(uint64(vs.TimeFetcher.GenesisTime().Unix()), slot)
if err != nil {
return nil, nil, err
}
if header.Timestamp() != uint64(t.Unix()) {
return nil, nil, fmt.Errorf("incorrect timestamp %d != %d", header.Timestamp(), uint64(t.Unix()))
}
if err := validateBuilderSignature(signedBid); err != nil {
return nil, nil, errors.Wrap(err, "could not validate builder signature")
}
var kzgCommitments [][]byte
if bid.Version() >= version.Deneb {
kzgCommitments, err = bid.BlobKzgCommitments()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get blob kzg commitments")
}
if len(kzgCommitments) > fieldparams.MaxBlobsPerBlock {
return nil, nil, fmt.Errorf("builder returned too many kzg commitments: %d", len(kzgCommitments))
}
for _, c := range kzgCommitments {
if len(c) != fieldparams.BLSPubkeyLength {
return nil, nil, fmt.Errorf("builder returned invalid kzg commitment length: %d", len(c))
}
}
}
l := log.WithFields(logrus.Fields{
"gweiValue": math.WeiToGwei(v),
"builderPubKey": fmt.Sprintf("%#x", bid.Pubkey()),
"blockHash": fmt.Sprintf("%#x", header.BlockHash()),
"slot": slot,
"validator": idx,
"sinceSlotStartTime": time.Since(t),
})
if len(kzgCommitments) > 0 {
l = l.WithField("kzgCommitmentCount", len(kzgCommitments))
}
l.Info("Received header with bid")
span.AddAttributes(
trace.StringAttribute("value", v.String()),
trace.StringAttribute("builderPubKey", fmt.Sprintf("%#x", bid.Pubkey())),
trace.StringAttribute("blockHash", fmt.Sprintf("%#x", header.BlockHash())),
)
return header, kzgCommitments, nil
}
// Validates builder signature and returns an error if the signature is invalid.
func validateBuilderSignature(signedBid builder.SignedBid) error {
d, err := signing.ComputeDomain(params.BeaconConfig().DomainApplicationBuilder,
@@ -308,53 +58,3 @@ func matchingWithdrawalsRoot(local, builder interfaces.ExecutionData) (bool, err
}
return true, nil
}
// setLocalExecution sets the execution context for a local beacon block.
// It delegates to setExecution for the actual work.
func setLocalExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData) error {
var kzgCommitments [][]byte
fullBlobsBundle := bundleCache.get(blk.Block().Slot())
if fullBlobsBundle != nil {
kzgCommitments = fullBlobsBundle.KzgCommitments
}
return setExecution(blk, execution, false, kzgCommitments)
}
// setBuilderExecution sets the execution context for a builder's beacon block.
// It delegates to setExecution for the actual work.
func setBuilderExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, builderKzgCommitments [][]byte) error {
return setExecution(blk, execution, true, builderKzgCommitments)
}
// setExecution sets the execution context for a beacon block. It also sets KZG commitments based on the block version.
// The function is designed to be flexible and handle both local and builder executions.
func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.ExecutionData, isBlinded bool, kzgCommitments [][]byte) error {
if execution == nil {
return errors.New("execution is nil")
}
// Set the execution data for the block
errMessage := "failed to set local execution"
if isBlinded {
errMessage = "failed to set builder execution"
}
if err := blk.SetExecution(execution); err != nil {
return errors.Wrap(err, errMessage)
}
// If the block version is below Deneb, no further actions are needed
if blk.Version() < version.Deneb {
return nil
}
// Set the KZG commitments for the block
errMessage = "failed to set local kzg commitments"
if isBlinded {
errMessage = "failed to set builder kzg commitments"
}
if err := blk.SetBlobKzgCommitments(kzgCommitments); err != nil {
return errors.Wrap(err, errMessage)
}
return nil
}

View File

@@ -1,38 +1,6 @@
package validator
import (
"context"
"math"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/api/client/builder"
blockchainTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
builderTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/builder/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
dbTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
powtesting "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"github.com/prysmaticlabs/prysm/v5/time/slots"
logTest "github.com/sirupsen/logrus/hooks/test"
)
/*
func TestServer_setExecutionData(t *testing.T) {
hook := logTest.NewGlobal()
@@ -92,12 +60,12 @@ func TestServer_setExecutionData(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block
@@ -153,12 +121,12 @@ func TestServer_setExecutionData(t *testing.T) {
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block because incorrect withdrawals
@@ -217,12 +185,12 @@ func TestServer_setExecutionData(t *testing.T) {
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(2), e.BlockNumber()) // Builder block
@@ -280,12 +248,12 @@ func TestServer_setExecutionData(t *testing.T) {
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, math.MaxUint64))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, math.MaxUint64))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(2), e.BlockNumber()) // builder block
@@ -343,12 +311,12 @@ func TestServer_setExecutionData(t *testing.T) {
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, 0))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, 0))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // local block
@@ -358,17 +326,17 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: 2 * 1e9}
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
//require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
})
t.Run("Builder configured. Local block and local boost has higher value", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
@@ -379,12 +347,12 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: 1 * 1e9}
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
@@ -401,12 +369,12 @@ func TestServer_setExecutionData(t *testing.T) {
}
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 4}, BlockValue: 0}
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, b.Slot(), b.ProposerIndex())
require.ErrorIs(t, consensus_types.ErrNilObjectWrapped, err) // Builder returns fault. Use local block
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(4), e.BlockNumber()) // Local block
@@ -434,11 +402,9 @@ func TestServer_setExecutionData(t *testing.T) {
ExecutionPayloadDeneb: &v1.ExecutionPayloadDeneb{BlockNumber: 4},
BlockValue: 0}
blk.SetSlot(primitives.Slot(params.BeaconConfig().DenebForkEpoch) * params.BeaconConfig().SlotsPerEpoch)
localPayload, _, err := vs.getLocalPayload(ctx, blk.Block(), capellaTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, blk.Block(), capellaTransitionState)
require.NoError(t, err)
require.Equal(t, uint64(4), localPayload.BlockNumber())
cachedBundle := bundleCache.get(blk.Block().Slot())
require.DeepEqual(t, cachedBundle, blobsBundle)
})
t.Run("Can get builder payload and blobs in Deneb", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
@@ -507,14 +473,14 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
blk.SetSlot(primitives.Slot(params.BeaconConfig().DenebForkEpoch) * params.BeaconConfig().SlotsPerEpoch)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, blk.Block().Slot(), blk.Block().ProposerIndex())
builderPayload, builderKzgCommitments, err := vs.populateBuilderPossibility(ctx, blk.Block().Slot(), blk.Block().ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, bid.BlobKzgCommitments, builderKzgCommitments)
require.Equal(t, bid.Header.BlockNumber, builderPayload.BlockNumber()) // header should be the same from block
localPayload, _, err := vs.getLocalPayload(ctx, blk.Block(), denebTransitionState)
localPayload, _, err := vs.setLocalPayloadResp(ctx, blk.Block(), denebTransitionState)
require.NoError(t, err)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
require.NoError(t, choosePayload(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
got, err := blk.Block().Body().BlobKzgCommitments()
require.NoError(t, err)
@@ -737,7 +703,7 @@ func TestServer_getPayloadHeader(t *testing.T) {
}}
hb, err := vs.HeadFetcher.HeadBlock(context.Background())
require.NoError(t, err)
h, _, err := vs.getPayloadHeaderFromBuilder(context.Background(), hb.Block().Slot(), 0)
h, _, err := vs.populateBuilderEtc(context.Background(), hb.Block().Slot(), 0)
if tc.err != "" {
require.ErrorContains(t, tc.err, err)
} else {
@@ -745,12 +711,12 @@ func TestServer_getPayloadHeader(t *testing.T) {
if tc.returnedHeader != nil {
want, err := blocks.WrappedExecutionPayloadHeader(tc.returnedHeader)
require.NoError(t, err)
require.DeepEqual(t, want, h)
require.DeepEqual(t, want, h.ExecutionData)
}
if tc.returnedHeaderCapella != nil {
want, err := blocks.WrappedExecutionPayloadHeaderCapella(tc.returnedHeaderCapella, big.NewInt(197121)) // value is a mock
require.NoError(t, err)
require.DeepEqual(t, want, h)
require.DeepEqual(t, want, h.ExecutionData)
}
}
})
@@ -851,3 +817,6 @@ func TestEmptyTransactionsRoot(t *testing.T) {
require.NoError(t, err)
require.DeepEqual(t, r, emptyTransactionsRoot)
}
*/

View File

@@ -2,60 +2,13 @@ package validator
import (
"errors"
"sync"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
var bundleCache = &blobsBundleCache{}
// BlobsBundleCache holds the KZG commitments and other relevant sidecar data for a local beacon block.
type blobsBundleCache struct {
sync.Mutex
slot primitives.Slot
bundle *enginev1.BlobsBundle
}
// add adds a blobs bundle to the cache.
// same slot overwrites the previous bundle.
func (c *blobsBundleCache) add(slot primitives.Slot, bundle *enginev1.BlobsBundle) {
c.Lock()
defer c.Unlock()
if slot >= c.slot {
c.bundle = bundle
c.slot = slot
}
}
// get gets a blobs bundle from the cache.
func (c *blobsBundleCache) get(slot primitives.Slot) *enginev1.BlobsBundle {
c.Lock()
defer c.Unlock()
if c.slot == slot {
return c.bundle
}
return nil
}
// prune acquires the lock before pruning.
func (c *blobsBundleCache) prune(minSlot primitives.Slot) {
c.Lock()
defer c.Unlock()
if minSlot > c.slot {
c.slot = 0
c.bundle = nil
}
}
// BuildBlobSidecars given a block, builds the blob sidecars for the block.
func BuildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgProofs [][]byte) ([]*ethpb.BlobSidecar, error) {
if blk.Version() < version.Deneb {

View File

@@ -6,44 +6,11 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestAdd(t *testing.T) {
slot := primitives.Slot(1)
bundle := &enginev1.BlobsBundle{KzgCommitments: [][]byte{{'a'}}}
bundleCache.add(slot, bundle)
require.Equal(t, bundleCache.bundle, bundle)
slot = primitives.Slot(2)
bundle = &enginev1.BlobsBundle{KzgCommitments: [][]byte{{'b'}}}
bundleCache.add(slot, bundle)
require.Equal(t, bundleCache.bundle, bundle)
}
func TestGet(t *testing.T) {
slot := primitives.Slot(3)
bundle := &enginev1.BlobsBundle{KzgCommitments: [][]byte{{'a'}}}
bundleCache.add(slot, bundle)
require.Equal(t, bundleCache.get(slot), bundle)
}
func TestPrune(t *testing.T) {
slot1 := primitives.Slot(4)
bundle1 := &enginev1.BlobsBundle{KzgCommitments: [][]byte{{'a'}}}
bundleCache.add(slot1, bundle1)
bundleCache.prune(slot1 + 1)
if bundleCache.get(slot1) != nil {
t.Errorf("Prune did not remove the bundle at slot1")
}
}
func TestServer_buildBlobSidecars(t *testing.T) {
kzgCommitments := [][]byte{bytesutil.PadTo([]byte{'a'}, 48), bytesutil.PadTo([]byte{'b'}, 48)}
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockDeneb())

View File

@@ -223,7 +223,7 @@ func constructMerkleProof(trie cache.MerkleTree, index int, deposit *ethpb.Depos
if err != nil {
return nil, errors.Wrapf(err, "could not generate merkle proof for deposit at index %d", index)
}
// For every deposit, we construct a Merkle proof using the powchain service's
// For every deposit, we constructGenericBeaconBlock a Merkle proof using the powchain service's
// in-memory deposits trie, which is updated only once the state's LatestETH1Data
// property changes during a state transition after a voting period.
deposit.Proof = proof

View File

@@ -8,14 +8,12 @@ import (
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
prysmtime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -24,7 +22,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
var (
@@ -40,73 +37,28 @@ var (
})
)
func setFeeRecipientIfBurnAddress(val *cache.TrackedValidator) {
if val.FeeRecipient == primitives.ExecutionAddress([20]byte{}) && val.Index == 0 {
val.FeeRecipient = primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
}
}
// This returns the local execution payload of a given slot. The function has full awareness of pre and post merge.
func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) (interfaces.ExecutionData, bool, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.getLocalPayload")
defer span.End()
if blk.Version() < version.Bellatrix {
return nil, false, nil
}
slot := blk.Slot()
vIdx := blk.ProposerIndex()
headRoot := blk.ParentRoot()
logFields := logrus.Fields{
"validatorIndex": vIdx,
"slot": slot,
"headRoot": fmt.Sprintf("%#x", headRoot),
}
payloadId, ok := vs.PayloadIDCache.PayloadID(slot, headRoot)
val, tracked := vs.TrackedValidatorsCache.Validator(vIdx)
if !tracked {
logrus.WithFields(logFields).Warn("could not find tracked proposer index")
}
setFeeRecipientIfBurnAddress(&val)
var err error
func (vs *Server) getPayloadID(ctx context.Context, blk interfaces.SignedBeaconBlock, st state.BeaconState, feeRecipient primitives.ExecutionAddress) (primitives.PayloadID, error) {
var pid primitives.PayloadID
slot := blk.Block().Slot()
head := blk.Block().ParentRoot()
payloadId, ok := vs.PayloadIDCache.PayloadID(slot, head)
if ok && payloadId != [8]byte{} {
// Payload ID is cache hit. Return the cached payload ID.
var pid primitives.PayloadID
copy(pid[:], payloadId[:])
payloadIDCacheHit.Inc()
payload, bundle, overrideBuilder, err := vs.ExecutionEngineCaller.GetPayload(ctx, pid, slot)
switch {
case err == nil:
bundleCache.add(slot, bundle)
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
return payload, overrideBuilder, nil
case errors.Is(err, context.DeadlineExceeded):
default:
return nil, false, errors.Wrap(err, "could not get cached payload from execution client")
}
}
log.WithFields(logFields).Debug("payload ID cache miss")
parentHash, err := vs.getParentBlockHash(ctx, st, slot)
switch {
case errors.Is(err, errActivationNotReached) || errors.Is(err, errNoTerminalBlockHash):
p, err := consensusblocks.WrappedExecutionPayload(emptyPayload())
if err != nil {
return nil, false, err
}
return p, false, nil
case err != nil:
return nil, false, err
// Payload ID is cache hit. Return the cached payload ID.
copy(pid[:], payloadId[:])
return pid, nil
}
payloadIDCacheMiss.Inc()
log.WithFields(logrus.Fields{
"validatorIndex": blk.Block().ProposerIndex(),
"slot": slot,
"headRoot": fmt.Sprintf("%#x", head),
}).Debug("payload ID cache miss")
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
parentHash, err := vs.getParentBlockHash(ctx, st, slot)
if err != nil {
return nil, false, err
return pid, err
}
finalizedBlockHash := [32]byte{}
justifiedBlockHash := [32]byte{}
// Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
@@ -114,78 +66,70 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
finalizedBlockHash = vs.FinalizationFetcher.FinalizedBlockHash()
justifiedBlockHash = vs.FinalizationFetcher.UnrealizedJustifiedPayloadBlockHash()
}
f := &enginev1.ForkchoiceState{
HeadBlockHash: parentHash,
SafeBlockHash: justifiedBlockHash[:],
FinalizedBlockHash: finalizedBlockHash[:],
}
attr, err := payloadAttributesForState(slot, head, st, feeRecipient)
if err != nil {
return pid, err
}
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, attr)
if err != nil {
return pid, errors.Wrap(err, "could not completeWithBest payload")
}
if payloadID == nil {
return pid, fmt.Errorf("nil payload with block hash: %#x", parentHash)
}
copy(pid[:], payloadID[:])
return pid, nil
}
func payloadAttributesForState(slot primitives.Slot, pr [32]byte, st state.BeaconState, feeRecipient primitives.ExecutionAddress) (payloadattribute.Attributer, error) {
random, err := helpers.RandaoMix(st, prysmtime.CurrentEpoch(st))
if err != nil {
return nil, err
}
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, false, err
return nil, err
}
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
return nil, err
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
return payloadattribute.New(&enginev1.PayloadAttributesV3{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: val.FeeRecipient[:],
SuggestedFeeRecipient: feeRecipient[:],
Withdrawals: withdrawals,
ParentBeaconBlockRoot: headRoot[:],
ParentBeaconBlockRoot: pr[:],
})
if err != nil {
return nil, false, err
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
return nil, err
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
return payloadattribute.New(&enginev1.PayloadAttributesV2{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: val.FeeRecipient[:],
SuggestedFeeRecipient: feeRecipient[:],
Withdrawals: withdrawals,
})
if err != nil {
return nil, false, err
}
case version.Bellatrix:
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
return payloadattribute.New(&enginev1.PayloadAttributes{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: val.FeeRecipient[:],
SuggestedFeeRecipient: feeRecipient[:],
})
if err != nil {
return nil, false, err
}
default:
return nil, false, errors.New("unknown beacon state version")
return nil, errors.New("unknown beacon state version")
}
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, attr)
if err != nil {
return nil, false, errors.Wrap(err, "could not prepare payload")
}
if payloadID == nil {
return nil, false, fmt.Errorf("nil payload with block hash: %#x", parentHash)
}
payload, bundle, overrideBuilder, err := vs.ExecutionEngineCaller.GetPayload(ctx, *payloadID, slot)
if err != nil {
return nil, false, err
}
bundleCache.add(slot, bundle)
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
localValueGwei, err := payload.ValueInGwei()
if err == nil {
log.WithField("value", localValueGwei).Debug("received execution payload from local engine")
}
return payload, overrideBuilder, nil
}
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not
@@ -232,27 +176,6 @@ func (vs *Server) getTerminalBlockHashIfExists(ctx context.Context, transitionTi
return vs.ExecutionEngineCaller.GetTerminalBlockHash(ctx, transitionTime)
}
func (vs *Server) getBuilderPayloadAndBlobs(ctx context.Context,
slot primitives.Slot,
vIdx primitives.ValidatorIndex) (interfaces.ExecutionData, [][]byte, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.getBuilderPayloadAndBlobs")
defer span.End()
if slots.ToEpoch(slot) < params.BeaconConfig().BellatrixForkEpoch {
return nil, nil, nil
}
canUseBuilder, err := vs.canUseBuilder(ctx, slot, vIdx)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to check if we can use the builder")
}
span.AddAttributes(trace.BoolAttribute("canUseBuilder", canUseBuilder))
if !canUseBuilder {
return nil, nil, nil
}
return vs.getPayloadHeaderFromBuilder(ctx, slot, vIdx)
}
var errActivationNotReached = errors.New("activation epoch not reached")
var errNoTerminalBlockHash = errors.New("no terminal block hash")

View File

@@ -2,26 +2,16 @@ package validator
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
chainMock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
dbTest "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
powtesting "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func TestServer_activationEpochNotReached(t *testing.T) {
@@ -37,6 +27,7 @@ func TestServer_activationEpochNotReached(t *testing.T) {
require.Equal(t, false, activationEpochNotReached(params.BeaconConfig().SlotsPerEpoch+1))
}
/*
func TestServer_getExecutionPayload(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
nonTransitionSt, _ := util.DeterministicGenesisStateBellatrix(t, 1)
@@ -114,10 +105,10 @@ func TestServer_getExecutionPayload(t *testing.T) {
validatorIndx: 100,
},
{
name: "transition completed, could not prepare payload",
name: "transition completed, could not completeWithBest payload",
st: transitionSt,
forkchoiceErr: errors.New("fork choice error"),
errString: "could not prepare payload",
errString: "could not completeWithBest payload",
},
{
name: "transition not-completed, latest exec block is nil",
@@ -165,7 +156,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
b, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
var gotOverride bool
_, gotOverride, err = vs.getLocalPayload(context.Background(), b.Block(), tt.st)
_, gotOverride, err = vs.setLocalPayloadResp(context.Background(), b.Block(), tt.st)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -176,6 +167,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
}
}
func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
nonTransitionSt, _ := util.DeterministicGenesisStateBellatrix(t, 1)
@@ -209,7 +201,7 @@ func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
blk.Block.ParentRoot = bytesutil.PadTo([]byte{'a'}, 32)
b, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
_, _, err = vs.getLocalPayload(context.Background(), b.Block(), nonTransitionSt)
_, _, err = vs.setLocalPayloadResp(context.Background(), b.Block(), nonTransitionSt)
require.NoError(t, err)
}
@@ -264,7 +256,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
blk.Block.ParentRoot = bytesutil.PadTo([]byte{}, 32)
b, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
gotPayload, _, err := vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
gotPayload, _, err := vs.setLocalPayloadResp(context.Background(), b.Block(), transitionSt)
require.NoError(t, err)
require.NotNil(t, gotPayload)
require.Equal(t, common.Address(gotPayload.FeeRecipient()), feeRecipient)
@@ -277,7 +269,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
payload.FeeRecipient = evilRecipientAddress[:]
vs.PayloadIDCache = cache.NewPayloadIDCache()
gotPayload, _, err = vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
gotPayload, _, err = vs.setLocalPayloadResp(context.Background(), b.Block(), transitionSt)
require.NoError(t, err)
require.NotNil(t, gotPayload)
@@ -285,6 +277,8 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
require.LogsContain(t, hook, "Fee recipient address from execution client is not what was expected")
}
*/
func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
params.SetupTestConfigCleanup(t)
tests := []struct {
@@ -385,14 +379,13 @@ func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
}
func TestSetFeeRecipientIfBurnAddress(t *testing.T) {
val := &cache.TrackedValidator{Index: 1}
val := cache.TrackedValidator{Index: 1}
cfg := params.BeaconConfig().Copy()
cfg.DefaultFeeRecipient = common.Address([20]byte{'a'})
params.OverrideBeaconConfig(cfg)
require.NotEqual(t, common.Address(val.FeeRecipient), params.BeaconConfig().DefaultFeeRecipient)
setFeeRecipientIfBurnAddress(val)
require.NotEqual(t, common.Address(val.FeeRecipient), params.BeaconConfig().DefaultFeeRecipient)
require.NotEqual(t, defaultIfBurnAddress(val), params.BeaconConfig().DefaultFeeRecipient)
val.Index = 0
setFeeRecipientIfBurnAddress(val)
require.Equal(t, common.Address(val.FeeRecipient), params.BeaconConfig().DefaultFeeRecipient)
require.Equal(t, defaultIfBurnAddress(val), params.BeaconConfig().DefaultFeeRecipient)
}

View File

@@ -0,0 +1,535 @@
package validator
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v5/api/client/builder"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v5/network/forks"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// emptyTransactionsRoot represents the returned value of ssz.TransactionsRoot([][]byte{}) and
// can be used as a constant to avoid recomputing this value in every call.
var emptyTransactionsRoot = [32]byte{127, 254, 36, 30, 166, 1, 135, 253, 176, 24, 123, 250, 34, 222, 53, 209, 249, 190, 215, 171, 6, 29, 148, 1, 253, 71, 227, 74, 84, 251, 237, 225}
var errNoProposalSource = errors.New("proposal process did not pick between builder and local block")
// blockBuilderTimeout is the maximum amount of time allowed for a block builder to respond to a
// block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec.
const blockBuilderTimeout = 1 * time.Second
var (
builderValueGweiGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "builder_value_gwei",
Help: "Builder payload value in gwei",
})
localValueGweiGauge = promauto.NewGauge(prometheus.GaugeOpts{
Name: "local_value_gwei",
Help: "Local payload value in gwei",
})
builderGetPayloadMissCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "builder_get_payload_miss_count",
Help: "The number of get payload misses for validator requests to builder",
})
)
// proposalBlock accumulates data needed to respond to the proposer GetBeaconBlock request.
type proposalResponseConstructor struct {
overrideBuilder bool
FeeRecipient primitives.ExecutionAddress
builder *PayloadPossibility
local *PayloadPossibility
head state.BeaconState
block interfaces.SignedBeaconBlock
}
func newProposalResponseConstructor(blk interfaces.SignedBeaconBlock, st state.BeaconState, feeRecipient primitives.ExecutionAddress) *proposalResponseConstructor {
return &proposalResponseConstructor{block: blk, head: st, FeeRecipient: feeRecipient}
}
func (resp *proposalResponseConstructor) buildBlockParallel(ctx context.Context, vs *Server, skipMevBoost bool, builderBoostFactor uint64) (*ethpb.GenericBeaconBlock, error) {
sBlk := resp.block
// Build consensus fields in background
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
// Set eth1 data.
eth1Data, err := vs.eth1DataMajorityVote(ctx, resp.head)
if err != nil {
eth1Data = &ethpb.Eth1Data{DepositRoot: params.BeaconConfig().ZeroHash[:], BlockHash: params.BeaconConfig().ZeroHash[:]}
log.WithError(err).Error("Could not get eth1data")
}
sBlk.SetEth1Data(eth1Data)
// Set deposit and attestation.
deposits, atts, err := vs.packDepositsAndAttestations(ctx, resp.head, eth1Data) // TODO: split attestations and deposits
if err != nil {
sBlk.SetDeposits([]*ethpb.Deposit{})
if err := sBlk.SetAttestations([]interfaces.Attestation{}); err != nil {
log.WithError(err).Error("Could not set attestations on block")
}
log.WithError(err).Error("Could not pack deposits and attestations")
} else {
sBlk.SetDeposits(deposits)
if err := sBlk.SetAttestations(atts); err != nil {
log.WithError(err).Error("Could not set attestations on block")
}
}
// Set slashings.
validProposerSlashings, validAttSlashings := vs.getSlashings(ctx, resp.head)
sBlk.SetProposerSlashings(validProposerSlashings)
if err := sBlk.SetAttesterSlashings(validAttSlashings); err != nil {
log.WithError(err).Error("Could not set attester slashings on block")
}
// Set exits.
sBlk.SetVoluntaryExits(vs.getExits(resp.head, sBlk.Block().Slot()))
// Set sync aggregate. New in Altair.
vs.setSyncAggregate(ctx, sBlk)
// Set bls to execution change. New in Capella.
vs.setBlsToExecData(sBlk, resp.head)
return nil
})
builderCtx, cancelBuilder := context.WithCancel(ctx)
eg.Go(func() error {
if err := resp.populateLocalPossibility(ctx, vs, cancelBuilder); err != nil {
if !errors.Is(err, errActivationNotReached) && !errors.Is(err, errNoTerminalBlockHash) {
return status.Errorf(codes.Internal, "Could not get local payload: %v", err)
}
}
return nil
})
if !skipMevBoost {
eg.Go(func() error {
// builderCtx will be canceled by populateLocalPossibility if the engine decides to override the builder.
if err := resp.populateBuilderPossibility(builderCtx, vs); err != nil {
builderGetPayloadMissCount.Inc()
log.WithError(err).Error("Could not get builder payload")
}
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
return resp.construct(ctx, builderBoostFactor)
}
// populateLocalPossibility queries the execution layer engine API to retrieve the local payload for the proposal.
func (resp *proposalResponseConstructor) populateLocalPossibility(ctx context.Context, vs *Server, cancelBuilder context.CancelFunc) error {
ctx, span := trace.StartSpan(ctx, "ProposerServer.setLocalPayloadResp")
defer span.End()
blk := resp.block.Block()
if blk.Version() < version.Bellatrix {
return nil
}
pid, err := vs.getPayloadID(ctx, resp.block, resp.head, resp.FeeRecipient)
if err != nil {
return errors.Wrap(err, "unable to determine payload id for proposal request")
}
payload, bid, bundle, overrideBuilder, err := vs.ExecutionEngineCaller.GetPayload(ctx, pid, blk.Slot())
if err != nil {
return err
}
resp.overrideBuilder = overrideBuilder
if resp.overrideBuilder {
cancelBuilder()
}
warnIfFeeRecipientDiffers(payload, resp.FeeRecipient)
pwb, err := NewPayloadPossibility(payload, bid, bundle, nil)
if err != nil {
return err
}
resp.local = pwb
log.WithField("value", math.WeiToGwei(bid)).Debug("received execution payload from local engine")
return nil
}
func (resp *proposalResponseConstructor) populateBuilderPossibility(ctx context.Context, vs *Server) error {
ctx, span := trace.StartSpan(ctx, "ProposerServer.populateBuilderPossibility")
defer span.End()
blk := resp.block.Block()
if blk.Version() < version.Bellatrix {
return nil
}
slot := blk.Slot()
vIdx := blk.ProposerIndex()
canUseBuilder, err := vs.canUseBuilder(ctx, slot, vIdx)
if err != nil {
return errors.Wrap(err, "failed to check if we can use the builder")
}
span.AddAttributes(trace.BoolAttribute("canUseBuilder", canUseBuilder))
if !canUseBuilder {
return nil
}
b, err := vs.HeadFetcher.HeadBlock(ctx)
if err != nil {
return err
}
h, err := b.Block().Body().Execution()
if err != nil {
return errors.Wrap(err, "failed to get execution header")
}
pk, err := vs.HeadFetcher.HeadValidatorIndexToPublicKey(ctx, vIdx)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(ctx, blockBuilderTimeout)
defer cancel()
signedBid, err := vs.BlockBuilder.GetHeader(ctx, slot, bytesutil.ToBytes32(h.BlockHash()), pk)
if err != nil {
return err
}
blockTime, err := slots.ToTime(uint64(vs.TimeFetcher.GenesisTime().Unix()), slot)
if err != nil {
return err
}
header, bid, bidWei, kzgCommitments, err := validatedBuilderPayload(signedBid, b, h, blockTime)
if err != nil {
return err
}
l := log.WithFields(logrus.Fields{
"gweiValue": math.WeiToGwei(bidWei),
"builderPubKey": fmt.Sprintf("%#x", bid.Pubkey()),
"blockHash": fmt.Sprintf("%#x", header.BlockHash()),
"slot": slot,
"validator": vIdx,
"sinceSlotStartTime": time.Since(blockTime),
})
if len(kzgCommitments) > 0 {
l = l.WithField("kzgCommitmentCount", len(kzgCommitments))
}
l.Info("Received header with bid")
span.AddAttributes(
trace.StringAttribute("value", math.WeiToBigInt(bidWei).String()),
trace.StringAttribute("builderPubKey", fmt.Sprintf("%#x", bid.Pubkey())),
trace.StringAttribute("blockHash", fmt.Sprintf("%#x", header.BlockHash())),
)
pwb, err := NewPayloadPossibility(header, bidWei, nil, kzgCommitments)
resp.builder = pwb
return err
}
func validatedBuilderPayload(sb builder.SignedBid, b interfaces.ReadOnlySignedBeaconBlock, headExecution interfaces.ExecutionData, blockTime time.Time) (interfaces.ExecutionData, builder.Bid, math.Wei, [][]byte, error) {
slot := b.Block().Slot()
if sb.IsNil() {
return nil, nil, math.ZeroWei, nil, errors.New("builder returned nil bid")
}
fork, err := forks.Fork(slots.ToEpoch(slot))
if err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "unable to get fork information")
}
forkName, ok := params.BeaconConfig().ForkVersionNames[bytesutil.ToBytes4(fork.CurrentVersion)]
if !ok {
return nil, nil, math.ZeroWei, nil, errors.New("unable to find current fork in schedule")
}
if !strings.EqualFold(version.String(sb.Version()), forkName) {
return nil, nil, math.ZeroWei, nil, fmt.Errorf("builder bid response version: %d is different from head block version: %d for epoch %d", sb.Version(), b.Version(), slots.ToEpoch(slot))
}
bid, err := sb.Message()
if err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "could not get bid")
}
if bid.IsNil() {
return nil, nil, math.ZeroWei, nil, errors.New("builder returned nil bid")
}
v := bytesutil.LittleEndianBytesToBigInt(bid.Value())
if v.String() == "0" {
return nil, nil, math.ZeroWei, nil, errors.New("builder returned header with 0 bid amount")
}
header, err := bid.Header()
if err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "could not get bid header")
}
bidWei := math.BigEndianBytesToWei(bid.Value())
txRoot, err := header.TransactionsRoot()
if err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "could not get transaction root")
}
if bytesutil.ToBytes32(txRoot) == emptyTransactionsRoot {
return nil, nil, math.ZeroWei, nil, errors.New("builder returned header with an empty tx root")
}
if !bytes.Equal(header.ParentHash(), headExecution.BlockHash()) {
return nil, nil, math.ZeroWei, nil, fmt.Errorf("incorrect parent hash %#x != %#x", header.ParentHash(), headExecution.BlockHash())
}
if header.Timestamp() != uint64(blockTime.Unix()) {
return nil, nil, math.ZeroWei, nil, fmt.Errorf("incorrect timestamp %d != %d", header.Timestamp(), uint64(blockTime.Unix()))
}
if err := validateBuilderSignature(sb); err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "could not validate builder signature")
}
var kzgCommitments [][]byte
if bid.Version() >= version.Deneb {
kzgCommitments, err = bid.BlobKzgCommitments()
if err != nil {
return nil, nil, math.ZeroWei, nil, errors.Wrap(err, "could not get blob kzg commitments")
}
if len(kzgCommitments) > fieldparams.MaxBlobsPerBlock {
return nil, nil, math.ZeroWei, nil, fmt.Errorf("builder returned too many kzg commitments: %d", len(kzgCommitments))
}
for _, c := range kzgCommitments {
if len(c) != fieldparams.BLSPubkeyLength {
return nil, nil, math.ZeroWei, nil, fmt.Errorf("builder returned invalid kzg commitment length: %d", len(c))
}
}
}
return header, nil, bidWei, kzgCommitments, nil
}
// construct picks the best proposal and sets the execution header/payload attributes for it on the block.
// It also takes the parent state as an argument so that it can compute and set the state root using the
// completely updated block.
func (pc *proposalResponseConstructor) construct(ctx context.Context, builderBoostFactor uint64) (*ethpb.GenericBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "proposalResponseConstructor.construct")
defer span.End()
if err := blocks.HasNilErr(pc.block); err != nil {
return nil, err
}
best, err := pc.choosePayload(ctx, builderBoostFactor)
if err != nil {
return nil, status.Errorf(codes.Internal, "Error constructing execution payload for block: %v", err)
}
best, err = pc.complete(ctx, best)
if err != nil {
return nil, err
}
return constructGenericBeaconBlock(pc.block, best.bid, best.bundle)
}
// Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions.
func (resp *proposalResponseConstructor) choosePayload(ctx context.Context, builderBoostFactor uint64) (*PayloadPossibility, error) {
_, span := trace.StartSpan(ctx, "validator.choosePayload")
defer span.End()
blk := resp.block
slot := blk.Block().Slot()
if slots.ToEpoch(slot) < params.BeaconConfig().BellatrixForkEpoch {
return nil, nil
}
if resp.local.IsNil() {
return nil, errors.New("local payload is nil")
}
if resp.overrideBuilder {
return resp.local, nil
}
// Use local payload if builder payload is nil.
if resp.builder.IsNil() {
return resp.local, nil
}
switch {
case blk.Version() >= version.Capella:
// Compare payload values between local and builder. Default to the local value if it is higher.
localValueGwei := uint64(resp.local.ValueInGwei())
builderValueGwei := uint64(resp.builder.ValueInGwei())
if builderValueGwei == 0 {
log.WithField("builderGwei", 0).Warn("Proposer: failed to get builder payload value") // Default to local if can't get builder value.
return resp.local, nil
}
withdrawalsMatched, err := matchingWithdrawalsRoot(resp.local.ExecutionData, resp.local.ExecutionData)
if err != nil {
tracing.AnnotateError(span, err)
log.WithError(err).Warn("Proposer: failed to match withdrawals root")
return resp.local, nil
}
// Use builder payload if the following in true:
// builder_bid_value * builderBoostFactor(default 100) > local_block_value * (local-block-value-boost + 100)
boost := params.BeaconConfig().LocalBlockValueBoost
higherValueBuilder := builderValueGwei*builderBoostFactor > localValueGwei*(100+boost)
if boost > 0 && builderBoostFactor != defaultBuilderBoostFactor {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: both local boost and builder boost are using non default values")
}
builderValueGweiGauge.Set(float64(builderValueGwei))
localValueGweiGauge.Set(float64(localValueGwei))
// If we can't get the builder value, just use local block.
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
return resp.builder, nil
}
if !higherValueBuilder {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: using local execution payload because higher value")
}
span.AddAttributes(
trace.BoolAttribute("higherValueBuilder", higherValueBuilder),
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderBoostFactor", int64(builderBoostFactor)), // lint:ignore uintcast -- This is OK for tracing.
)
return resp.local, nil
default: // Bellatrix case.
return resp.builder, nil
}
}
func (pc *proposalResponseConstructor) complete(ctx context.Context, best *PayloadPossibility) (*PayloadPossibility, error) {
err := pc.completeWithBest(ctx, best)
if err == nil {
return best, nil
}
// We can fall back from the builder to local, but not the other way. If local fails, we're done.
if best == pc.local {
return nil, err
}
// Try again with the local payload. If this fails then we're truly done.
return pc.local, pc.completeWithBest(ctx, pc.local)
}
func (pc *proposalResponseConstructor) completeWithBest(ctx context.Context, best *PayloadPossibility) error {
ctx, span := trace.StartSpan(ctx, "proposalResponseConstructor.completeWithBest")
defer span.End()
if best.IsNil() {
return errNoProposalSource
}
if err := pc.block.SetExecution(best.ExecutionData); err != nil {
return err
}
if pc.block.Version() >= version.Deneb {
kzgc := best.kzgCommitments
if best.bundle != nil {
kzgc = best.bundle.KzgCommitments
}
if err := pc.block.SetBlobKzgCommitments(kzgc); err != nil {
return err
}
}
root, err := transition.CalculateStateRoot(ctx, pc.head, pc.block)
if err != nil {
return errors.Wrapf(err, "could not calculate state root for proposal with parent root=%#x at slot %d", pc.block.Block().ParentRoot(), pc.head.Slot())
}
log.WithField("beaconStateRoot", fmt.Sprintf("%#x", root)).Debugf("Computed state root")
pc.block.SetStateRoot(root[:])
return nil
}
// constructGenericBeaconBlock constructs a `GenericBeaconBlock` based on the block version and other parameters.
func constructGenericBeaconBlock(blk interfaces.SignedBeaconBlock, bid math.Wei, bundle *enginev1.BlobsBundle) (*ethpb.GenericBeaconBlock, error) {
if err := blocks.HasNilErr(blk); err != nil {
return nil, err
}
blockProto, err := blk.Block().Proto()
if err != nil {
return nil, err
}
payloadValue := math.WeiToBigInt(bid).String()
switch pb := blockProto.(type) {
case *ethpb.BeaconBlockDeneb:
denebContents := &ethpb.BeaconBlockContentsDeneb{Block: pb}
if bundle != nil {
denebContents.KzgProofs = bundle.Proofs
denebContents.Blobs = bundle.Blobs
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: denebContents}, IsBlinded: false, PayloadValue: payloadValue}, nil
case *ethpb.BlindedBeaconBlockDeneb:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
case *ethpb.BeaconBlockCapella:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: pb}, IsBlinded: false, PayloadValue: payloadValue}, nil
case *ethpb.BlindedBeaconBlockCapella:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
case *ethpb.BeaconBlockBellatrix:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb}, IsBlinded: false, PayloadValue: payloadValue}, nil
case *ethpb.BlindedBeaconBlockBellatrix:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
case *ethpb.BeaconBlockAltair:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: pb}}, nil
case *ethpb.BeaconBlock:
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Phase0{Phase0: pb}}, nil
}
return nil, fmt.Errorf("unknown .block version: %d", blk.Version())
}
// PayloadPossibility represents one of the payload possibilities that the proposer may select between (local, builder).
type PayloadPossibility struct {
interfaces.ExecutionData
bid math.Wei
bundle *enginev1.BlobsBundle
kzgCommitments [][]byte
}
// NewPayloadPossibility initializes a PayloadPossibility. This should only be used to represent payloads that have a bid,
// otherwise directly use an ExecutionData type.
func NewPayloadPossibility(p interfaces.ExecutionData, bid math.Wei, bundle *enginev1.BlobsBundle, kzgc [][]byte) (*PayloadPossibility, error) {
if err := blocks.HasNilErr(p); err != nil {
return nil, err
}
if bid == nil {
bid = math.ZeroWei
}
return &PayloadPossibility{ExecutionData: p, bid: bid, bundle: bundle, kzgCommitments: kzgc}, nil
}
func (p *PayloadPossibility) IsNil() bool {
return p == nil || p.ExecutionData.IsNil()
}
// ValueInGwei is a helper to converts the bid value to its gwei representation.
func (p *PayloadPossibility) ValueInGwei() math.Gwei {
return math.WeiToGwei(p.bid)
}

View File

@@ -18,7 +18,6 @@ import (
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
coretime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
dbutil "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
@@ -37,7 +36,6 @@ import (
mockSync "github.com/prysmaticlabs/prysm/v5/beacon-chain/sync/initial-sync/testing"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/container/trie"
@@ -818,39 +816,6 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
}
}
func TestProposer_ComputeStateRoot_OK(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
beaconState, parentRoot, privKeys := util.DeterministicGenesisStateWithGenesisBlock(t, ctx, db, 100)
proposerServer := &Server{
ChainStartFetcher: &mockExecution.Chain{},
Eth1InfoFetcher: &mockExecution.Chain{},
Eth1BlockFetcher: &mockExecution.Chain{},
StateGen: stategen.New(db, doublylinkedtree.New()),
}
req := util.NewBeaconBlock()
req.Block.ProposerIndex = 84
req.Block.ParentRoot = parentRoot[:]
req.Block.Slot = 1
require.NoError(t, beaconState.SetSlot(beaconState.Slot()+1))
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
require.NoError(t, err)
proposerIdx, err := helpers.BeaconProposerIndex(ctx, beaconState)
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(slots.PrevSlot(beaconState.Slot())))
req.Block.Body.RandaoReveal = randaoReveal
currentEpoch := coretime.CurrentEpoch(beaconState)
req.Signature, err = signing.ComputeDomainAndSign(beaconState, currentEpoch, req.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
wsb, err := blocks.NewSignedBeaconBlock(req)
require.NoError(t, err)
_, err = proposerServer.computeStateRoot(context.Background(), wsb)
require.NoError(t, err)
}
func TestProposer_PendingDeposits_Eth1DataVoteOK(t *testing.T) {
ctx := context.Background()

View File

@@ -32,7 +32,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/network/forks"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
@@ -203,25 +202,3 @@ func (vs *Server) WaitForChainStart(_ *emptypb.Empty, stream ethpb.BeaconNodeVal
}
return stream.Send(res)
}
// PruneBlobsBundleCacheRoutine prunes the blobs bundle cache at 6s mark of the slot.
func (vs *Server) PruneBlobsBundleCacheRoutine() {
go func() {
clock, err := vs.ClockWaiter.WaitForClock(vs.Ctx)
if err != nil {
log.WithError(err).Error("PruneBlobsBundleCacheRoutine failed to receive genesis data")
return
}
pruneInterval := time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot/2)
ticker := slots.NewSlotTickerWithIntervals(clock.GenesisTime(), []time.Duration{pruneInterval})
for {
select {
case <-vs.Ctx.Done():
return
case slotInterval := <-ticker.C():
bundleCache.prune(slotInterval.Slot)
}
}
}()
}

View File

@@ -351,7 +351,6 @@ var _ stategen.CurrentSlotter = blockchain.ChainInfoFetcher(nil)
// Start the gRPC server.
func (s *Service) Start() {
grpcprometheus.EnableHandlingTimeHistogram()
s.validatorServer.PruneBlobsBundleCacheRoutine()
go func() {
if s.listener != nil {
if err := s.grpcServer.Serve(s.listener); err != nil {

View File

@@ -32,7 +32,6 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@@ -24,10 +24,8 @@ type executionPayload struct {
}
// NewWrappedExecutionData creates an appropriate execution payload wrapper based on the incoming type.
func NewWrappedExecutionData(v proto.Message, weiValue math.Wei) (interfaces.ExecutionData, error) {
if weiValue == nil {
weiValue = new(big.Int).SetInt64(0)
}
func NewWrappedExecutionData(v proto.Message) (interfaces.ExecutionData, error) {
weiValue := new(big.Int).SetInt64(0)
switch pbStruct := v.(type) {
case *enginev1.ExecutionPayload:
return WrappedExecutionPayload(pbStruct)
@@ -35,11 +33,15 @@ func NewWrappedExecutionData(v proto.Message, weiValue math.Wei) (interfaces.Exe
return WrappedExecutionPayloadCapella(pbStruct, weiValue)
case *enginev1.ExecutionPayloadDeneb:
return WrappedExecutionPayloadDeneb(pbStruct, weiValue)
case *enginev1.ExecutionPayloadElectra:
return WrappedExecutionPayloadElectra(pbStruct, weiValue)
default:
return nil, ErrUnsupportedVersion
}
}
var _ interfaces.ExecutionData = &executionPayload{}
// WrappedExecutionPayload is a constructor which wraps a protobuf execution payload into an interface.
func WrappedExecutionPayload(p *enginev1.ExecutionPayload) (interfaces.ExecutionData, error) {
w := executionPayload{p: p}
@@ -189,26 +191,6 @@ func (e executionPayload) ExcessBlobGas() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// ValueInWei --
func (executionPayload) ValueInWei() (math.Wei, error) {
return nil, consensus_types.ErrUnsupportedField
}
// ValueInGwei --
func (executionPayload) ValueInGwei() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// DepositReceipts --
func (e executionPayload) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayload) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// executionPayloadHeader is a convenience wrapper around a blinded beacon block body's execution header data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
@@ -216,6 +198,8 @@ type executionPayloadHeader struct {
p *enginev1.ExecutionPayloadHeader
}
var _ interfaces.ExecutionData = &executionPayloadHeader{}
// WrappedExecutionPayloadHeader is a constructor which wraps a protobuf execution header into an interface.
func WrappedExecutionPayloadHeader(p *enginev1.ExecutionPayloadHeader) (interfaces.ExecutionData, error) {
w := executionPayloadHeader{p: p}
@@ -365,26 +349,6 @@ func (e executionPayloadHeader) ExcessBlobGas() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// ValueInWei --
func (executionPayloadHeader) ValueInWei() (math.Wei, error) {
return nil, consensus_types.ErrUnsupportedField
}
// ValueInGwei --
func (executionPayloadHeader) ValueInGwei() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// DepositReceipts --
func (e executionPayloadHeader) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayloadHeader) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PayloadToHeader converts `payload` into execution payload header format.
func PayloadToHeader(payload interfaces.ExecutionData) (*enginev1.ExecutionPayloadHeader, error) {
txs, err := payload.Transactions()
@@ -422,6 +386,8 @@ type executionPayloadCapella struct {
gweiValue uint64
}
var _ interfaces.ExecutionData = &executionPayloadCapella{}
// WrappedExecutionPayloadCapella is a constructor which wraps a protobuf execution payload into an interface.
func WrappedExecutionPayloadCapella(p *enginev1.ExecutionPayloadCapella, value math.Wei) (interfaces.ExecutionData, error) {
w := executionPayloadCapella{p: p, weiValue: value, gweiValue: uint64(math.WeiToGwei(value))}
@@ -571,26 +537,6 @@ func (e executionPayloadCapella) ExcessBlobGas() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// ValueInWei --
func (e executionPayloadCapella) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadCapella) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadCapella) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayloadCapella) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// executionPayloadHeaderCapella is a convenience wrapper around a blinded beacon block body's execution header data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
@@ -600,6 +546,8 @@ type executionPayloadHeaderCapella struct {
gweiValue uint64
}
var _ interfaces.ExecutionData = &executionPayloadHeaderCapella{}
// WrappedExecutionPayloadHeaderCapella is a constructor which wraps a protobuf execution header into an interface.
func WrappedExecutionPayloadHeaderCapella(p *enginev1.ExecutionPayloadHeaderCapella, value math.Wei) (interfaces.ExecutionData, error) {
w := executionPayloadHeaderCapella{p: p, weiValue: value, gweiValue: uint64(math.WeiToGwei(value))}
@@ -749,26 +697,6 @@ func (e executionPayloadHeaderCapella) ExcessBlobGas() (uint64, error) {
return 0, consensus_types.ErrUnsupportedField
}
// ValueInWei --
func (e executionPayloadHeaderCapella) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadHeaderCapella) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadHeaderCapella) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayloadHeaderCapella) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PayloadToHeaderCapella converts `payload` into execution payload header format.
func PayloadToHeaderCapella(payload interfaces.ExecutionData) (*enginev1.ExecutionPayloadHeaderCapella, error) {
txs, err := payload.Transactions()
@@ -856,7 +784,7 @@ func PayloadToHeaderDeneb(payload interfaces.ExecutionData) (*enginev1.Execution
}
// PayloadToHeaderElectra converts `payload` into execution payload header format.
func PayloadToHeaderElectra(payload interfaces.ExecutionData) (*enginev1.ExecutionPayloadHeaderElectra, error) {
func PayloadToHeaderElectra(payload interfaces.ExecutionDataElectra) (*enginev1.ExecutionPayloadHeaderElectra, error) {
txs, err := payload.Transactions()
if err != nil {
return nil, err
@@ -882,18 +810,13 @@ func PayloadToHeaderElectra(payload interfaces.ExecutionData) (*enginev1.Executi
return nil, err
}
depositReceipts, err := payload.DepositReceipts()
if err != nil {
return nil, err
}
depositReceipts := payload.DepositReceipts()
depositReceiptsRoot, err := ssz.DepositReceiptSliceRoot(depositReceipts, fieldparams.MaxDepositReceiptsPerPayload)
if err != nil {
return nil, err
}
withdrawalRequests, err := payload.WithdrawalRequests()
if err != nil {
return nil, err
}
withdrawalRequests := payload.WithdrawalRequests()
withdrawalRequestsRoot, err := ssz.WithdrawalRequestSliceRoot(withdrawalRequests, fieldparams.MaxWithdrawalRequestsPerPayload)
if err != nil {
return nil, err
@@ -980,23 +903,14 @@ func IsEmptyExecutionData(data interfaces.ExecutionData) (bool, error) {
return false, nil
}
drs, err := data.DepositReceipts()
switch {
case errors.Is(err, consensus_types.ErrUnsupportedField):
case err != nil:
return false, err
default:
epe, postElectra := data.(interfaces.ExecutionDataElectra)
if postElectra {
drs := epe.DepositReceipts()
if len(drs) != 0 {
return false, nil
}
}
wrs, err := data.WithdrawalRequests()
switch {
case errors.Is(err, consensus_types.ErrUnsupportedField):
case err != nil:
return false, err
default:
wrs := epe.WithdrawalRequests()
if len(wrs) != 0 {
return false, nil
}
@@ -1014,6 +928,8 @@ type executionPayloadHeaderDeneb struct {
gweiValue uint64
}
var _ interfaces.ExecutionData = &executionPayloadHeaderDeneb{}
// WrappedExecutionPayloadHeaderDeneb is a constructor which wraps a protobuf execution header into an interface.
func WrappedExecutionPayloadHeaderDeneb(p *enginev1.ExecutionPayloadHeaderDeneb, value math.Wei) (interfaces.ExecutionData, error) {
w := executionPayloadHeaderDeneb{p: p, weiValue: value, gweiValue: uint64(math.WeiToGwei(value))}
@@ -1158,26 +1074,6 @@ func (e executionPayloadHeaderDeneb) ExcessBlobGas() (uint64, error) {
return e.p.ExcessBlobGas, nil
}
// ValueInWei --
func (e executionPayloadHeaderDeneb) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadHeaderDeneb) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadHeaderDeneb) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayloadHeaderDeneb) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// IsBlinded returns true if the underlying data is blinded.
func (e executionPayloadHeaderDeneb) IsBlinded() bool {
return true
@@ -1192,6 +1088,8 @@ type executionPayloadDeneb struct {
gweiValue uint64
}
var _ interfaces.ExecutionData = &executionPayloadDeneb{}
// WrappedExecutionPayloadDeneb is a constructor which wraps a protobuf execution payload into an interface.
func WrappedExecutionPayloadDeneb(p *enginev1.ExecutionPayloadDeneb, value math.Wei) (interfaces.ExecutionData, error) {
w := executionPayloadDeneb{p: p, weiValue: value, gweiValue: uint64(math.WeiToGwei(value))}
@@ -1334,26 +1232,6 @@ func (e executionPayloadDeneb) ExcessBlobGas() (uint64, error) {
return e.p.ExcessBlobGas, nil
}
// ValueInWei --
func (e executionPayloadDeneb) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadDeneb) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadDeneb) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
}
// WithdrawalRequests --
func (e executionPayloadDeneb) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return nil, consensus_types.ErrUnsupportedField
}
// IsBlinded returns true if the underlying data is blinded.
func (e executionPayloadDeneb) IsBlinded() bool {
return false
@@ -1368,6 +1246,9 @@ type executionPayloadHeaderElectra struct {
gweiValue uint64
}
var _ interfaces.ExecutionData = &executionPayloadElectra{}
var _ interfaces.ExecutionDataElectra = &executionPayloadElectra{}
// WrappedExecutionPayloadHeaderElectra is a constructor which wraps a protobuf execution header into an interface.
func WrappedExecutionPayloadHeaderElectra(p *enginev1.ExecutionPayloadHeaderElectra, value math.Wei) (interfaces.ExecutionData, error) {
w := executionPayloadHeaderElectra{p: p, weiValue: value, gweiValue: uint64(math.WeiToGwei(value))}
@@ -1512,36 +1393,6 @@ func (e executionPayloadHeaderElectra) ExcessBlobGas() (uint64, error) {
return e.p.ExcessBlobGas, nil
}
// PbElectra --
func (e executionPayloadHeaderElectra) PbElectra() (*enginev1.ExecutionPayloadElectra, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbDeneb --
func (executionPayloadHeaderElectra) PbDeneb() (*enginev1.ExecutionPayloadDeneb, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbBellatrix --
func (executionPayloadHeaderElectra) PbBellatrix() (*enginev1.ExecutionPayload, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbCapella --
func (executionPayloadHeaderElectra) PbCapella() (*enginev1.ExecutionPayloadCapella, error) {
return nil, consensus_types.ErrUnsupportedField
}
// ValueInWei --
func (e executionPayloadHeaderElectra) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadHeaderElectra) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadHeaderElectra) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return nil, consensus_types.ErrUnsupportedField
@@ -1575,6 +1426,9 @@ func WrappedExecutionPayloadElectra(p *enginev1.ExecutionPayloadElectra, value m
return w, nil
}
var _ interfaces.ExecutionData = &executionPayloadElectra{}
var _ interfaces.ExecutionDataElectra = &executionPayloadElectra{}
// IsNil checks if the underlying data is nil.
func (e executionPayloadElectra) IsNil() bool {
return e.p == nil
@@ -1708,60 +1562,17 @@ func (e executionPayloadElectra) ExcessBlobGas() (uint64, error) {
return e.p.ExcessBlobGas, nil
}
// PbBellatrix --
func (e executionPayloadElectra) PbBellatrix() (*enginev1.ExecutionPayload, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbCapella --
func (e executionPayloadElectra) PbCapella() (*enginev1.ExecutionPayloadCapella, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbDeneb --
func (e executionPayloadElectra) PbDeneb() (*enginev1.ExecutionPayloadDeneb, error) {
return nil, consensus_types.ErrUnsupportedField
}
// PbElectra --
func (e executionPayloadElectra) PbElectra() (*enginev1.ExecutionPayloadElectra, error) {
return e.p, nil
}
// ValueInWei --
func (e executionPayloadElectra) ValueInWei() (math.Wei, error) {
return e.weiValue, nil
}
// ValueInGwei --
func (e executionPayloadElectra) ValueInGwei() (uint64, error) {
return e.gweiValue, nil
}
// DepositReceipts --
func (e executionPayloadElectra) DepositReceipts() ([]*enginev1.DepositReceipt, error) {
return e.p.DepositReceipts, nil
func (e executionPayloadElectra) DepositReceipts() []*enginev1.DepositReceipt {
return e.p.DepositReceipts
}
// WithdrawalRequests --
func (e executionPayloadElectra) WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error) {
return e.p.WithdrawalRequests, nil
func (e executionPayloadElectra) WithdrawalRequests() []*enginev1.ExecutionLayerWithdrawalRequest {
return e.p.WithdrawalRequests
}
// IsBlinded returns true if the underlying data is blinded.
func (e executionPayloadElectra) IsBlinded() bool {
return false
}
// PayloadValueToWei returns a Wei value given the payload's value
func PayloadValueToWei(value []byte) math.Wei {
// We have to convert big endian to little endian because the value is coming from the execution layer.
return big.NewInt(0).SetBytes(bytesutil.ReverseByteOrder(value))
}
// PayloadValueToGwei returns a Gwei value given the payload's value
func PayloadValueToGwei(value []byte) math.Gwei {
// We have to convert big endian to little endian because the value is coming from the execution layer.
v := big.NewInt(0).SetBytes(bytesutil.ReverseByteOrder(value))
return math.WeiToGwei(v)
}

View File

@@ -108,12 +108,6 @@ func TestWrapExecutionPayloadCapella(t *testing.T) {
}
payload, err := blocks.WrappedExecutionPayloadCapella(data, big.NewInt(10*1e9))
require.NoError(t, err)
wei, err := payload.ValueInWei()
require.NoError(t, err)
assert.Equal(t, 0, big.NewInt(10*1e9).Cmp(wei))
gwei, err := payload.ValueInGwei()
require.NoError(t, err)
assert.Equal(t, uint64(10), gwei)
assert.DeepEqual(t, data, payload.Proto())
}
@@ -139,13 +133,6 @@ func TestWrapExecutionPayloadHeaderCapella(t *testing.T) {
payload, err := blocks.WrappedExecutionPayloadHeaderCapella(data, big.NewInt(10*1e9))
require.NoError(t, err)
wei, err := payload.ValueInWei()
require.NoError(t, err)
assert.Equal(t, 0, big.NewInt(10*1e9).Cmp(wei))
gwei, err := payload.ValueInGwei()
require.NoError(t, err)
assert.Equal(t, uint64(10), gwei)
assert.DeepEqual(t, data, payload.Proto())
txRoot, err := payload.TransactionsRoot()
@@ -238,12 +225,6 @@ func TestWrapExecutionPayloadDeneb(t *testing.T) {
}
payload, err := blocks.WrappedExecutionPayloadDeneb(data, big.NewInt(420*1e9))
require.NoError(t, err)
wei, err := payload.ValueInWei()
require.NoError(t, err)
assert.Equal(t, 0, big.NewInt(420*1e9).Cmp(wei))
gwei, err := payload.ValueInGwei()
require.NoError(t, err)
assert.Equal(t, uint64(420), gwei)
g, err := payload.BlobGasUsed()
require.NoError(t, err)
@@ -277,13 +258,6 @@ func TestWrapExecutionPayloadHeaderDeneb(t *testing.T) {
payload, err := blocks.WrappedExecutionPayloadHeaderDeneb(data, big.NewInt(10*1e9))
require.NoError(t, err)
wei, err := payload.ValueInWei()
require.NoError(t, err)
assert.Equal(t, 0, big.NewInt(10*1e9).Cmp(wei))
gwei, err := payload.ValueInGwei()
require.NoError(t, err)
assert.Equal(t, uint64(10), gwei)
g, err := payload.BlobGasUsed()
require.NoError(t, err)
require.DeepEqual(t, uint64(88), g)

View File

@@ -21,6 +21,8 @@ var (
// errUnsupportedBeaconBlockBody is returned when the struct type is not a supported beacon block body
// type.
errUnsupportedBeaconBlockBody = errors.New("unsupported beacon block body")
// ErrIsNil is used when a value fails an IsNil check.
ErrIsNil = errors.New("unexpected nil value")
// ErrNilObject is returned in a constructor when the underlying object is nil.
ErrNilObject = errors.New("received nil object")
// ErrNilSignedBeaconBlock is returned when a nil signed beacon block is received.
@@ -460,10 +462,11 @@ func BuildSignedBeaconBlockFromExecutionPayload(blk interfaces.ReadOnlySignedBea
if err != nil {
return nil, err
}
consolidations, err := b.Body().Consolidations()
if err != nil {
return nil, err
electraBody, ok := b.Body().(interfaces.ROBlockBodyElectra)
if !ok {
return nil, errors.Wrapf(interfaces.ErrInvalidCast, "%T does not support electra getters", b.Body())
}
consolidations := electraBody.Consolidations()
var atts []*eth.AttestationElectra
if b.Body().Attestations() != nil {
atts = make([]*eth.AttestationElectra, len(b.Body().Attestations()))

View File

@@ -7,6 +7,7 @@ import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -537,3 +538,26 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
require.DeepEqual(t, uint64(321), payload.BlobGasUsed)
})
}
func TestElectraBlockBodyCast(t *testing.T) {
t.Run("deneb cast fails", func(t *testing.T) {
pb := &eth.BeaconBlockBodyDeneb{}
i, err := NewBeaconBlockBody(pb)
require.NoError(t, err)
b, ok := i.(*BeaconBlockBody)
require.Equal(t, true, ok)
assert.Equal(t, version.Deneb, b.version)
_, err = interfaces.AsROBlockBodyElectra(b)
require.ErrorIs(t, err, interfaces.ErrInvalidCast)
})
t.Run("electra cast succeeds", func(t *testing.T) {
pb := &eth.BeaconBlockBodyElectra{}
i, err := NewBeaconBlockBody(pb)
require.NoError(t, err)
b, ok := i.(*BeaconBlockBody)
require.Equal(t, true, ok)
assert.Equal(t, version.Electra, b.version)
_, err = interfaces.AsROBlockBodyElectra(b)
require.NoError(t, err)
})
}

View File

@@ -2,7 +2,6 @@ package blocks
import (
"fmt"
"math/big"
"github.com/pkg/errors"
ssz "github.com/prysmaticlabs/fastssz"
@@ -10,14 +9,26 @@ import (
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
log "github.com/sirupsen/logrus"
)
// IsNiler describes a type that provides a method that can be used to determine if it holds a nil value.
type IsNiler interface {
IsNil() bool
}
// HasNilErr uses the IsNil method of the provided IsNiler. If IsNil returns true, an error is returned.
// If IsNil is false, the return value is nil.
func HasNilErr(n IsNiler) error {
if n == nil || n.IsNil() {
return ErrIsNil
}
return nil
}
// BeaconBlockIsNil checks if any composite field of input signed beacon block is nil.
// Access to these nil fields will result in run time panic,
// it is recommended to run these checks as first line of defense.
@@ -252,7 +263,11 @@ func (b *SignedBeaconBlock) ToBlinded() (interfaces.ReadOnlySignedBeaconBlock, e
Signature: b.signature[:],
})
case *enginev1.ExecutionPayloadElectra:
header, err := PayloadToHeaderElectra(payload)
pe, ok := payload.(interfaces.ExecutionDataElectra)
if !ok {
return nil, interfaces.ErrIncompatibleFork
}
header, err := PayloadToHeaderElectra(pe)
if err != nil {
return nil, err
}
@@ -281,7 +296,6 @@ func (b *SignedBeaconBlock) ToBlinded() (interfaces.ReadOnlySignedBeaconBlock, e
},
Signature: b.signature[:],
})
default:
return nil, fmt.Errorf("%T is not an execution payload header", p)
}
@@ -325,44 +339,6 @@ func (b *SignedBeaconBlock) IsBlinded() bool {
return b.version >= version.Bellatrix && b.block.body.executionPayload == nil
}
// ValueInWei metadata on the payload value returned by the builder.
func (b *SignedBeaconBlock) ValueInWei() math.Wei {
exec, err := b.block.body.Execution()
if err != nil {
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
log.WithError(err).Warn("failed to retrieve execution payload")
}
return big.NewInt(0)
}
val, err := exec.ValueInWei()
if err != nil {
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
log.WithError(err).Warn("failed to retrieve execution payload")
}
return big.NewInt(0)
}
return val
}
// ValueInGwei metadata on the payload value returned by the builder.
func (b *SignedBeaconBlock) ValueInGwei() uint64 {
exec, err := b.block.body.Execution()
if err != nil {
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
log.WithError(err).Warn("failed to retrieve execution payload")
}
return 0
}
val, err := exec.ValueInGwei()
if err != nil {
if !errors.Is(err, consensus_types.ErrUnsupportedField) {
log.WithError(err).Warn("failed to retrieve execution payload")
}
return 0
}
return val
}
// Header converts the underlying protobuf object from blinded block to header format.
func (b *SignedBeaconBlock) Header() (*eth.SignedBeaconBlockHeader, error) {
if b.IsNil() {
@@ -1170,11 +1146,8 @@ func (b *BeaconBlockBody) BlobKzgCommitments() ([][]byte, error) {
}
}
func (b *BeaconBlockBody) Consolidations() ([]*eth.SignedConsolidation, error) {
if b.version < version.Electra {
return nil, consensus_types.ErrNotSupported("Consolidations", b.version)
}
return b.signedConsolidations, nil
func (b *BeaconBlockBody) Consolidations() []*eth.SignedConsolidation {
return b.signedConsolidations
}
// Version returns the version of the beacon block body

View File

@@ -490,3 +490,9 @@ func hydrateBeaconBlockBody() *eth.BeaconBlockBody {
},
}
}
func TestPreElectraFailsInterfaceAssertion(t *testing.T) {
var epd interfaces.ExecutionData = &executionPayloadDeneb{}
_, ok := epd.(interfaces.ExecutionDataElectra)
require.Equal(t, false, ok)
}

View File

@@ -57,6 +57,9 @@ type BeaconBlockBody struct {
signedConsolidations []*eth.SignedConsolidation
}
var _ interfaces.ReadOnlyBeaconBlockBody = &BeaconBlockBody{}
var _ interfaces.ROBlockBodyElectra = &BeaconBlockBody{}
// BeaconBlock is the main beacon block structure. It can represent any block type.
type BeaconBlock struct {
version int

View File

@@ -4,6 +4,8 @@ go_library(
name = "go_default_library",
srcs = [
"beacon_block.go",
"cast.go",
"error.go",
"utils.go",
],
importpath = "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces",
@@ -11,10 +13,10 @@ go_library(
deps = [
"//config/fieldparams:go_default_library",
"//consensus-types/primitives:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
@@ -24,14 +26,19 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["utils_test.go"],
srcs = [
"error_test.go",
"utils_test.go",
],
embed = [":go_default_library"],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/blocks:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)

View File

@@ -1,17 +1,19 @@
package interfaces
import (
"github.com/pkg/errors"
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/go-bitfield"
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
"google.golang.org/protobuf/proto"
)
var ErrIncompatibleFork = errors.New("Can't convert to fork-specific interface")
// ReadOnlySignedBeaconBlock is an interface describing the method set of
// a signed beacon block.
type ReadOnlySignedBeaconBlock interface {
@@ -26,8 +28,6 @@ type ReadOnlySignedBeaconBlock interface {
ssz.Unmarshaler
Version() int
IsBlinded() bool
ValueInWei() math.Wei
ValueInGwei() uint64
Header() (*ethpb.SignedBeaconBlockHeader, error)
}
@@ -70,7 +70,11 @@ type ReadOnlyBeaconBlockBody interface {
Execution() (ExecutionData, error)
BLSToExecutionChanges() ([]*ethpb.SignedBLSToExecutionChange, error)
BlobKzgCommitments() ([][]byte, error)
Consolidations() ([]*ethpb.SignedConsolidation, error)
}
type ROBlockBodyElectra interface {
ReadOnlyBeaconBlockBody
Consolidations() []*ethpb.SignedConsolidation
}
type SignedBeaconBlock interface {
@@ -95,12 +99,20 @@ type SignedBeaconBlock interface {
Unblind(e ExecutionData) error
}
// ProtoSSZ is satisfied by any of the types that use our protobuf + fastssz codegen.
type ProtoSSZ interface {
MarshalSSZ() ([]byte, error)
MarshalSSZTo(dst []byte) ([]byte, error)
SizeSSZ() int
UnmarshalSSZ(buf []byte) error
HashTreeRoot() ([32]byte, error)
HashTreeRootWith(hh *ssz.Hasher) error
}
// ExecutionData represents execution layer information that is contained
// within post-Bellatrix beacon block bodies.
type ExecutionData interface {
ssz.Marshaler
ssz.Unmarshaler
ssz.HashRoot
ProtoSSZ
IsNil() bool
IsBlinded() bool
Proto() proto.Message
@@ -123,17 +135,17 @@ type ExecutionData interface {
TransactionsRoot() ([]byte, error)
Withdrawals() ([]*enginev1.Withdrawal, error)
WithdrawalsRoot() ([]byte, error)
ValueInWei() (math.Wei, error)
ValueInGwei() (uint64, error)
DepositReceipts() ([]*enginev1.DepositReceipt, error)
WithdrawalRequests() ([]*enginev1.ExecutionLayerWithdrawalRequest, error)
}
type ExecutionDataElectra interface {
ExecutionData
DepositReceipts() []*enginev1.DepositReceipt
WithdrawalRequests() []*enginev1.ExecutionLayerWithdrawalRequest
}
type Attestation interface {
proto.Message
ssz.Marshaler
ssz.Unmarshaler
ssz.HashRoot
ProtoSSZ
Version() int
GetAggregationBits() bitfield.Bitlist
GetData() *ethpb.AttestationData
@@ -143,9 +155,7 @@ type Attestation interface {
type AttesterSlashing interface {
proto.Message
ssz.Marshaler
ssz.Unmarshaler
ssz.HashRoot
ProtoSSZ
Version() int
GetFirstAttestation() ethpb.IndexedAtt
GetSecondAttestation() ethpb.IndexedAtt

View File

@@ -0,0 +1,15 @@
package interfaces
import "github.com/prysmaticlabs/prysm/v5/runtime/version"
// AsROBlockBodyElectra safely asserts the ReadOnlyBeaconBlockBody to a ROBlockBodyElectra.
// This allows the caller to access methods on the block body which are only available on values after
// the Electra hard fork. If the value is for an earlier fork (based on comparing its Version() to the electra version)
// an error will be returned. Callers that want to conditionally process electra data can check for this condition
// and safely ignore it like `if err != nil && errors.Is(interfaces.ErrInvalidCast) {`
func AsROBlockBodyElectra(in ReadOnlyBeaconBlockBody) (ROBlockBodyElectra, error) {
if in.Version() >= version.Electra {
return in.(ROBlockBodyElectra), nil
}
return nil, NewInvalidCastError(in.Version(), version.Electra)
}

View File

@@ -0,0 +1,27 @@
package interfaces
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
var ErrInvalidCast = errors.New("unable to cast between types")
type InvalidCastError struct {
from int
to int
}
func (e *InvalidCastError) Error() string {
return errors.Wrapf(ErrInvalidCast,
"from=%s(%d), to=%s(%d)", version.String(e.from), e.from, version.String(e.to), e.to).
Error()
}
func (*InvalidCastError) Is(err error) bool {
return errors.Is(err, ErrInvalidCast)
}
func NewInvalidCastError(from, to int) *InvalidCastError {
return &InvalidCastError{from: from, to: to}
}

View File

@@ -0,0 +1,14 @@
package interfaces
import (
"testing"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestNewInvalidCastError(t *testing.T) {
err := NewInvalidCastError(version.Phase0, version.Electra)
require.Equal(t, true, errors.Is(err, ErrInvalidCast))
}

View File

@@ -9,7 +9,6 @@ go_library(
"//config/fieldparams:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",

View File

@@ -5,7 +5,6 @@ import (
field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
"google.golang.org/protobuf/proto"
@@ -75,14 +74,6 @@ func (SignedBeaconBlock) Header() (*eth.SignedBeaconBlockHeader, error) {
panic("implement me")
}
func (SignedBeaconBlock) ValueInWei() math.Wei {
panic("implement me")
}
func (SignedBeaconBlock) ValueInGwei() uint64 {
panic("implement me")
}
type BeaconBlock struct {
Htr [field_params.RootLength]byte
HtrErr error
@@ -284,8 +275,7 @@ func (b *BeaconBlockBody) BlobKzgCommitments() ([][]byte, error) {
func (b *BeaconBlockBody) Attestations() []interfaces.Attestation {
panic("implement me")
}
func (b *BeaconBlockBody) Consolidations() ([]*eth.SignedConsolidation, error) {
func (b *BeaconBlockBody) Consolidations() []*eth.SignedConsolidation {
panic("implement me")
}
@@ -296,3 +286,4 @@ func (b *BeaconBlockBody) Version() int {
var _ interfaces.ReadOnlySignedBeaconBlock = &SignedBeaconBlock{}
var _ interfaces.ReadOnlyBeaconBlock = &BeaconBlock{}
var _ interfaces.ReadOnlyBeaconBlockBody = &BeaconBlockBody{}
var _ interfaces.ROBlockBodyElectra = &BeaconBlockBody{}

View File

@@ -6,6 +6,7 @@ import (
stdmath "math"
"math/big"
"math/bits"
"slices"
"sync"
"github.com/thomaso-mirodin/intmath/u64"
@@ -216,9 +217,17 @@ func AddInt(i ...int) (int, error) {
// Wei is the smallest unit of Ether, represented as a pointer to a bigInt.
type Wei *big.Int
// WeiToBigInt is a convenience method to cast a wei back to a big int
func WeiToBigInt(w Wei) *big.Int {
return w
}
// Gwei is a denomination of 1e9 Wei represented as an uint64.
type Gwei uint64
// ZeroWei is a non-nil zero value for math.Wei
var ZeroWei Wei = big.NewInt(0)
// WeiToGwei converts big int wei to uint64 gwei.
// The input `v` is copied before being modified.
func WeiToGwei(v Wei) Gwei {
@@ -235,3 +244,17 @@ func WeiToGwei(v Wei) Gwei {
func IsValidUint256(bi *big.Int) bool {
return bi.Cmp(big.NewInt(0)) >= 0 && bi.BitLen() <= 256
}
// BigEndianBytesToWei returns a Wei value given a big-endian binary representation (eg engine api payload bids).
func BigEndianBytesToWei(value []byte) Wei {
v := make([]byte, len(value))
copy(v, value)
slices.Reverse(v)
// We have to convert big endian to little endian because the value is coming from the execution layer.
return big.NewInt(0).SetBytes(v)
}
// Uint64ToWei creates a new Wei (aka big.Int) representing the given uint64 value.
func Uint64ToWei(v uint64) Wei {
return big.NewInt(0).SetUint64(v)
}

View File

@@ -575,3 +575,9 @@ func TestWeiToGwei_CopyOk(t *testing.T) {
require.Equal(t, math.Gwei(1), got)
require.Equal(t, big.NewInt(1e9).Uint64(), v.Uint64())
}
func TestWeiStringer(t *testing.T) {
require.Equal(t, "0", math.WeiToBigInt(math.ZeroWei).String())
require.Equal(t, "1234", math.WeiToBigInt(math.Uint64ToWei(1234)).String())
require.Equal(t, "18446744073709551615", math.WeiToBigInt(math.Uint64ToWei(stdmath.MaxUint64)).String())
}