mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
12 Commits
v6.0.1
...
narrower-E
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
817d30844b | ||
|
|
59ce7542df | ||
|
|
0a7acc88a5 | ||
|
|
71ab24806b | ||
|
|
eed9e61297 | ||
|
|
371b6435a4 | ||
|
|
74e49b4062 | ||
|
|
d5d6e9e1f1 | ||
|
|
5b2f60f706 | ||
|
|
ff10f906ad | ||
|
|
3610879b56 | ||
|
|
e1620c96f6 |
@@ -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 --
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 --
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blockProto.(*ethpb.BlindedBeaconBlockDeneb)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
|
||||
}
|
||||
denebContents := ðpb.BeaconBlockContentsDeneb{Block: blockProto.(*ethpb.BeaconBlockDeneb)}
|
||||
if bundle != nil {
|
||||
denebContents.KzgProofs = bundle.Proofs
|
||||
denebContents.Blobs = bundle.Blobs
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.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 ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb.(*ethpb.BlindedBeaconBlockCapella)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.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 ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb.(*ethpb.BlindedBeaconBlockBellatrix)}, IsBlinded: true, PayloadValue: (*payloadValue).String()}
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb.(*ethpb.BeaconBlockBellatrix)}, IsBlinded: false, PayloadValue: (*payloadValue).String()}
|
||||
}
|
||||
|
||||
func (vs *Server) constructAltairBlock(pb proto.Message) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: pb.(*ethpb.BeaconBlockAltair)}}
|
||||
}
|
||||
|
||||
func (vs *Server) constructPhase0Block(pb proto.Message) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: pb.(*ethpb.BeaconBlock)}}
|
||||
}
|
||||
|
||||
@@ -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 := ð.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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -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 = ðpb.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() {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 = ðpb.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 := ðpb.BeaconBlockContentsDeneb{Block: pb}
|
||||
if bundle != nil {
|
||||
denebContents.KzgProofs = bundle.Proofs
|
||||
denebContents.Blobs = bundle.Blobs
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Deneb{Deneb: denebContents}, IsBlinded: false, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BlindedBeaconBlockDeneb:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BeaconBlockCapella:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: pb}, IsBlinded: false, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BlindedBeaconBlockCapella:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BeaconBlockBellatrix:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: pb}, IsBlinded: false, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BlindedBeaconBlockBellatrix:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: pb}, IsBlinded: true, PayloadValue: payloadValue}, nil
|
||||
case *ethpb.BeaconBlockAltair:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: pb}}, nil
|
||||
case *ethpb.BeaconBlock:
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.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)
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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 := ð.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 := ð.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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
15
consensus-types/interfaces/cast.go
Normal file
15
consensus-types/interfaces/cast.go
Normal 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)
|
||||
}
|
||||
27
consensus-types/interfaces/error.go
Normal file
27
consensus-types/interfaces/error.go
Normal 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}
|
||||
}
|
||||
14
consensus-types/interfaces/error_test.go
Normal file
14
consensus-types/interfaces/error_test.go
Normal 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))
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user