diff --git a/core/validatorapi/mocks/handler.go b/core/validatorapi/mocks/handler.go index 65281d98c..5da33c729 100644 --- a/core/validatorapi/mocks/handler.go +++ b/core/validatorapi/mocks/handler.go @@ -10,8 +10,6 @@ import ( context "context" - gloas "github.com/attestantio/go-eth2-client/spec/gloas" - http "net/http" mock "github.com/stretchr/testify/mock" @@ -216,29 +214,29 @@ func (_m *Handler) NodeVersion(ctx context.Context, opts *api.NodeVersionOpts) ( return r0, r1 } -// PayloadAttestationData provides a mock function with given fields: ctx, slot -func (_m *Handler) PayloadAttestationData(ctx context.Context, slot uint64) (*gloas.PayloadAttestationData, error) { - ret := _m.Called(ctx, slot) +// PayloadAttestationData provides a mock function with given fields: ctx, opts +func (_m *Handler) PayloadAttestationData(ctx context.Context, opts *api.PayloadAttestationDataOpts) (*api.Response[*spec.VersionedPayloadAttestationData], error) { + ret := _m.Called(ctx, opts) if len(ret) == 0 { panic("no return value specified for PayloadAttestationData") } - var r0 *gloas.PayloadAttestationData + var r0 *api.Response[*spec.VersionedPayloadAttestationData] var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64) (*gloas.PayloadAttestationData, error)); ok { - return rf(ctx, slot) + if rf, ok := ret.Get(0).(func(context.Context, *api.PayloadAttestationDataOpts) (*api.Response[*spec.VersionedPayloadAttestationData], error)); ok { + return rf(ctx, opts) } - if rf, ok := ret.Get(0).(func(context.Context, uint64) *gloas.PayloadAttestationData); ok { - r0 = rf(ctx, slot) + if rf, ok := ret.Get(0).(func(context.Context, *api.PayloadAttestationDataOpts) *api.Response[*spec.VersionedPayloadAttestationData]); ok { + r0 = rf(ctx, opts) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*gloas.PayloadAttestationData) + r0 = ret.Get(0).(*api.Response[*spec.VersionedPayloadAttestationData]) } } - if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { - r1 = rf(ctx, slot) + if rf, ok := ret.Get(1).(func(context.Context, *api.PayloadAttestationDataOpts) error); ok { + r1 = rf(ctx, opts) } else { r1 = ret.Error(1) } @@ -390,17 +388,17 @@ func (_m *Handler) SubmitBlindedProposal(ctx context.Context, opts *api.SubmitBl return r0 } -// SubmitPayloadAttestationMessages provides a mock function with given fields: ctx, messages -func (_m *Handler) SubmitPayloadAttestationMessages(ctx context.Context, messages []*gloas.PayloadAttestationMessage) error { - ret := _m.Called(ctx, messages) +// SubmitPayloadAttestationMessages provides a mock function with given fields: ctx, opts +func (_m *Handler) SubmitPayloadAttestationMessages(ctx context.Context, opts *api.SubmitPayloadAttestationMessagesOpts) error { + ret := _m.Called(ctx, opts) if len(ret) == 0 { panic("no return value specified for SubmitPayloadAttestationMessages") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []*gloas.PayloadAttestationMessage) error); ok { - r0 = rf(ctx, messages) + if rf, ok := ret.Get(0).(func(context.Context, *api.SubmitPayloadAttestationMessagesOpts) error); ok { + r0 = rf(ctx, opts) } else { r0 = ret.Error(0) } diff --git a/core/validatorapi/router.go b/core/validatorapi/router.go index 8f4cf2f37..26a498831 100644 --- a/core/validatorapi/router.go +++ b/core/validatorapi/router.go @@ -78,6 +78,8 @@ type Handler interface { eth2client.BeaconCommitteeSelectionsProvider eth2client.BlindedProposalSubmitter eth2client.NodeVersionProvider + eth2client.PayloadAttestationDataProvider + eth2client.PayloadAttestationMessagesSubmitter eth2client.ProposerDutiesProvider eth2client.SyncCommitteeContributionProvider eth2client.SyncCommitteeContributionsSubmitter @@ -89,15 +91,6 @@ type Handler interface { eth2client.VoluntaryExitSubmitter // Above sorted alphabetically. - // TODO(gloas): replace with eth2client.PayloadAttestationDataProvider and - // eth2client.PayloadAttestationMessagesSubmitter once go-eth2-client exposes them - // (attestantio/go-eth2-client#311). - - // PayloadAttestationData returns the payload attestation data for the provided slot. - PayloadAttestationData(ctx context.Context, slot uint64) (*gloas.PayloadAttestationData, error) - // SubmitPayloadAttestationMessages receives partially signed payload attestation messages. - SubmitPayloadAttestationMessages(ctx context.Context, messages []*gloas.PayloadAttestationMessage) error - // Address returns the address of the beacon node. Address() string // Headers returns custom headers to include in requests to the beacon node. @@ -1606,32 +1599,41 @@ func submitAggregateAttestations(s eth2client.AggregateAttestationsSubmitter) ha } // payloadAttestationData returns a handler function for the payload attestation data endpoint. -func payloadAttestationData(h Handler) handlerFunc { +func payloadAttestationData(p eth2client.PayloadAttestationDataProvider) handlerFunc { return func(ctx context.Context, _ map[string]string, _ http.Header, query url.Values, _ contentType, _ []byte) (any, http.Header, error) { slot, err := uintQuery(query, "slot") if err != nil { return nil, nil, err } - data, err := h.PayloadAttestationData(ctx, slot) + opts := ð2api.PayloadAttestationDataOpts{ + Slot: eth2p0.Slot(slot), + } + + eth2Resp, err := p.PayloadAttestationData(ctx, opts) if err != nil { return nil, nil, err } - version := eth2spec.DataVersionGloas.String() + data := eth2Resp.Data + if data.Version != eth2spec.DataVersionGloas || data.Gloas == nil { + return nil, nil, errors.New("unsupported payload attestation data version") + } + + version := data.Version.String() return struct { Version string `json:"version"` Data *gloas.PayloadAttestationData `json:"data"` }{ Version: version, - Data: data, + Data: data.Gloas, }, http.Header{versionHeader: []string{version}}, nil } } // submitPayloadAttestationMessages returns a handler function for the payload attestation pool submission endpoint. -func submitPayloadAttestationMessages(h Handler) handlerFunc { +func submitPayloadAttestationMessages(s eth2client.PayloadAttestationMessagesSubmitter) handlerFunc { return func(ctx context.Context, _ map[string]string, header http.Header, _ url.Values, typ contentType, body []byte) (any, http.Header, error) { var version eth2spec.DataVersion @@ -1658,7 +1660,15 @@ func submitPayloadAttestationMessages(h Handler) handlerFunc { return nil, nil, errors.Wrap(err, "unmarshal payload attestation messages") } - err = h.SubmitPayloadAttestationMessages(ctx, msgs) + versioned := make([]*eth2spec.VersionedPayloadAttestationMessage, 0, len(msgs)) + for _, msg := range msgs { + versioned = append(versioned, ð2spec.VersionedPayloadAttestationMessage{ + Version: version, + Gloas: msg, + }) + } + + err = s.SubmitPayloadAttestationMessages(ctx, ð2api.SubmitPayloadAttestationMessagesOpts{Messages: versioned}) if err != nil { return nil, nil, err } diff --git a/core/validatorapi/router_internal_test.go b/core/validatorapi/router_internal_test.go index 629de8102..4c88edc51 100644 --- a/core/validatorapi/router_internal_test.go +++ b/core/validatorapi/router_internal_test.go @@ -2288,8 +2288,8 @@ type testHandler struct { SubmitSyncCommitteeMessagesFunc func(ctx context.Context, messages []*altair.SyncCommitteeMessage) error SyncCommitteeDutiesFunc func(ctx context.Context, opts *eth2api.SyncCommitteeDutiesOpts) (*eth2api.Response[[]*eth2v1.SyncCommitteeDuty], error) SyncCommitteeContributionFunc func(ctx context.Context, opts *eth2api.SyncCommitteeContributionOpts) (*eth2api.Response[*altair.SyncCommitteeContribution], error) - PayloadAttestationDataFunc func(ctx context.Context, slot uint64) (*gloas.PayloadAttestationData, error) - SubmitPayloadAttMsgsFunc func(ctx context.Context, messages []*gloas.PayloadAttestationMessage) error + PayloadAttestationDataFunc func(ctx context.Context, opts *eth2api.PayloadAttestationDataOpts) (*eth2api.Response[*eth2spec.VersionedPayloadAttestationData], error) + SubmitPayloadAttMsgsFunc func(ctx context.Context, opts *eth2api.SubmitPayloadAttestationMessagesOpts) error ProxyFunc func(ctx context.Context, req *http.Request) (*http.Response, error) AddressFunc func() string HeadersFunc func() map[string]string @@ -2299,12 +2299,12 @@ func (h testHandler) AttestationData(ctx context.Context, opts *eth2api.Attestat return h.AttestationDataFunc(ctx, opts) } -func (h testHandler) PayloadAttestationData(ctx context.Context, slot uint64) (*gloas.PayloadAttestationData, error) { - return h.PayloadAttestationDataFunc(ctx, slot) +func (h testHandler) PayloadAttestationData(ctx context.Context, opts *eth2api.PayloadAttestationDataOpts) (*eth2api.Response[*eth2spec.VersionedPayloadAttestationData], error) { + return h.PayloadAttestationDataFunc(ctx, opts) } -func (h testHandler) SubmitPayloadAttestationMessages(ctx context.Context, messages []*gloas.PayloadAttestationMessage) error { - return h.SubmitPayloadAttMsgsFunc(ctx, messages) +func (h testHandler) SubmitPayloadAttestationMessages(ctx context.Context, opts *eth2api.SubmitPayloadAttestationMessagesOpts) error { + return h.SubmitPayloadAttMsgsFunc(ctx, opts) } func (h testHandler) AttesterDuties(ctx context.Context, opts *eth2api.AttesterDutiesOpts) (*eth2api.Response[[]*eth2v1.AttesterDuty], error) { @@ -2572,10 +2572,13 @@ func TestPayloadAttestationRoutes(t *testing.T) { expected.Slot = 42 handler := testHandler{ - PayloadAttestationDataFunc: func(_ context.Context, slot uint64) (*gloas.PayloadAttestationData, error) { - require.Equal(t, uint64(42), slot) + PayloadAttestationDataFunc: func(_ context.Context, opts *eth2api.PayloadAttestationDataOpts) (*eth2api.Response[*eth2spec.VersionedPayloadAttestationData], error) { + require.Equal(t, eth2p0.Slot(42), opts.Slot) - return expected, nil + return wrapResponse(ð2spec.VersionedPayloadAttestationData{ + Version: eth2spec.DataVersionGloas, + Gloas: expected, + }), nil }, } @@ -2606,11 +2609,11 @@ func TestPayloadAttestationRoutes(t *testing.T) { t.Run("submit_payload_attestations", func(t *testing.T) { msg := testutil.RandomPayloadAttestationMessage() - var received []*gloas.PayloadAttestationMessage + var received []*eth2spec.VersionedPayloadAttestationMessage handler := testHandler{ - SubmitPayloadAttMsgsFunc: func(_ context.Context, messages []*gloas.PayloadAttestationMessage) error { - received = messages + SubmitPayloadAttMsgsFunc: func(_ context.Context, opts *eth2api.SubmitPayloadAttestationMessagesOpts) error { + received = opts.Messages return nil }, @@ -2632,7 +2635,8 @@ func TestPayloadAttestationRoutes(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) require.Len(t, received, 1) - require.Equal(t, msg, received[0]) + require.Equal(t, eth2spec.DataVersionGloas, received[0].Version) + require.Equal(t, msg, received[0].Gloas) } testRawRouter(t, handler, callback) @@ -2640,7 +2644,7 @@ func TestPayloadAttestationRoutes(t *testing.T) { t.Run("submit_payload_attestations_bad_version_header", func(t *testing.T) { handler := testHandler{ - SubmitPayloadAttMsgsFunc: func(_ context.Context, _ []*gloas.PayloadAttestationMessage) error { + SubmitPayloadAttMsgsFunc: func(_ context.Context, _ *eth2api.SubmitPayloadAttestationMessagesOpts) error { require.Fail(t, "handler must not be called") return nil diff --git a/core/validatorapi/validatorapi.go b/core/validatorapi/validatorapi.go index 4c54d2d7a..e7a666f92 100644 --- a/core/validatorapi/validatorapi.go +++ b/core/validatorapi/validatorapi.go @@ -977,22 +977,30 @@ func (c Component) SubmitSyncCommitteeMessages(ctx context.Context, messages []* return nil } -// PayloadAttestationData returns the payload attestation data for the provided slot. -func (c Component) PayloadAttestationData(ctx context.Context, slot uint64) (*gloas.PayloadAttestationData, error) { +// PayloadAttestationData implements the eth2client.PayloadAttestationDataProvider for the router. +func (c Component) PayloadAttestationData(ctx context.Context, opts *eth2api.PayloadAttestationDataOpts) (*eth2api.Response[*eth2spec.VersionedPayloadAttestationData], error) { var span trace.Span - duty := core.NewPayloadAttestationDuty(slot) + duty := core.NewPayloadAttestationDuty(uint64(opts.Slot)) ctx, span = core.StartDutyTrace(ctx, duty, "core/validatorapi.PayloadAttestationData") defer span.End() - return c.awaitPayloadAttDataFunc(ctx, slot) + data, err := c.awaitPayloadAttDataFunc(ctx, uint64(opts.Slot)) + if err != nil { + return nil, err + } + + return wrapResponse(ð2spec.VersionedPayloadAttestationData{ + Version: eth2spec.DataVersionGloas, + Gloas: data, + }), nil } -// SubmitPayloadAttestationMessages receives partially signed gloas.PayloadAttestationMessage. +// SubmitPayloadAttestationMessages implements the eth2client.PayloadAttestationMessagesSubmitter for the router. // - It verifies the partial signature on each message. // - It then calls all the subscribers for further steps on the partially signed messages. -func (c Component) SubmitPayloadAttestationMessages(ctx context.Context, messages []*gloas.PayloadAttestationMessage) error { +func (c Component) SubmitPayloadAttestationMessages(ctx context.Context, opts *eth2api.SubmitPayloadAttestationMessagesOpts) error { vals, err := c.eth2Cl.ActiveValidators(ctx) if err != nil { return err @@ -1000,7 +1008,12 @@ func (c Component) SubmitPayloadAttestationMessages(ctx context.Context, message psigsBySlot := make(map[eth2p0.Slot]core.ParSignedDataSet) - for _, msg := range messages { + for _, versioned := range opts.Messages { + if versioned.Version != eth2spec.DataVersionGloas || versioned.Gloas == nil { + return errors.New("unsupported payload attestation message version") + } + + msg := versioned.Gloas if msg.Data == nil { return errors.New("no payload attestation data") } diff --git a/core/validatorapi/validatorapi_test.go b/core/validatorapi/validatorapi_test.go index 0dbcde32f..d7e078a73 100644 --- a/core/validatorapi/validatorapi_test.go +++ b/core/validatorapi/validatorapi_test.go @@ -2762,9 +2762,10 @@ func TestComponent_PayloadAttestationData(t *testing.T) { return expected, nil }) - data, err := vapi.PayloadAttestationData(ctx, 42) + resp, err := vapi.PayloadAttestationData(ctx, ð2api.PayloadAttestationDataOpts{Slot: 42}) require.NoError(t, err) - require.Equal(t, expected, data) + require.Equal(t, eth2spec.DataVersionGloas, resp.Data.Version) + require.Equal(t, expected, resp.Data.Gloas) } func TestComponent_SubmitPayloadAttestationMessages(t *testing.T) { @@ -2800,6 +2801,8 @@ func TestComponent_SubmitPayloadAttestationMessages(t *testing.T) { return nil }) - require.NoError(t, vapi.SubmitPayloadAttestationMessages(ctx, []*gloas.PayloadAttestationMessage{msg})) + require.NoError(t, vapi.SubmitPayloadAttestationMessages(ctx, ð2api.SubmitPayloadAttestationMessagesOpts{ + Messages: []*eth2spec.VersionedPayloadAttestationMessage{{Version: eth2spec.DataVersionGloas, Gloas: msg}}, + })) require.Equal(t, 1, count) } diff --git a/go.mod b/go.mod index 9d5e21798..2bd02d745 100644 --- a/go.mod +++ b/go.mod @@ -289,7 +289,7 @@ require ( replace github.com/coinbase/kryptology => github.com/ObolNetwork/kryptology v0.1.0 // We're replacing go-eth2-client with a branch off our fork. The branch is kept up to date with the latest attestantio versions. -replace github.com/attestantio/go-eth2-client => github.com/ObolNetwork/go-eth2-client v0.29.0-obol.1-gloas //nolint +replace github.com/attestantio/go-eth2-client => github.com/ObolNetwork/go-eth2-client v0.29.0-obol.2-gloas //nolint tool ( github.com/bufbuild/buf/cmd/buf diff --git a/go.sum b/go.sum index 546fb9bbf..520218fe6 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ObolNetwork/go-eth2-client v0.29.0-obol.1-gloas h1:P33jtt5RE6+uROys1WOSOiwhJZ1lAQu2tUzS6v5XdKE= -github.com/ObolNetwork/go-eth2-client v0.29.0-obol.1-gloas/go.mod h1:yhVnKAzIsFhtawbq6k/rA/Dy4vsPpu2Z2cGdQVrIjd0= +github.com/ObolNetwork/go-eth2-client v0.29.0-obol.2-gloas h1:zWRrvhl/GHiTXTgeVk7ikTO4VMHEQy8kgHha8yr8R74= +github.com/ObolNetwork/go-eth2-client v0.29.0-obol.2-gloas/go.mod h1:yhVnKAzIsFhtawbq6k/rA/Dy4vsPpu2Z2cGdQVrIjd0= github.com/ObolNetwork/kryptology v0.1.0 h1:AhoG4My70+xMhEJSpVaJay/t+T/vIUNHQYLjsDJHulI= github.com/ObolNetwork/kryptology v0.1.0/go.mod h1:/Wl7Js2f676GyXZDTaojf/O+l0fxFPWudbyjdFhkpSA= github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 h1:d/SJkN8/9Ca+1YmuDiUJxAiV4w/a9S8NcsG7GMQSrVI=