From 8f8659221d6fed9bc7fec7c2a51944a90503017a Mon Sep 17 00:00:00 2001 From: Kasey Kirkham Date: Fri, 25 Oct 2024 14:37:17 -0500 Subject: [PATCH] defer payload attribute computation --- beacon-chain/blockchain/execution_engine.go | 25 ++ beacon-chain/blockchain/process_block.go | 5 - beacon-chain/core/feed/state/events.go | 2 + beacon-chain/rpc/eth/events/BUILD.bazel | 3 +- beacon-chain/rpc/eth/events/events.go | 251 ++++++++++-------- beacon-chain/rpc/eth/events/events_test.go | 2 +- consensus-types/payload-attribute/BUILD.bazel | 4 + consensus-types/payload-attribute/getters.go | 13 + .../payload-attribute/interface.go | 1 + consensus-types/payload-attribute/types.go | 18 ++ 10 files changed, 212 insertions(+), 112 deletions(-) diff --git a/beacon-chain/blockchain/execution_engine.go b/beacon-chain/blockchain/execution_engine.go index 0317098b0923..7bb2c2f56719 100644 --- a/beacon-chain/blockchain/execution_engine.go +++ b/beacon-chain/blockchain/execution_engine.go @@ -8,6 +8,8 @@ import ( "github.com/pkg/errors" "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/feed" + statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" @@ -69,6 +71,29 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (* if arg.attributes == nil { arg.attributes = payloadattribute.EmptyWithVersion(headBlk.Version()) } + go func() { + pidx, err := helpers.BeaconProposerIndex(ctx, arg.headState) + if err != nil { + log.WithError(err). + WithField("head_root", arg.headRoot[:]). + Error("Could not get proposer index for PayloadAttributes event") + return + } + s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ + Type: statefeed.PayloadAttributes, + Data: payloadattribute.EventData{ + ProposerIndex: pidx, + ProposalSlot: arg.headState.Slot(), + ParentBlockNumber: headPayload.BlockNumber(), + ParentBlockRoot: arg.headRoot[:], + ParentBlockHash: headPayload.BlockHash(), + Attributer: arg.attributes, + HeadRoot: arg.headRoot, + HeadState: arg.headState, + HeadBlock: arg.headBlock, + }, + }) + }() payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, arg.attributes) if err != nil { switch { diff --git a/beacon-chain/blockchain/process_block.go b/beacon-chain/blockchain/process_block.go index e2889507b801..66b4a3f54a7d 100644 --- a/beacon-chain/blockchain/process_block.go +++ b/beacon-chain/blockchain/process_block.go @@ -7,8 +7,6 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed" - statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" @@ -620,9 +618,6 @@ func (s *Service) lateBlockTasks(ctx context.Context) { if !s.inRegularSync() { return } - s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ - Type: statefeed.MissedSlot, - }) s.headLock.RLock() headRoot := s.headRoot() headState := s.headState(ctx) diff --git a/beacon-chain/core/feed/state/events.go b/beacon-chain/core/feed/state/events.go index 1a31d10cfee3..cc6f7f126249 100644 --- a/beacon-chain/core/feed/state/events.go +++ b/beacon-chain/core/feed/state/events.go @@ -31,6 +31,8 @@ const ( LightClientFinalityUpdate // LightClientOptimisticUpdate event LightClientOptimisticUpdate + // PayloadAttributes events are fired upon a missed slot or new head. + PayloadAttributes ) // BlockProcessedData is the data sent with BlockProcessed events. diff --git a/beacon-chain/rpc/eth/events/BUILD.bazel b/beacon-chain/rpc/eth/events/BUILD.bazel index ad6b09eb30bd..f2404e34083f 100644 --- a/beacon-chain/rpc/eth/events/BUILD.bazel +++ b/beacon-chain/rpc/eth/events/BUILD.bazel @@ -19,11 +19,12 @@ go_library( "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/time:go_default_library", - "//beacon-chain/core/transition:go_default_library", "//config/params:go_default_library", + "//consensus-types/payload-attribute:go_default_library", "//consensus-types/primitives:go_default_library", "//monitoring/tracing/trace:go_default_library", "//network/httputil:go_default_library", + "//proto/engine/v1:go_default_library", "//proto/eth/v1:go_default_library", "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/beacon-chain/rpc/eth/events/events.go b/beacon-chain/rpc/eth/events/events.go index 92071fda9d2a..853171b7f5d9 100644 --- a/beacon-chain/rpc/eth/events/events.go +++ b/beacon-chain/rpc/eth/events/events.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "strconv" "time" "github.com/ethereum/go-ethereum/common/hexutil" @@ -18,11 +19,12 @@ import ( statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" chaintime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/v5/config/params" + payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" "github.com/prysmaticlabs/prysm/v5/network/httputil" + engine "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" @@ -31,6 +33,7 @@ import ( ) const DefaultEventFeedDepth = 1000 +const payloadAttributeTimeout = 2 * time.Second const ( InvalidTopic = "__invalid__" @@ -89,12 +92,12 @@ var opsFeedEventTopics = map[feed.EventType]string{ var stateFeedEventTopics = map[feed.EventType]string{ statefeed.NewHead: HeadTopic, - statefeed.MissedSlot: PayloadAttributesTopic, statefeed.FinalizedCheckpoint: FinalizedCheckpointTopic, statefeed.LightClientFinalityUpdate: LightClientFinalityUpdateTopic, statefeed.LightClientOptimisticUpdate: LightClientOptimisticUpdateTopic, statefeed.Reorg: ChainReorgTopic, statefeed.BlockProcessed: BlockTopic, + statefeed.PayloadAttributes: PayloadAttributesTopic, } var topicsForStateFeed = topicsForFeed(stateFeedEventTopics) @@ -418,10 +421,9 @@ func topicForEvent(event *feed.Event) string { return ChainReorgTopic case *statefeed.BlockProcessedData: return BlockTopic + case payloadattribute.EventData: + return PayloadAttributesTopic default: - if event.Type == statefeed.MissedSlot { - return PayloadAttributesTopic - } return InvalidTopic } } @@ -431,31 +433,17 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi if !topics.requested(eventName) { return nil, errNotRequested } - if eventName == PayloadAttributesTopic { - return s.currentPayloadAttributes(ctx) - } if event == nil || event.Data == nil { return nil, errors.New("event or event data is nil") } switch v := event.Data.(type) { + case payloadattribute.EventData: + return s.payloadAttributesReader(ctx, v) case *ethpb.EventHead: // The head event is a special case because, if the client requested the payload attributes topic, // we send two event messages in reaction; the head event and the payload attributes. - headReader := func() io.Reader { - return jsonMarshalReader(eventName, structs.HeadEventFromV1(v)) - } - // Don't do the expensive attr lookup unless the client requested it. - if !topics.requested(PayloadAttributesTopic) { - return headReader, nil - } - // Since payload attributes could change before the outbox is written, we need to do a blocking operation to - // get the current payload attributes right here. - attrReader, err := s.currentPayloadAttributes(ctx) - if err != nil { - return nil, errors.Wrap(err, "could not get payload attributes for head event") - } return func() io.Reader { - return io.MultiReader(headReader(), attrReader()) + return jsonMarshalReader(eventName, structs.HeadEventFromV1(v)) }, nil case *operation.AggregatedAttReceivedData: return func() io.Reader { @@ -556,112 +544,165 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi } } -// This event stream is intended to be used by builders and relays. -// Parent fields are based on state at N_{current_slot}, while the rest of fields are based on state of N_{current_slot + 1} -func (s *Server) currentPayloadAttributes(ctx context.Context) (lazyReader, error) { - headRoot, err := s.HeadFetcher.HeadRoot(ctx) - if err != nil { - return nil, errors.Wrap(err, "could not get head root") +var errUnsupportedPayloadAttribute = errors.New("cannot compute payload attributes pre-Bellatrix") + +func (s *Server) computePayloadAttributes(ctx context.Context, ev payloadattribute.EventData) (payloadattribute.Attributer, error) { + v := ev.HeadState.Version() + if v < version.Bellatrix { + return nil, errors.Wrapf(errUnsupportedPayloadAttribute, "%s is not supported", version.String(v)) } - st, err := s.HeadFetcher.HeadState(ctx) + + t, err := slots.ToTime(ev.HeadState.GenesisTime(), ev.HeadState.Slot()) if err != nil { - return nil, errors.Wrap(err, "could not get head state") + return nil, errors.Wrap(err, "could not get head state slot time") } - // advance the head state - headState, err := transition.ProcessSlotsIfPossible(ctx, st, s.ChainInfoFetcher.CurrentSlot()+1) + timestamp := uint64(t.Unix()) + prevRando, err := helpers.RandaoMix(ev.HeadState, chaintime.CurrentEpoch(ev.HeadState)) if err != nil { - return nil, errors.Wrap(err, "could not advance head state") + return nil, errors.Wrap(err, "could not get head state randao mix") } - - headBlock, err := s.HeadFetcher.HeadBlock(ctx) + proposerIndex, err := helpers.BeaconProposerIndex(ctx, ev.HeadState) if err != nil { - return nil, errors.Wrap(err, "could not get head block") + return nil, errors.Wrap(err, "could not get head state proposer index") + } + feeRecpt := params.BeaconConfig().DefaultFeeRecipient.Bytes() + tValidator, exists := s.TrackedValidatorsCache.Validator(proposerIndex) + if exists { + feeRecpt = tValidator.FeeRecipient[:] } - headPayload, err := headBlock.Block().Body().Execution() - if err != nil { - return nil, errors.Wrap(err, "could not get execution payload") + if v == version.Bellatrix { + return payloadattribute.New(&engine.PayloadAttributes{ + Timestamp: timestamp, + PrevRandao: prevRando, + SuggestedFeeRecipient: feeRecpt, + }) } - t, err := slots.ToTime(headState.GenesisTime(), headState.Slot()) + w, _, err := ev.HeadState.ExpectedWithdrawals() if err != nil { - return nil, errors.Wrap(err, "could not get head state slot time") + return nil, errors.Wrap(err, "could not get withdrawals from head state") + } + if v == version.Capella { + return payloadattribute.New(&engine.PayloadAttributesV2{ + Timestamp: timestamp, + PrevRandao: prevRando, + SuggestedFeeRecipient: feeRecpt, + Withdrawals: w, + }) } - prevRando, err := helpers.RandaoMix(headState, chaintime.CurrentEpoch(headState)) + pr, err := ev.HeadBlock.Block().HashTreeRoot() if err != nil { - return nil, errors.Wrap(err, "could not get head state randao mix") + return nil, errors.Wrap(err, "could not compute head block root") + } + return payloadattribute.New(&engine.PayloadAttributesV3{ + Timestamp: timestamp, + PrevRandao: prevRando, + SuggestedFeeRecipient: feeRecpt, + Withdrawals: w, + ParentBeaconBlockRoot: pr[:], + }) +} + +func (s *Server) marshalAttributes(ctx context.Context, attr payloadattribute.Attributer) ([]byte, error) { + v := attr.Version() + if v < version.Bellatrix { + return nil, errors.Wrapf(errUnsupportedPayloadAttribute, "Payload version %s is not supported", version.String(v)) } - proposerIndex, err := helpers.BeaconProposerIndex(ctx, headState) + timestamp := strconv.FormatUint(attr.Timestamp(), 10) + prevRandao := hexutil.Encode(attr.PrevRandao()) + feeRecpt := hexutil.Encode(attr.SuggestedFeeRecipient()) + if v == version.Bellatrix { + return json.Marshal(&structs.PayloadAttributesV1{ + Timestamp: timestamp, + PrevRandao: prevRandao, + SuggestedFeeRecipient: feeRecpt, + }) + } + w, err := attr.Withdrawals() if err != nil { - return nil, errors.Wrap(err, "could not get head state proposer index") + return nil, errors.Wrap(err, "could not get withdrawals from payload attributes event") + } + withdrawals := structs.WithdrawalsFromConsensus(w) + if v == version.Capella { + return json.Marshal(&structs.PayloadAttributesV2{ + Timestamp: timestamp, + PrevRandao: prevRandao, + SuggestedFeeRecipient: feeRecpt, + Withdrawals: withdrawals, + }) } - feeRecipient := params.BeaconConfig().DefaultFeeRecipient.Bytes() - tValidator, exists := s.TrackedValidatorsCache.Validator(proposerIndex) - if exists { - feeRecipient = tValidator.FeeRecipient[:] - } - var attributes interface{} - switch headState.Version() { - case version.Bellatrix: - attributes = &structs.PayloadAttributesV1{ - Timestamp: fmt.Sprintf("%d", t.Unix()), - PrevRandao: hexutil.Encode(prevRando), - SuggestedFeeRecipient: hexutil.Encode(feeRecipient), - } - case version.Capella: - withdrawals, _, err := headState.ExpectedWithdrawals() - if err != nil { - return nil, errors.Wrap(err, "could not get head state expected withdrawals") - } - attributes = &structs.PayloadAttributesV2{ - Timestamp: fmt.Sprintf("%d", t.Unix()), - PrevRandao: hexutil.Encode(prevRando), - SuggestedFeeRecipient: hexutil.Encode(feeRecipient), - Withdrawals: structs.WithdrawalsFromConsensus(withdrawals), - } - case version.Deneb, version.Electra: - withdrawals, _, err := headState.ExpectedWithdrawals() - if err != nil { - return nil, errors.Wrap(err, "could not get head state expected withdrawals") + parentRoot, err := attr.ParentBeaconBlockRoot() + if err != nil { + return nil, errors.Wrap(err, "could not get parent beacon block root from payload attributes event") + } + return json.Marshal(&structs.PayloadAttributesV3{ + Timestamp: timestamp, + PrevRandao: prevRandao, + SuggestedFeeRecipient: feeRecpt, + Withdrawals: withdrawals, + ParentBeaconBlockRoot: hexutil.Encode(parentRoot), + }) +} + +type asyncPayloadAttrData struct { + data json.RawMessage + version string + err error +} + +// This event stream is intended to be used by builders and relays. +// Parent fields are based on state at N_{current_slot}, while the rest of fields are based on state of N_{current_slot + 1} +func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribute.EventData) (lazyReader, error) { + ctx, cancel := context.WithTimeout(ctx, payloadAttributeTimeout) + edc := make(chan asyncPayloadAttrData) + go func() { + d := asyncPayloadAttrData{} + defer func() { + edc <- d + }() + attr := ev.Attributer + if attr.IsEmpty() { + attr, d.err = s.computePayloadAttributes(ctx, ev) + if d.err != nil { + return + } } - parentRoot, err := headBlock.Block().HashTreeRoot() + attributesBytes, err := s.marshalAttributes(ctx, attr) if err != nil { - return nil, errors.Wrap(err, "could not get head block root") + d.err = errors.Wrap(err, "errors marshaling payload attributes to json") + return } - attributes = &structs.PayloadAttributesV3{ - Timestamp: fmt.Sprintf("%d", t.Unix()), - PrevRandao: hexutil.Encode(prevRando), - SuggestedFeeRecipient: hexutil.Encode(feeRecipient), - Withdrawals: structs.WithdrawalsFromConsensus(withdrawals), - ParentBeaconBlockRoot: hexutil.Encode(parentRoot[:]), + d.data, d.err = json.Marshal(structs.PayloadAttributesEventData{ + ProposerIndex: strconv.FormatUint(uint64(ev.ProposerIndex), 10), + ProposalSlot: strconv.FormatUint(uint64(ev.ProposalSlot), 10), + ParentBlockNumber: strconv.FormatUint(uint64(ev.ParentBlockNumber), 10), + ParentBlockRoot: hexutil.Encode(ev.ParentBlockRoot), + ParentBlockHash: hexutil.Encode(ev.ParentBlockHash), + PayloadAttributes: attributesBytes, + }) + if d.err != nil { + d.err = errors.Wrap(d.err, "errors marshaling payload attributes event data to json") } - default: - return nil, errors.Wrapf(err, "Payload version %s is not supported", version.String(headState.Version())) - } - - attributesBytes, err := json.Marshal(attributes) - if err != nil { - return nil, errors.Wrap(err, "errors marshaling payload attributes to json") - } - eventData := structs.PayloadAttributesEventData{ - ProposerIndex: fmt.Sprintf("%d", proposerIndex), - ProposalSlot: fmt.Sprintf("%d", headState.Slot()), - ParentBlockNumber: fmt.Sprintf("%d", headPayload.BlockNumber()), - ParentBlockRoot: hexutil.Encode(headRoot), - ParentBlockHash: hexutil.Encode(headPayload.BlockHash()), - PayloadAttributes: attributesBytes, - } - eventDataBytes, err := json.Marshal(eventData) - if err != nil { - return nil, errors.Wrap(err, "errors marshaling payload attributes event data to json") - } + }() return func() io.Reader { - return jsonMarshalReader(PayloadAttributesTopic, &structs.PayloadAttributesEvent{ - Version: version.String(headState.Version()), - Data: eventDataBytes, - }) + defer cancel() + select { + case <-ctx.Done(): + log.WithError(ctx.Err()).Warn("Context canceled while waiting for payload attributes event data") + return nil + case ed := <-edc: + if ed.err != nil { + log.WithError(ed.err).Warn("Error while marshaling payload attributes event data") + return nil + } + return jsonMarshalReader(PayloadAttributesTopic, &structs.PayloadAttributesEvent{ + Version: ed.version, + Data: ed.data, + }) + } }, nil } diff --git a/beacon-chain/rpc/eth/events/events_test.go b/beacon-chain/rpc/eth/events/events_test.go index 32daf1c7218f..8f1942e1149d 100644 --- a/beacon-chain/rpc/eth/events/events_test.go +++ b/beacon-chain/rpc/eth/events/events_test.go @@ -489,7 +489,7 @@ func TestStreamEvents_OperationsEvents(t *testing.T) { require.NoError(t, err) request := topics.testHttpRequest(testSync.ctx, t) w := NewStreamingResponseWriterRecorder(testSync.ctx) - events := []*feed.Event{&feed.Event{Type: statefeed.MissedSlot}} + events := []*feed.Event{&feed.Event{Type: statefeed.PayloadAttributes}} go func() { s.StreamEvents(w, request) diff --git a/consensus-types/payload-attribute/BUILD.bazel b/consensus-types/payload-attribute/BUILD.bazel index dc4d31bfc4df..6dc7e80e9712 100644 --- a/consensus-types/payload-attribute/BUILD.bazel +++ b/consensus-types/payload-attribute/BUILD.bazel @@ -10,8 +10,12 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute", visibility = ["//visibility:public"], deps = [ + "//beacon-chain/state:go_default_library", + "//config/fieldparams:go_default_library", "//consensus-types:go_default_library", "//consensus-types/blocks:go_default_library", + "//consensus-types/interfaces:go_default_library", + "//consensus-types/primitives:go_default_library", "//proto/engine/v1:go_default_library", "//runtime/version:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/consensus-types/payload-attribute/getters.go b/consensus-types/payload-attribute/getters.go index e32c72336e16..3eee54e14c83 100644 --- a/consensus-types/payload-attribute/getters.go +++ b/consensus-types/payload-attribute/getters.go @@ -38,6 +38,16 @@ func (a *data) Withdrawals() ([]*enginev1.Withdrawal, error) { return a.withdrawals, nil } +func (a *data) ParentBeaconBlockRoot() ([]byte, error) { + if len(a.parentBeaconBlockRoot) == 0 { + return nil, errNoParentRoot + } + if a.version < version.Deneb { + return nil, consensus_types.ErrNotSupported("ParentBeaconBlockRoot", a.version) + } + return a.parentBeaconBlockRoot, nil +} + // PbV1 returns the payload attribute in version 1. func (a *data) PbV1() (*enginev1.PayloadAttributes, error) { if a == nil { @@ -97,6 +107,9 @@ func (a *data) PbV3() (*enginev1.PayloadAttributesV3, error) { // IsEmpty returns whether the given payload attribute is empty func (a *data) IsEmpty() bool { + if a == nil { + return true + } if len(a.PrevRandao()) != 0 { return false } diff --git a/consensus-types/payload-attribute/interface.go b/consensus-types/payload-attribute/interface.go index c36e839e2eba..05f33fdf6283 100644 --- a/consensus-types/payload-attribute/interface.go +++ b/consensus-types/payload-attribute/interface.go @@ -10,6 +10,7 @@ type Attributer interface { Timestamp() uint64 SuggestedFeeRecipient() []byte Withdrawals() ([]*enginev1.Withdrawal, error) + ParentBeaconBlockRoot() ([]byte, error) PbV1() (*enginev1.PayloadAttributes, error) PbV2() (*enginev1.PayloadAttributesV2, error) PbV3() (*enginev1.PayloadAttributesV3, error) diff --git a/consensus-types/payload-attribute/types.go b/consensus-types/payload-attribute/types.go index dbb9c49d38b0..2e71f79615f7 100644 --- a/consensus-types/payload-attribute/types.go +++ b/consensus-types/payload-attribute/types.go @@ -2,7 +2,11 @@ package payloadattribute import ( "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" + field_params "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "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" "github.com/prysmaticlabs/prysm/v5/runtime/version" ) @@ -23,6 +27,7 @@ type data struct { var ( errNilPayloadAttribute = errors.New("received nil payload attribute") errUnsupportedPayloadAttribute = errors.New("unsupported payload attribute") + errNoParentRoot = errors.New("parent root is empty") ) // New returns a new payload attribute with the given input object. @@ -89,3 +94,16 @@ func initPayloadAttributeFromV3(a *enginev1.PayloadAttributesV3) (Attributer, er parentBeaconBlockRoot: a.ParentBeaconBlockRoot, }, nil } + +// EventData holds the values for a PayloadAttributes event. +type EventData struct { + ProposerIndex primitives.ValidatorIndex + ProposalSlot primitives.Slot + ParentBlockNumber uint64 + ParentBlockRoot []byte + ParentBlockHash []byte + Attributer Attributer + HeadState state.BeaconState + HeadBlock interfaces.ReadOnlySignedBeaconBlock + HeadRoot [field_params.RootLength]byte +}