From 7aa5d156a1191ff9a9e2ca4cc0921e1077296939 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 25 Sep 2024 16:01:58 +0200 Subject: [PATCH 01/12] add endpoint --- api/server/structs/endpoints_beacon.go | 5 ++++ beacon-chain/rpc/endpoints.go | 9 +++++++ beacon-chain/rpc/endpoints_test.go | 1 + beacon-chain/rpc/eth/beacon/handlers_pool.go | 26 ++++++++++++++++--- .../rpc/eth/beacon/handlers_pool_test.go | 15 ++++++++++- 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 3fa8a4bc8f47..87947c1e661f 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -172,6 +172,11 @@ type GetAttesterSlashingsResponse struct { Data []*AttesterSlashing `json:"data"` } +type GetAttesterSlashingsV2Response struct { + Version string `json:"version"` + Data []*AttesterSlashing `json:"data"` +} + type GetProposerSlashingsResponse struct { Data []*ProposerSlashing `json:"data"` } diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 3d99b2d291ee..e6b449e7e342 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -679,6 +679,15 @@ func (s *Service) beaconEndpoints( handler: server.GetAttesterSlashings, methods: []string{http.MethodGet}, }, + { + template: "/eth/v2/beacon/pool/attester_slashings", + name: namespace + ".GetAttesterSlashings", + middleware: []middleware.Middleware{ + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.GetAttesterSlashingsV2, + methods: []string{http.MethodGet}, + }, { template: "/eth/v1/beacon/pool/attester_slashings", name: namespace + ".SubmitAttesterSlashing", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index 6b7799303f31..1e5454060a14 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -40,6 +40,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet}, "/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost}, + "/eth/v2/beacon/pool/attester_slashings": {http.MethodGet}, "/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost}, "/eth/v1/beacon/pool/sync_committees": {http.MethodPost}, "/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost}, diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 9af9a0320f85..2abc295eec05 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -461,11 +461,30 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashings") defer span.End() + _, slashings := s.attesterSlashings(ctx, w) + httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) +} + +// GetAttesterSlashingsV2 retrieves attester slashings known by the node but +// not necessarily incorporated into any block. +func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashingsV2") + defer span.End() + + v, slashings := s.attesterSlashings(ctx, w) + httputil.WriteJson(w, &structs.GetAttesterSlashingsV2Response{ + Version: v, + Data: slashings, + }) +} + +func (s *Server) attesterSlashings(ctx context.Context, w http.ResponseWriter) (string, []*structs.AttesterSlashing) { headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx) if err != nil { httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) - return + return "", nil } + v := version.String(headState.Version()) sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) ss := make([]*eth.AttesterSlashing, 0, len(sourceSlashings)) for _, slashing := range sourceSlashings { @@ -474,12 +493,11 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { ss = append(ss, s) } else { httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) - return + return "", nil } } slashings := structs.AttesterSlashingsFromConsensus(ss) - - httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) + return v, slashings } // SubmitAttesterSlashing submits an attester slashing object to node's pool and diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index d2ccfbfde25c..511b27a166f0 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1063,7 +1063,7 @@ func TestGetAttesterSlashings(t *testing.T) { SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, } - request := httptest.NewRequest(http.MethodGet, "http://example.com/beacon/pool/attester_slashings", nil) + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} @@ -1074,6 +1074,19 @@ func TestGetAttesterSlashings(t *testing.T) { require.NotNil(t, resp) require.NotNil(t, resp.Data) assert.Equal(t, 2, len(resp.Data)) + + request = httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer = httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp2 := &structs.GetAttesterSlashingsV2Response{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp2)) + require.NotNil(t, resp2) + require.NotNil(t, resp2.Data) + assert.Equal(t, 2, len(resp2.Data)) + assert.Equal(t, "phase0", resp2.Version) } func TestGetProposerSlashings(t *testing.T) { From b7b1e538525ca6e947fe12e705b2da6e8b6c99a2 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 25 Sep 2024 16:58:46 +0200 Subject: [PATCH 02/12] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3fe9a5828d..0531aa62d974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Light client support: Implement capella and deneb changes. - Light client support: Implement `BlockToLightClientHeaderXXX` functions upto Deneb - GetBeaconStateV2: add Electra case. +- Added GetPoolAttesterSlashingsV2 endpoint. ### Changed From 8ed8796339835a7a0ca0c529aae358466e15bfcb Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 26 Sep 2024 15:27:40 +0200 Subject: [PATCH 03/12] correct resp with both attestationSlashings types --- api/server/structs/endpoints_beacon.go | 4 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 60 +++++++++++++++---- .../rpc/eth/beacon/handlers_pool_test.go | 4 +- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 87947c1e661f..54b6e722e97a 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -173,8 +173,8 @@ type GetAttesterSlashingsResponse struct { } type GetAttesterSlashingsV2Response struct { - Version string `json:"version"` - Data []*AttesterSlashing `json:"data"` + Version string `json:"version"` + Data interface{} `json:"data"` } type GetProposerSlashingsResponse struct { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 2abc295eec05..f3204d202a8b 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -465,19 +465,6 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) } -// GetAttesterSlashingsV2 retrieves attester slashings known by the node but -// not necessarily incorporated into any block. -func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { - ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashingsV2") - defer span.End() - - v, slashings := s.attesterSlashings(ctx, w) - httputil.WriteJson(w, &structs.GetAttesterSlashingsV2Response{ - Version: v, - Data: slashings, - }) -} - func (s *Server) attesterSlashings(ctx context.Context, w http.ResponseWriter) (string, []*structs.AttesterSlashing) { headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx) if err != nil { @@ -500,6 +487,53 @@ func (s *Server) attesterSlashings(ctx context.Context, w http.ResponseWriter) ( return v, slashings } +// GetAttesterSlashingsV2 retrieves attester slashings known by the node but +// not necessarily incorporated into any block, supporting both AttesterSlashing and AttesterSlashingElectra. +func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashingsV2") + defer span.End() + + headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx) + if err != nil { + httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) + return + } + v := version.String(headState.Version()) + resp := &structs.GetAttesterSlashingsV2Response{Version: v} + + // Retrieve the slashings based on the version + sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) + if v == version.String(version.Electra) { + // Handle Electra version + ss := make([]*eth.AttesterSlashingElectra, 0, len(sourceSlashings)) + for _, slashing := range sourceSlashings { + s, ok := slashing.(*eth.AttesterSlashingElectra) + if ok { + ss = append(ss, s) + } else { + httputil.HandleError(w, fmt.Sprintf("unable to convert electra slashing of type %T", slashing), http.StatusInternalServerError) + return + } + } + // Convert Electra slashings to the appropriate struct and store in interface slice + resp.Data = structs.AttesterSlashingsElectraFromConsensus(ss) + } else { + // Handle regular version + ss := make([]*eth.AttesterSlashing, 0, len(sourceSlashings)) + for _, slashing := range sourceSlashings { + s, ok := slashing.(*eth.AttesterSlashing) + if ok { + ss = append(ss, s) + } else { + httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) + return + } + } + resp.Data = structs.AttesterSlashingsFromConsensus(ss) + } + httputil.WriteJson(w, resp) +} + // SubmitAttesterSlashing submits an attester slashing object to node's pool and // if passes validation node MUST broadcast it to network. func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 511b27a166f0..0c67291c5f0c 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1085,7 +1085,9 @@ func TestGetAttesterSlashings(t *testing.T) { require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp2)) require.NotNil(t, resp2) require.NotNil(t, resp2.Data) - assert.Equal(t, 2, len(resp2.Data)) + dataSlice, ok := resp2.Data.([]interface{}) + assert.Equal(t, true, ok) + assert.Equal(t, 2, len(dataSlice)) assert.Equal(t, "phase0", resp2.Version) } From 0f45617ab5d373bc1ae100e25c8289660f624399 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 30 Sep 2024 14:35:48 +0200 Subject: [PATCH 04/12] fix and comment --- api/server/structs/endpoints_beacon.go | 2 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 54b6e722e97a..9a3b363a281d 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -174,7 +174,7 @@ type GetAttesterSlashingsResponse struct { type GetAttesterSlashingsV2Response struct { Version string `json:"version"` - Data interface{} `json:"data"` + Data interface{} `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } type GetProposerSlashingsResponse struct { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index f3204d202a8b..578e77d2eae5 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -503,7 +503,7 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) // Retrieve the slashings based on the version sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) - if v == version.String(version.Electra) { + if v >= version.String(version.Electra) { // Handle Electra version ss := make([]*eth.AttesterSlashingElectra, 0, len(sourceSlashings)) for _, slashing := range sourceSlashings { From ef65f6d44e41eeea8fd0290218735f22bdec89be Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 30 Sep 2024 15:27:23 +0200 Subject: [PATCH 05/12] fix test --- .../rpc/eth/beacon/handlers_pool_test.go | 98 +++++++++++++++++-- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 0c67291c5f0c..6dbc400bbb32 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1074,21 +1074,103 @@ func TestGetAttesterSlashings(t *testing.T) { require.NotNil(t, resp) require.NotNil(t, resp.Data) assert.Equal(t, 2, len(resp.Data)) +} + +func TestGetAttesterSlashingsV2(t *testing.T) { + bs, err := util.NewBeaconStateElectra() + require.NoError(t, err) + slashing1 := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), + }, + } + slashing2 := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature4"), 96), + }, + } - request = httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) - writer = httptest.NewRecorder() + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} s.GetAttesterSlashingsV2(writer, request) require.Equal(t, http.StatusOK, writer.Code) - resp2 := &structs.GetAttesterSlashingsV2Response{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp2)) - require.NotNil(t, resp2) - require.NotNil(t, resp2.Data) - dataSlice, ok := resp2.Data.([]interface{}) + resp := &structs.GetAttesterSlashingsV2Response{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + dataSlice, ok := resp.Data.([]interface{}) assert.Equal(t, true, ok) assert.Equal(t, 2, len(dataSlice)) - assert.Equal(t, "phase0", resp2.Version) + assert.Equal(t, "electra", resp.Version) } func TestGetProposerSlashings(t *testing.T) { From 2b268deb878de205ddf6d5f3a48ca6c28552ade0 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 30 Sep 2024 18:09:49 +0200 Subject: [PATCH 06/12] fix version check --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 578e77d2eae5..64cd4d00a9e5 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -498,12 +498,12 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) return } - v := version.String(headState.Version()) - resp := &structs.GetAttesterSlashingsV2Response{Version: v} + v := headState.Version() + resp := &structs.GetAttesterSlashingsV2Response{Version: version.String(v)} // Retrieve the slashings based on the version sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) - if v >= version.String(version.Electra) { + if v >= version.Electra { // Handle Electra version ss := make([]*eth.AttesterSlashingElectra, 0, len(sourceSlashings)) for _, slashing := range sourceSlashings { From 387b79bda8399c6d2b32b75f79b4c81214e2154e Mon Sep 17 00:00:00 2001 From: Saolyn Date: Tue, 8 Oct 2024 17:06:03 +0200 Subject: [PATCH 07/12] review + fixes --- api/server/structs/endpoints_beacon.go | 4 +- beacon-chain/rpc/endpoints.go | 4 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 41 ++++++++++++------- .../rpc/eth/beacon/handlers_pool_test.go | 11 +++-- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 9a3b363a281d..f60f36c942ee 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -173,8 +173,8 @@ type GetAttesterSlashingsResponse struct { } type GetAttesterSlashingsV2Response struct { - Version string `json:"version"` - Data interface{} `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types + Version string `json:"version"` + Data []json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } type GetProposerSlashingsResponse struct { diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index e6b449e7e342..ee98543eef5a 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -681,7 +681,7 @@ func (s *Service) beaconEndpoints( }, { template: "/eth/v2/beacon/pool/attester_slashings", - name: namespace + ".GetAttesterSlashings", + name: namespace + ".GetAttesterSlashingsV2", middleware: []middleware.Middleware{ middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, @@ -695,7 +695,7 @@ func (s *Service) beaconEndpoints( middleware.ContentTypeHandler([]string{api.JsonMediaType}), middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, - handler: server.SubmitAttesterSlashing, + handler: server.SubmitAttesterSlashings, methods: []string{http.MethodPost}, }, { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 64cd4d00a9e5..50b3bc2af6c8 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -501,35 +501,48 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) v := headState.Version() resp := &structs.GetAttesterSlashingsV2Response{Version: version.String(v)} - // Retrieve the slashings based on the version sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) + if v >= version.Electra { - // Handle Electra version - ss := make([]*eth.AttesterSlashingElectra, 0, len(sourceSlashings)) - for _, slashing := range sourceSlashings { - s, ok := slashing.(*eth.AttesterSlashingElectra) + // Initialize the slice with length equal to len(sourceSlashings) so that we can index into it + ss := make([]*eth.AttesterSlashingElectra, len(sourceSlashings)) + for i, slashing := range sourceSlashings { + a, ok := slashing.(*eth.AttesterSlashingElectra) if ok { - ss = append(ss, s) + ss[i] = a // Safe to index here because the slice has the correct length } else { httputil.HandleError(w, fmt.Sprintf("unable to convert electra slashing of type %T", slashing), http.StatusInternalServerError) return } } - // Convert Electra slashings to the appropriate struct and store in interface slice - resp.Data = structs.AttesterSlashingsElectraFromConsensus(ss) + for _, slashing := range ss { + slashingBytes, err := json.Marshal(slashing) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("failed to marshal electra slashing: %v", err), http.StatusInternalServerError) + return + } + resp.Data = append(resp.Data, slashingBytes) + } } else { - // Handle regular version - ss := make([]*eth.AttesterSlashing, 0, len(sourceSlashings)) - for _, slashing := range sourceSlashings { - s, ok := slashing.(*eth.AttesterSlashing) + // Initialize the slice with length equal to len(sourceSlashings) so that we can index into it + ss := make([]*eth.AttesterSlashing, len(sourceSlashings)) + for i, slashing := range sourceSlashings { + a, ok := slashing.(*eth.AttesterSlashing) if ok { - ss = append(ss, s) + ss[i] = a // Safe to index here because the slice has the correct length } else { httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) return } } - resp.Data = structs.AttesterSlashingsFromConsensus(ss) + for _, slashing := range ss { + slashingBytes, err := json.Marshal(slashing) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) + return + } + resp.Data = append(resp.Data, slashingBytes) + } } httputil.WriteJson(w, resp) } diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 6dbc400bbb32..f078f0445eda 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1079,6 +1079,7 @@ func TestGetAttesterSlashings(t *testing.T) { func TestGetAttesterSlashingsV2(t *testing.T) { bs, err := util.NewBeaconStateElectra() require.NoError(t, err) + slashing1 := ðpbv1alpha1.AttesterSlashingElectra{ Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ AttestingIndices: []uint64{1, 10}, @@ -1167,10 +1168,14 @@ func TestGetAttesterSlashingsV2(t *testing.T) { require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) require.NotNil(t, resp) require.NotNil(t, resp.Data) - dataSlice, ok := resp.Data.([]interface{}) - assert.Equal(t, true, ok) - assert.Equal(t, 2, len(dataSlice)) assert.Equal(t, "electra", resp.Version) + assert.Equal(t, 2, len(resp.Data)) + + var unmarshaledSlashing1, unmarshaledSlashing2 ethpbv1alpha1.AttesterSlashingElectra + require.NoError(t, json.Unmarshal(resp.Data[0], &unmarshaledSlashing1)) + require.NoError(t, json.Unmarshal(resp.Data[1], &unmarshaledSlashing2)) + require.DeepEqual(t, slashing1, &unmarshaledSlashing1) + require.DeepEqual(t, slashing2, &unmarshaledSlashing2) } func TestGetProposerSlashings(t *testing.T) { From 1b4c3c2ccaf905846498421abd7f92fe5fe672e6 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 9 Oct 2024 17:14:58 +0200 Subject: [PATCH 08/12] fix --- beacon-chain/rpc/endpoints.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index ee98543eef5a..11773c0265fd 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -695,7 +695,7 @@ func (s *Service) beaconEndpoints( middleware.ContentTypeHandler([]string{api.JsonMediaType}), middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, - handler: server.SubmitAttesterSlashings, + handler: server.SubmitAttesterSlashing, methods: []string{http.MethodPost}, }, { From e2be768fff7a61ac13e5ece64788785c521e0edd Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 10 Oct 2024 11:16:04 +0200 Subject: [PATCH 09/12] James' review --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 24 +++++--------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 50b3bc2af6c8..0ebe00cd2b49 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -504,19 +504,13 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) if v >= version.Electra { - // Initialize the slice with length equal to len(sourceSlashings) so that we can index into it - ss := make([]*eth.AttesterSlashingElectra, len(sourceSlashings)) - for i, slashing := range sourceSlashings { + for _, slashing := range sourceSlashings { a, ok := slashing.(*eth.AttesterSlashingElectra) - if ok { - ss[i] = a // Safe to index here because the slice has the correct length - } else { + if !ok { httputil.HandleError(w, fmt.Sprintf("unable to convert electra slashing of type %T", slashing), http.StatusInternalServerError) return } - } - for _, slashing := range ss { - slashingBytes, err := json.Marshal(slashing) + slashingBytes, err := json.Marshal(a) if err != nil { httputil.HandleError(w, fmt.Sprintf("failed to marshal electra slashing: %v", err), http.StatusInternalServerError) return @@ -524,19 +518,13 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) resp.Data = append(resp.Data, slashingBytes) } } else { - // Initialize the slice with length equal to len(sourceSlashings) so that we can index into it - ss := make([]*eth.AttesterSlashing, len(sourceSlashings)) - for i, slashing := range sourceSlashings { + for _, slashing := range sourceSlashings { a, ok := slashing.(*eth.AttesterSlashing) - if ok { - ss[i] = a // Safe to index here because the slice has the correct length - } else { + if !ok { httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) return } - } - for _, slashing := range ss { - slashingBytes, err := json.Marshal(slashing) + slashingBytes, err := json.Marshal(a) if err != nil { httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) return From aa692032e00259468ae18d0c032637e8ea2ae9ca Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 10 Oct 2024 13:10:35 +0200 Subject: [PATCH 10/12] Review items --- api/server/structs/endpoints_beacon.go | 4 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 61 ++-- .../rpc/eth/beacon/handlers_pool_test.go | 343 +++++++++--------- 3 files changed, 200 insertions(+), 208 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index f60f36c942ee..81a7c78f5e06 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -173,8 +173,8 @@ type GetAttesterSlashingsResponse struct { } type GetAttesterSlashingsV2Response struct { - Version string `json:"version"` - Data []json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types + Version string `json:"version"` + Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } type GetProposerSlashingsResponse struct { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 0ebe00cd2b49..70369c56f280 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -461,30 +461,22 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashings") defer span.End() - _, slashings := s.attesterSlashings(ctx, w) - httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) -} - -func (s *Server) attesterSlashings(ctx context.Context, w http.ResponseWriter) (string, []*structs.AttesterSlashing) { headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx) if err != nil { httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) - return "", nil + return } - v := version.String(headState.Version()) sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) - ss := make([]*eth.AttesterSlashing, 0, len(sourceSlashings)) - for _, slashing := range sourceSlashings { - s, ok := slashing.(*eth.AttesterSlashing) - if ok { - ss = append(ss, s) - } else { + slashings := make([]*structs.AttesterSlashing, len(sourceSlashings)) + for i, slashing := range sourceSlashings { + as, ok := slashing.(*eth.AttesterSlashing) + if !ok { httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) - return "", nil + return } + slashings[i] = structs.AttesterSlashingFromConsensus(as) } - slashings := structs.AttesterSlashingsFromConsensus(ss) - return v, slashings + httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) } // GetAttesterSlashingsV2 retrieves attester slashings known by the node but @@ -498,40 +490,35 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) return } - v := headState.Version() - resp := &structs.GetAttesterSlashingsV2Response{Version: version.String(v)} - + var attStructs []interface{} + resp := &structs.GetAttesterSlashingsV2Response{} sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) - - if v >= version.Electra { - for _, slashing := range sourceSlashings { + for _, slashing := range sourceSlashings { + if slashing.Version() >= version.Electra { a, ok := slashing.(*eth.AttesterSlashingElectra) if !ok { httputil.HandleError(w, fmt.Sprintf("unable to convert electra slashing of type %T", slashing), http.StatusInternalServerError) return } - slashingBytes, err := json.Marshal(a) - if err != nil { - httputil.HandleError(w, fmt.Sprintf("failed to marshal electra slashing: %v", err), http.StatusInternalServerError) - return - } - resp.Data = append(resp.Data, slashingBytes) - } - } else { - for _, slashing := range sourceSlashings { + attStruct := structs.AttesterSlashingElectraFromConsensus(a) + attStructs = append(attStructs, attStruct) + } else { a, ok := slashing.(*eth.AttesterSlashing) if !ok { httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) return } - slashingBytes, err := json.Marshal(a) - if err != nil { - httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) - return - } - resp.Data = append(resp.Data, slashingBytes) + attStruct := structs.AttesterSlashingFromConsensus(a) + attStructs = append(attStructs, attStruct) } + resp.Version = version.String(slashing.Version()) + } + attBytes, err := json.Marshal(attStructs) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) + return } + resp.Data = attBytes httputil.WriteJson(w, resp) } diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index f078f0445eda..e750155c9188 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -983,199 +983,204 @@ func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) { } func TestGetAttesterSlashings(t *testing.T) { - bs, err := util.NewBeaconState() - require.NoError(t, err) - slashing1 := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{1, 10}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), + t.Run("V1", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) + + slashing1 := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), }, - Signature: bytesutil.PadTo([]byte("signature1"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{2, 20}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 2, - CommitteeIndex: 2, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 2, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 20, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), }, - Signature: bytesutil.PadTo([]byte("signature2"), 96), - }, - } - slashing2 := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{3, 30}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 3, - CommitteeIndex: 3, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 3, - Root: bytesutil.PadTo([]byte("sourceroot3"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 30, - Root: bytesutil.PadTo([]byte("targetroot3"), 32), + } + slashing2 := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), }, - Signature: bytesutil.PadTo([]byte("signature3"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{4, 40}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 4, - CommitteeIndex: 4, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 4, - Root: bytesutil.PadTo([]byte("sourceroot4"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 40, - Root: bytesutil.PadTo([]byte("targetroot4"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature4"), 96), }, - Signature: bytesutil.PadTo([]byte("signature4"), 96), - }, - } - - s := &Server{ - ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, - } + } - request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + } - s.GetAttesterSlashings(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - resp := &structs.GetAttesterSlashingsResponse{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) - require.NotNil(t, resp) - require.NotNil(t, resp.Data) - assert.Equal(t, 2, len(resp.Data)) -} + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} -func TestGetAttesterSlashingsV2(t *testing.T) { - bs, err := util.NewBeaconStateElectra() - require.NoError(t, err) + s.GetAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + assert.Equal(t, 2, len(resp.Data)) + }) + t.Run("V2", func(t *testing.T) { + bs, err := util.NewBeaconStateElectra() + require.NoError(t, err) - slashing1 := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{1, 10}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), + slashing1 := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), }, - Signature: bytesutil.PadTo([]byte("signature1"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{2, 20}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 2, - CommitteeIndex: 2, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 2, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 20, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), }, - Signature: bytesutil.PadTo([]byte("signature2"), 96), - }, - } - slashing2 := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{3, 30}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 3, - CommitteeIndex: 3, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 3, - Root: bytesutil.PadTo([]byte("sourceroot3"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 30, - Root: bytesutil.PadTo([]byte("targetroot3"), 32), + } + slashing2 := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), }, - Signature: bytesutil.PadTo([]byte("signature3"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{4, 40}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 4, - CommitteeIndex: 4, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 4, - Root: bytesutil.PadTo([]byte("sourceroot4"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 40, - Root: bytesutil.PadTo([]byte("targetroot4"), 32), + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, }, + Signature: bytesutil.PadTo([]byte("signature4"), 96), }, - Signature: bytesutil.PadTo([]byte("signature4"), 96), - }, - } + } - s := &Server{ - ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, - } + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + } - request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} - s.GetAttesterSlashingsV2(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - resp := &structs.GetAttesterSlashingsV2Response{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) - require.NotNil(t, resp) - require.NotNil(t, resp.Data) - assert.Equal(t, "electra", resp.Version) - assert.Equal(t, 2, len(resp.Data)) + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsV2Response{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + assert.Equal(t, "electra", resp.Version) - var unmarshaledSlashing1, unmarshaledSlashing2 ethpbv1alpha1.AttesterSlashingElectra - require.NoError(t, json.Unmarshal(resp.Data[0], &unmarshaledSlashing1)) - require.NoError(t, json.Unmarshal(resp.Data[1], &unmarshaledSlashing2)) - require.DeepEqual(t, slashing1, &unmarshaledSlashing1) - require.DeepEqual(t, slashing2, &unmarshaledSlashing2) + // Unmarshal resp.Data into a slice of slashings + var slashings []*structs.AttesterSlashingElectra + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsElectraToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1, ss[0]) + require.DeepEqual(t, slashing2, ss[1]) + }) } func TestGetProposerSlashings(t *testing.T) { From af5afc8bf33019fd9618a11b664840390dac34e2 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Tue, 15 Oct 2024 14:28:33 +0200 Subject: [PATCH 11/12] Radek' review --- api/server/structs/endpoints_beacon.go | 4 ---- beacon-chain/rpc/eth/beacon/handlers_pool.go | 9 +++++++-- beacon-chain/rpc/eth/beacon/handlers_pool_test.go | 12 ++++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index 81a7c78f5e06..e7379704bc09 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -169,10 +169,6 @@ type BLSToExecutionChangesPoolResponse struct { } type GetAttesterSlashingsResponse struct { - Data []*AttesterSlashing `json:"data"` -} - -type GetAttesterSlashingsV2Response struct { Version string `json:"version"` Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 70369c56f280..1ab1511bd4c2 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -476,7 +476,12 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { } slashings[i] = structs.AttesterSlashingFromConsensus(as) } - httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) + attBytes, err := json.Marshal(slashings) + if err != nil { + httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) + return + } + httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: attBytes}) } // GetAttesterSlashingsV2 retrieves attester slashings known by the node but @@ -491,7 +496,7 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) return } var attStructs []interface{} - resp := &structs.GetAttesterSlashingsV2Response{} + resp := &structs.GetAttesterSlashingsResponse{} sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) for _, slashing := range sourceSlashings { if slashing.Version() >= version.Electra { diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index e750155c9188..0316240caa74 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1075,7 +1075,15 @@ func TestGetAttesterSlashings(t *testing.T) { require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) require.NotNil(t, resp) require.NotNil(t, resp.Data) - assert.Equal(t, 2, len(resp.Data)) + + var slashings []*structs.AttesterSlashing + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1, ss[0]) + require.DeepEqual(t, slashing2, ss[1]) }) t.Run("V2", func(t *testing.T) { bs, err := util.NewBeaconStateElectra() @@ -1165,7 +1173,7 @@ func TestGetAttesterSlashings(t *testing.T) { s.GetAttesterSlashingsV2(writer, request) require.Equal(t, http.StatusOK, writer.Code) - resp := &structs.GetAttesterSlashingsV2Response{} + resp := &structs.GetAttesterSlashingsResponse{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) require.NotNil(t, resp) require.NotNil(t, resp.Data) From b61860aad2ddb676292392a265aca82ec817a4ea Mon Sep 17 00:00:00 2001 From: Saolyn Date: Tue, 15 Oct 2024 17:27:33 +0200 Subject: [PATCH 12/12] Radek' review --- api/server/structs/endpoints_beacon.go | 2 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 17 +- .../rpc/eth/beacon/handlers_pool_test.go | 324 ++++++++++-------- 3 files changed, 186 insertions(+), 157 deletions(-) diff --git a/api/server/structs/endpoints_beacon.go b/api/server/structs/endpoints_beacon.go index e7379704bc09..b5cb0bcd126d 100644 --- a/api/server/structs/endpoints_beacon.go +++ b/api/server/structs/endpoints_beacon.go @@ -169,7 +169,7 @@ type BLSToExecutionChangesPoolResponse struct { } type GetAttesterSlashingsResponse struct { - Version string `json:"version"` + Version string `json:"version,omitempty"` Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types } diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 1ab1511bd4c2..95199a3025e2 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -471,14 +471,14 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { for i, slashing := range sourceSlashings { as, ok := slashing.(*eth.AttesterSlashing) if !ok { - httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) + httputil.HandleError(w, fmt.Sprintf("Unable to convert slashing of type %T", slashing), http.StatusInternalServerError) return } slashings[i] = structs.AttesterSlashingFromConsensus(as) } attBytes, err := json.Marshal(slashings) if err != nil { - httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) + httputil.HandleError(w, fmt.Sprintf("Failed to marshal slashings: %v", err), http.StatusInternalServerError) return } httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: attBytes}) @@ -496,13 +496,12 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) return } var attStructs []interface{} - resp := &structs.GetAttesterSlashingsResponse{} sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */) for _, slashing := range sourceSlashings { if slashing.Version() >= version.Electra { a, ok := slashing.(*eth.AttesterSlashingElectra) if !ok { - httputil.HandleError(w, fmt.Sprintf("unable to convert electra slashing of type %T", slashing), http.StatusInternalServerError) + httputil.HandleError(w, fmt.Sprintf("Unable to convert electra slashing of type %T to an Electra slashing", slashing), http.StatusInternalServerError) return } attStruct := structs.AttesterSlashingElectraFromConsensus(a) @@ -510,20 +509,22 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) } else { a, ok := slashing.(*eth.AttesterSlashing) if !ok { - httputil.HandleError(w, fmt.Sprintf("unable to convert slashing of type %T", slashing), http.StatusInternalServerError) + httputil.HandleError(w, fmt.Sprintf("Unable to convert slashing of type %T to a Phase0 slashing", slashing), http.StatusInternalServerError) return } attStruct := structs.AttesterSlashingFromConsensus(a) attStructs = append(attStructs, attStruct) } - resp.Version = version.String(slashing.Version()) } attBytes, err := json.Marshal(attStructs) if err != nil { - httputil.HandleError(w, fmt.Sprintf("failed to marshal slashing: %v", err), http.StatusInternalServerError) + httputil.HandleError(w, fmt.Sprintf("Failed to marshal slashing: %v", err), http.StatusInternalServerError) return } - resp.Data = attBytes + resp := &structs.GetAttesterSlashingsResponse{ + Version: version.String(sourceSlashings[0].Version()), + Data: attBytes, + } httputil.WriteJson(w, resp) } diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 0316240caa74..6f53ed66cbf2 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -983,86 +983,158 @@ func TestSubmitSignedBLSToExecutionChanges_Failures(t *testing.T) { } func TestGetAttesterSlashings(t *testing.T) { - t.Run("V1", func(t *testing.T) { - bs, err := util.NewBeaconState() - require.NoError(t, err) - - slashing1 := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{1, 10}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), - }, + slashing1PreElectra := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), }, - Signature: bytesutil.PadTo([]byte("signature1"), 96), }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{2, 20}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 2, - CommitteeIndex: 2, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 2, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 20, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), }, - Signature: bytesutil.PadTo([]byte("signature2"), 96), }, - } - slashing2 := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{3, 30}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 3, - CommitteeIndex: 3, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 3, - Root: bytesutil.PadTo([]byte("sourceroot3"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 30, - Root: bytesutil.PadTo([]byte("targetroot3"), 32), - }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), + }, + } + slashing2PreElectra := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), }, - Signature: bytesutil.PadTo([]byte("signature3"), 96), }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{4, 40}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 4, - CommitteeIndex: 4, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 4, - Root: bytesutil.PadTo([]byte("sourceroot4"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 40, - Root: bytesutil.PadTo([]byte("targetroot4"), 32), - }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), }, - Signature: bytesutil.PadTo([]byte("signature4"), 96), }, - } + Signature: bytesutil.PadTo([]byte("signature4"), 96), + }, + } + slashing1PostElectra := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{1, 10}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot1"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot1"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature1"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{2, 20}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 2, + CommitteeIndex: 2, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 2, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 20, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature2"), 96), + }, + } + slashing2PostElectra := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{3, 30}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 3, + CommitteeIndex: 3, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 3, + Root: bytesutil.PadTo([]byte("sourceroot3"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 30, + Root: bytesutil.PadTo([]byte("targetroot3"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature3"), 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{4, 40}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 4, + CommitteeIndex: 4, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 4, + Root: bytesutil.PadTo([]byte("sourceroot4"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 40, + Root: bytesutil.PadTo([]byte("targetroot4"), 32), + }, + }, + Signature: bytesutil.PadTo([]byte("signature4"), 96), + }, + } + + t.Run("V1", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) s := &Server{ ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PreElectra, slashing2PreElectra}}, } request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) @@ -1082,89 +1154,16 @@ func TestGetAttesterSlashings(t *testing.T) { ss, err := structs.AttesterSlashingsToConsensus(slashings) require.NoError(t, err) - require.DeepEqual(t, slashing1, ss[0]) - require.DeepEqual(t, slashing2, ss[1]) + require.DeepEqual(t, slashing1PreElectra, ss[0]) + require.DeepEqual(t, slashing2PreElectra, ss[1]) }) - t.Run("V2", func(t *testing.T) { + t.Run("V2-post-electra", func(t *testing.T) { bs, err := util.NewBeaconStateElectra() require.NoError(t, err) - slashing1 := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{1, 10}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot1"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot1"), 32), - }, - }, - Signature: bytesutil.PadTo([]byte("signature1"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{2, 20}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 2, - CommitteeIndex: 2, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 2, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 20, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, - }, - Signature: bytesutil.PadTo([]byte("signature2"), 96), - }, - } - slashing2 := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{3, 30}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 3, - CommitteeIndex: 3, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot3"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 3, - Root: bytesutil.PadTo([]byte("sourceroot3"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 30, - Root: bytesutil.PadTo([]byte("targetroot3"), 32), - }, - }, - Signature: bytesutil.PadTo([]byte("signature3"), 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{4, 40}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 4, - CommitteeIndex: 4, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 4, - Root: bytesutil.PadTo([]byte("sourceroot4"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 40, - Root: bytesutil.PadTo([]byte("targetroot4"), 32), - }, - }, - Signature: bytesutil.PadTo([]byte("signature4"), 96), - }, - } - s := &Server{ ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1, slashing2}}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PostElectra, slashing2PostElectra}}, } request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/beacon/pool/attester_slashings", nil) @@ -1186,8 +1185,37 @@ func TestGetAttesterSlashings(t *testing.T) { ss, err := structs.AttesterSlashingsElectraToConsensus(slashings) require.NoError(t, err) - require.DeepEqual(t, slashing1, ss[0]) - require.DeepEqual(t, slashing2, ss[1]) + require.DeepEqual(t, slashing1PostElectra, ss[0]) + require.DeepEqual(t, slashing2PostElectra, ss[1]) + }) + t.Run("V2-pre-electra", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) + + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{PendingAttSlashings: []ethpbv1alpha1.AttSlashing{slashing1PreElectra, slashing2PreElectra}}, + } + + request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/attester_slashings", nil) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.GetAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + resp := &structs.GetAttesterSlashingsResponse{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp)) + require.NotNil(t, resp) + require.NotNil(t, resp.Data) + + var slashings []*structs.AttesterSlashing + require.NoError(t, json.Unmarshal(resp.Data, &slashings)) + + ss, err := structs.AttesterSlashingsToConsensus(slashings) + require.NoError(t, err) + + require.DeepEqual(t, slashing1PreElectra, ss[0]) + require.DeepEqual(t, slashing2PreElectra, ss[1]) }) }