From 2bb5edbab0038d12042b984637df5e7f79bc4f25 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 25 Sep 2024 16:13:36 +0200 Subject: [PATCH 01/13] add endpoint --- beacon-chain/rpc/endpoints.go | 10 ++++ beacon-chain/rpc/endpoints_test.go | 1 + beacon-chain/rpc/eth/beacon/handlers_pool.go | 57 ++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 3d99b2d291ee..4a31dfa578ab 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -689,6 +689,16 @@ func (s *Service) beaconEndpoints( handler: server.SubmitAttesterSlashing, methods: []string{http.MethodPost}, }, + { + template: "/eth/v2/beacon/pool/attester_slashings", + name: namespace + ".SubmitAttesterSlashing", + middleware: []middleware.Middleware{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitAttesterSlashingV2, + methods: []string{http.MethodPost}, + }, { template: "/eth/v1/beacon/pool/proposer_slashings", name: namespace + ".GetProposerSlashings", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index 6b7799303f31..0aebf5c904e1 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.MethodPost}, "/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..862bd3dfbd3b 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -539,6 +539,63 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) } } +// SubmitAttesterSlashingV2 submits an attester slashing object to node's pool and +// if passes validation node MUST broadcast it to network. +func (s *Server) SubmitAttesterSlashingV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashing") + defer span.End() + + var req structs.AttesterSlashingElectra + err := json.NewDecoder(r.Body).Decode(&req) + switch { + case errors.Is(err, io.EOF): + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + case err != nil: + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + + slashing, err := req.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + return + } + headState, err := s.ChainInfoFetcher.HeadState(ctx) + if err != nil { + httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) + return + } + headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.Attestation_1.Data.Slot) + if err != nil { + httputil.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError) + return + } + err = blocks.VerifyAttesterSlashing(ctx, headState, slashing) + if err != nil { + httputil.HandleError(w, "Invalid attester slashing: "+err.Error(), http.StatusBadRequest) + return + } + err = s.SlashingsPool.InsertAttesterSlashing(ctx, headState, slashing) + if err != nil { + httputil.HandleError(w, "Could not insert attester slashing into pool: "+err.Error(), http.StatusInternalServerError) + return + } + // notify events + s.OperationNotifier.OperationFeed().Send(&feed.Event{ + Type: operation.AttesterSlashingReceived, + Data: &operation.AttesterSlashingReceivedData{ + AttesterSlashing: slashing, + }, + }) + if !features.Get().DisableBroadcastSlashings { + if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil { + httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError) + return + } + } +} + // GetProposerSlashings retrieves proposer slashings known by the node // but not necessarily incorporated into any block. func (s *Server) GetProposerSlashings(w http.ResponseWriter, r *http.Request) { From 38b10995b1304ac6a3b6ed00791b8849c75ce2d5 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 25 Sep 2024 17:02:00 +0200 Subject: [PATCH 02/13] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3fe9a5828d..17e947094560 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 SubmitPoolAttesterSlashingV2 endpoint. ### Changed From 01f7eba50a2951429687aebaa4a8ddc0fc649eb0 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 25 Sep 2024 17:13:44 +0200 Subject: [PATCH 03/13] add required version header --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 862bd3dfbd3b..ae8a9642185a 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server" "github.com/prysmaticlabs/prysm/v5/api/server/structs" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" @@ -556,6 +557,11 @@ func (s *Server) SubmitAttesterSlashingV2(w http.ResponseWriter, r *http.Request return } + versionHeader := r.Header.Get(api.VersionHeader) + if versionHeader == "" { + httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest) + } + slashing, err := req.ToConsensus() if err != nil { httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) From 6f88104860ab09985900be7ec0005c912f0356ec Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 30 Sep 2024 17:13:36 +0200 Subject: [PATCH 04/13] fix return values --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 99 ++++++++++---------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index ae8a9642185a..1689d43beda3 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -505,39 +505,8 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) return } - headState, err := s.ChainInfoFetcher.HeadState(ctx) - if err != nil { - httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) - return - } - headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.Attestation_1.Data.Slot) - if err != nil { - httputil.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError) - return - } - err = blocks.VerifyAttesterSlashing(ctx, headState, slashing) - if err != nil { - httputil.HandleError(w, "Invalid attester slashing: "+err.Error(), http.StatusBadRequest) - return - } - err = s.SlashingsPool.InsertAttesterSlashing(ctx, headState, slashing) - if err != nil { - httputil.HandleError(w, "Could not insert attester slashing into pool: "+err.Error(), http.StatusInternalServerError) - return - } - // notify events - s.OperationNotifier.OperationFeed().Send(&feed.Event{ - Type: operation.AttesterSlashingReceived, - Data: &operation.AttesterSlashingReceivedData{ - AttesterSlashing: slashing, - }, - }) - if !features.Get().DisableBroadcastSlashings { - if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil { - httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError) - return - } - } + s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + } // SubmitAttesterSlashingV2 submits an attester slashing object to node's pool and @@ -546,33 +515,67 @@ func (s *Server) SubmitAttesterSlashingV2(w http.ResponseWriter, r *http.Request ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashing") defer span.End() - var req structs.AttesterSlashingElectra - err := json.NewDecoder(r.Body).Decode(&req) - switch { - case errors.Is(err, io.EOF): - httputil.HandleError(w, "No data submitted", http.StatusBadRequest) - return - case err != nil: - httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) - return - } - versionHeader := r.Header.Get(api.VersionHeader) if versionHeader == "" { httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest) } - - slashing, err := req.ToConsensus() + v, err := version.FromString(versionHeader) if err != nil { - httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + httputil.HandleError(w, "Invalid version: "+err.Error(), http.StatusBadRequest) return } + + if v >= version.Electra { + var req structs.AttesterSlashingElectra + err := json.NewDecoder(r.Body).Decode(&req) + switch { + case errors.Is(err, io.EOF): + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + case err != nil: + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + + slashing, err := req.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + return + } + s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + } else { + var req structs.AttesterSlashing + err := json.NewDecoder(r.Body).Decode(&req) + switch { + case errors.Is(err, io.EOF): + httputil.HandleError(w, "No data submitted", http.StatusBadRequest) + return + case err != nil: + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + + slashing, err := req.ToConsensus() + if err != nil { + httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) + return + } + s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + } +} + +func (s *Server) attesterSlashing( + w http.ResponseWriter, + ctx context.Context, + slot primitives.Slot, + slashing eth.AttSlashing, +) { headState, err := s.ChainInfoFetcher.HeadState(ctx) if err != nil { httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) return } - headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.Attestation_1.Data.Slot) + headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slot) if err != nil { httputil.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError) return From c66fd65dde9fa9722a043db27d89ae8675377d88 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 30 Sep 2024 18:59:22 +0200 Subject: [PATCH 05/13] broken test --- .../rpc/eth/beacon/handlers_pool_test.go | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index d2ccfbfde25c..8dcd5a730bcf 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server" "github.com/prysmaticlabs/prysm/v5/api/server/structs" blockchainmock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing" @@ -37,6 +38,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/ssz" "github.com/prysmaticlabs/prysm/v5/network/httputil" ethpbv1alpha1 "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" "github.com/prysmaticlabs/prysm/v5/testing/util" @@ -1234,6 +1236,99 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) { assert.Equal(t, true, ok) } +func TestSubmitAttesterSlashingV2_Ok(t *testing.T) { + ctx := context.Background() + + transition.SkipSlotCache.Disable() + defer transition.SkipSlotCache.Enable() + + _, keys, err := util.DeterministicDepositsAndKeys(1) + require.NoError(t, err) + validator := ðpbv1alpha1.Validator{ + PublicKey: keys[0].PublicKey().Marshal(), + } + bs, err := util.NewBeaconStateElectra(func(state *ethpbv1alpha1.BeaconStateElectra) error { + state.Validators = []*ethpbv1alpha1.Validator{validator} + return nil + }) + require.NoError(t, err) + + slashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + 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: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + Signature: make([]byte, 96), + }, + } + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + require.Equal(t, 1, broadcaster.NumMessages()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, ok) +} + func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) { ctx := context.Background() From b5c38655df79e91b663f1e72eb64e05c43363eae Mon Sep 17 00:00:00 2001 From: Saolyn Date: Tue, 1 Oct 2024 16:23:04 +0200 Subject: [PATCH 06/13] fix test --- .../rpc/eth/beacon/handlers_pool_test.go | 97 ++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 8dcd5a730bcf..5a14c577b25e 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1323,9 +1323,9 @@ func TestSubmitAttesterSlashingV2_Ok(t *testing.T) { pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) assert.Equal(t, true, ok) } @@ -1421,6 +1421,99 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) { assert.Equal(t, true, ok) } +func TestSubmitAttesterSlashingV2_AcrossFork(t *testing.T) { + ctx := context.Background() + + transition.SkipSlotCache.Disable() + defer transition.SkipSlotCache.Enable() + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + params.OverrideBeaconConfig(config) + + bs, keys := util.DeterministicGenesisState(t, 1) + + slashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: ðpbv1alpha1.AttestationData{ + Slot: params.BeaconConfig().SlotsPerEpoch, + 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: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: ðpbv1alpha1.AttestationData{ + Slot: params.BeaconConfig().SlotsPerEpoch, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + }, + Signature: make([]byte, 96), + }, + } + + newBs := bs.Copy() + newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) + assert.Equal(t, true, ok) +} + func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) { bs, err := util.NewBeaconState() require.NoError(t, err) From f496b12f069caa1129aa7276784cb03e7fe4cb93 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Tue, 1 Oct 2024 17:15:09 +0200 Subject: [PATCH 07/13] linter --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 1 - 1 file changed, 1 deletion(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 1689d43beda3..104cbbefbf77 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -506,7 +506,6 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) return } s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) - } // SubmitAttesterSlashingV2 submits an attester slashing object to node's pool and From 61d943699d53d9fd9490aca7014c6515f9ad98e2 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 7 Oct 2024 15:29:31 +0200 Subject: [PATCH 08/13] Fix --- beacon-chain/rpc/endpoints.go | 8 +- beacon-chain/rpc/eth/beacon/handlers_pool.go | 12 +- .../rpc/eth/beacon/handlers_pool_test.go | 354 +++++++++++++++++- proto/prysm/v1alpha1/beacon_chain.pb.go | 10 +- 4 files changed, 364 insertions(+), 20 deletions(-) diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 4a31dfa578ab..d6407b6b7fe5 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -681,22 +681,22 @@ func (s *Service) beaconEndpoints( }, { template: "/eth/v1/beacon/pool/attester_slashings", - name: namespace + ".SubmitAttesterSlashing", + name: namespace + ".SubmitAttesterSlashings", middleware: []middleware.Middleware{ middleware.ContentTypeHandler([]string{api.JsonMediaType}), middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, - handler: server.SubmitAttesterSlashing, + handler: server.SubmitAttesterSlashings, methods: []string{http.MethodPost}, }, { template: "/eth/v2/beacon/pool/attester_slashings", - name: namespace + ".SubmitAttesterSlashing", + name: namespace + ".SubmitAttesterSlashingsV2", middleware: []middleware.Middleware{ middleware.ContentTypeHandler([]string{api.JsonMediaType}), middleware.AcceptHeaderHandler([]string{api.JsonMediaType}), }, - handler: server.SubmitAttesterSlashingV2, + handler: server.SubmitAttesterSlashingsV2, 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 104cbbefbf77..7b334a049b6c 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -483,10 +483,10 @@ func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) { httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: slashings}) } -// SubmitAttesterSlashing submits an attester slashing object to node's pool and +// SubmitAttesterSlashings 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) { - ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashing") +func (s *Server) SubmitAttesterSlashings(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashings") defer span.End() var req structs.AttesterSlashing @@ -508,10 +508,10 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request) s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) } -// SubmitAttesterSlashingV2 submits an attester slashing object to node's pool and +// SubmitAttesterSlashingsV2 submits an attester slashing object to node's pool and // if passes validation node MUST broadcast it to network. -func (s *Server) SubmitAttesterSlashingV2(w http.ResponseWriter, r *http.Request) { - ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashing") +func (s *Server) SubmitAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashingsV2") defer span.End() versionHeader := r.Header.Get(api.VersionHeader) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 5a14c577b25e..bfe09ada4c3a 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1144,6 +1144,350 @@ func TestGetProposerSlashings(t *testing.T) { assert.Equal(t, 2, len(resp.Data)) } +func TestSubmitAttesterSlashings(t *testing.T) { + ctx := context.Background() + + transition.SkipSlotCache.Disable() + defer transition.SkipSlotCache.Enable() + + attestationData1 := ð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), + }, + } + attestationData2 := ðpbv1alpha1.AttestationData{ + Slot: 1, + CommitteeIndex: 1, + BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), + Source: ðpbv1alpha1.Checkpoint{ + Epoch: 1, + Root: bytesutil.PadTo([]byte("sourceroot2"), 32), + }, + Target: ðpbv1alpha1.Checkpoint{ + Epoch: 10, + Root: bytesutil.PadTo([]byte("targetroot2"), 32), + }, + } + + t.Run("V1", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + slashing := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), + }, + } + + _, keys, err := util.DeterministicDepositsAndKeys(1) + require.NoError(t, err) + validator := ðpbv1alpha1.Validator{ + PublicKey: keys[0].PublicKey().Marshal(), + } + + bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { + state.Validators = []*ethpbv1alpha1.Validator{validator} + return nil + }) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + chainmock := &blockchainmock.ChainService{State: bs} + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + require.Equal(t, 1, broadcaster.NumMessages()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, ok) + }) + t.Run("accross-fork", func(t *testing.T) { + attestationData1.Slot = params.BeaconConfig().SlotsPerEpoch + attestationData2.Slot = params.BeaconConfig().SlotsPerEpoch + slashing := ðpbv1alpha1.AttesterSlashing{ + Attestation_1: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestation{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), + }, + } + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + params.OverrideBeaconConfig(config) + + bs, keys := util.DeterministicGenesisState(t, 1) + newBs := bs.Copy() + newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + require.Equal(t, 1, broadcaster.NumMessages()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) + assert.Equal(t, true, ok) + }) + t.Run("invalid-slashing", func(t *testing.T) { + bs, err := util.NewBeaconState() + require.NoError(t, err) + + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + } + + var body bytes.Buffer + _, err = body.WriteString(invalidAttesterSlashing) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashings(writer, request) + require.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.StringContains(t, "Invalid attester slashing", e.Message) + }) + }) + t.Run("V2", func(t *testing.T) { + t.Run("ok", func(t *testing.T) { + electraSlashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), + }, + } + + _, keys, err := util.DeterministicDepositsAndKeys(1) + require.NoError(t, err) + validator := ðpbv1alpha1.Validator{ + PublicKey: keys[0].PublicKey().Marshal(), + } + + ebs, err := util.NewBeaconStateElectra(func(state *ethpbv1alpha1.BeaconStateElectra) error { + state.Validators = []*ethpbv1alpha1.Validator{validator} + return nil + }) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{electraSlashing.Attestation_1, electraSlashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(ebs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + chainmock := &blockchainmock.ChainService{State: ebs} + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{electraSlashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_electras", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, ebs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, electraSlashing, pendingSlashings[0]) + require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) + assert.Equal(t, true, ok) + }) + t.Run("accross-fork", func(t *testing.T) { + attestationData1.Slot = params.BeaconConfig().SlotsPerEpoch + attestationData2.Slot = params.BeaconConfig().SlotsPerEpoch + slashing := ðpbv1alpha1.AttesterSlashingElectra{ + Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData1, + Signature: make([]byte, 96), + }, + Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ + AttestingIndices: []uint64{0}, + Data: attestationData2, + Signature: make([]byte, 96), + }, + } + + params.SetupTestConfigCleanup(t) + config := params.BeaconConfig() + config.AltairForkEpoch = 1 + params.OverrideBeaconConfig(config) + + bs, keys := util.DeterministicGenesisState(t, 1) + newBs := bs.Copy() + newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) + require.NoError(t, err) + + for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { + sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + att.Signature = sig.Marshal() + } + + broadcaster := &p2pMock.MockBroadcaster{} + chainmock := &blockchainmock.ChainService{State: bs} + s := &Server{ + ChainInfoFetcher: chainmock, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + OperationNotifier: chainmock.OperationNotifier(), + } + + toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) + b, err := json.Marshal(toSubmit[0]) + require.NoError(t, err) + var body bytes.Buffer + _, err = body.Write(b) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusOK, writer.Code) + pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) + require.Equal(t, 1, len(pendingSlashings)) + assert.DeepEqual(t, slashing, pendingSlashings[0]) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) + require.Equal(t, 1, broadcaster.NumMessages()) + _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) + assert.Equal(t, true, ok) + }) + }) + t.Run("invalid-slashing", func(t *testing.T) { + bs, err := util.NewBeaconStateElectra() + require.NoError(t, err) + + broadcaster := &p2pMock.MockBroadcaster{} + s := &Server{ + ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, + SlashingsPool: &slashingsmock.PoolMock{}, + Broadcaster: broadcaster, + } + + var body bytes.Buffer + _, err = body.WriteString(invalidAttesterSlashing) + require.NoError(t, err) + request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) + request.Header.Set(api.VersionHeader, version.String(version.Electra)) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + + s.SubmitAttesterSlashingsV2(writer, request) + require.Equal(t, http.StatusBadRequest, writer.Code) + e := &httputil.DefaultJsonError{} + require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) + assert.Equal(t, http.StatusBadRequest, e.Code) + assert.StringContains(t, "Invalid attester slashing", e.Message) + }) +} + func TestSubmitAttesterSlashing_Ok(t *testing.T) { ctx := context.Background() @@ -1225,7 +1569,7 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.SubmitAttesterSlashing(writer, request) + s.SubmitAttesterSlashings(writer, request) require.Equal(t, http.StatusOK, writer.Code) pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) @@ -1318,7 +1662,7 @@ func TestSubmitAttesterSlashingV2_Ok(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.SubmitAttesterSlashingV2(writer, request) + s.SubmitAttesterSlashingsV2(writer, request) require.Equal(t, http.StatusOK, writer.Code) pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) @@ -1410,7 +1754,7 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.SubmitAttesterSlashing(writer, request) + s.SubmitAttesterSlashings(writer, request) require.Equal(t, http.StatusOK, writer.Code) pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) @@ -1503,7 +1847,7 @@ func TestSubmitAttesterSlashingV2_AcrossFork(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.SubmitAttesterSlashingV2(writer, request) + s.SubmitAttesterSlashingsV2(writer, request) require.Equal(t, http.StatusOK, writer.Code) pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) @@ -1532,7 +1876,7 @@ func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) { writer := httptest.NewRecorder() writer.Body = &bytes.Buffer{} - s.SubmitAttesterSlashing(writer, request) + s.SubmitAttesterSlashings(writer, request) require.Equal(t, http.StatusBadRequest, writer.Code) e := &httputil.DefaultJsonError{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) diff --git a/proto/prysm/v1alpha1/beacon_chain.pb.go b/proto/prysm/v1alpha1/beacon_chain.pb.go index 9b07cd75de0a..bc4f7b911f23 100755 --- a/proto/prysm/v1alpha1/beacon_chain.pb.go +++ b/proto/prysm/v1alpha1/beacon_chain.pb.go @@ -4497,7 +4497,7 @@ var file_proto_prysm_v1alpha1_beacon_chain_proto_depIdxs = []int32{ 22, // 42: ethereum.eth.v1alpha1.BeaconChain.ListValidatorAssignments:input_type -> ethereum.eth.v1alpha1.ListValidatorAssignmentsRequest 24, // 43: ethereum.eth.v1alpha1.BeaconChain.GetValidatorParticipation:input_type -> ethereum.eth.v1alpha1.GetValidatorParticipationRequest 57, // 44: ethereum.eth.v1alpha1.BeaconChain.GetBeaconConfig:input_type -> google.protobuf.Empty - 58, // 45: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashing:input_type -> ethereum.eth.v1alpha1.AttesterSlashing + 58, // 45: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashings:input_type -> ethereum.eth.v1alpha1.AttesterSlashing 59, // 46: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashingElectra:input_type -> ethereum.eth.v1alpha1.AttesterSlashingElectra 60, // 47: ethereum.eth.v1alpha1.BeaconChain.SubmitProposerSlashing:input_type -> ethereum.eth.v1alpha1.ProposerSlashing 31, // 48: ethereum.eth.v1alpha1.BeaconChain.GetIndividualVotes:input_type -> ethereum.eth.v1alpha1.IndividualVotesRequest @@ -4519,7 +4519,7 @@ var file_proto_prysm_v1alpha1_beacon_chain_proto_depIdxs = []int32{ 23, // 64: ethereum.eth.v1alpha1.BeaconChain.ListValidatorAssignments:output_type -> ethereum.eth.v1alpha1.ValidatorAssignments 25, // 65: ethereum.eth.v1alpha1.BeaconChain.GetValidatorParticipation:output_type -> ethereum.eth.v1alpha1.ValidatorParticipationResponse 29, // 66: ethereum.eth.v1alpha1.BeaconChain.GetBeaconConfig:output_type -> ethereum.eth.v1alpha1.BeaconConfig - 30, // 67: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashing:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse + 30, // 67: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashings:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 30, // 68: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashingElectra:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 30, // 69: ethereum.eth.v1alpha1.BeaconChain.SubmitProposerSlashing:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 32, // 70: ethereum.eth.v1alpha1.BeaconChain.GetIndividualVotes:output_type -> ethereum.eth.v1alpha1.IndividualVotesRespond @@ -5294,7 +5294,7 @@ func (c *beaconChainClient) GetBeaconConfig(ctx context.Context, in *emptypb.Emp func (c *beaconChainClient) SubmitAttesterSlashing(ctx context.Context, in *AttesterSlashing, opts ...grpc.CallOption) (*SubmitSlashingResponse, error) { out := new(SubmitSlashingResponse) - err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashing", in, out, opts...) + err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashings", in, out, opts...) if err != nil { return nil, err } @@ -5415,7 +5415,7 @@ func (*UnimplementedBeaconChainServer) GetBeaconConfig(context.Context, *emptypb return nil, status.Errorf(codes.Unimplemented, "method GetBeaconConfig not implemented") } func (*UnimplementedBeaconChainServer) SubmitAttesterSlashing(context.Context, *AttesterSlashing) (*SubmitSlashingResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashing not implemented") + return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashings not implemented") } func (*UnimplementedBeaconChainServer) SubmitAttesterSlashingElectra(context.Context, *AttesterSlashingElectra) (*SubmitSlashingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashingElectra not implemented") @@ -5765,7 +5765,7 @@ func _BeaconChain_SubmitAttesterSlashing_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashing", + FullMethod: "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashings", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BeaconChainServer).SubmitAttesterSlashing(ctx, req.(*AttesterSlashing)) From 116770fba41e865c3ec8da0ee8e6d1eb55b0473c Mon Sep 17 00:00:00 2001 From: Saolyn Date: Mon, 7 Oct 2024 15:38:13 +0200 Subject: [PATCH 09/13] Proto update --- proto/prysm/v1alpha1/beacon_chain.pb.go | 10 +++++----- testing/mock/beacon_service_mock.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/proto/prysm/v1alpha1/beacon_chain.pb.go b/proto/prysm/v1alpha1/beacon_chain.pb.go index bc4f7b911f23..9b07cd75de0a 100755 --- a/proto/prysm/v1alpha1/beacon_chain.pb.go +++ b/proto/prysm/v1alpha1/beacon_chain.pb.go @@ -4497,7 +4497,7 @@ var file_proto_prysm_v1alpha1_beacon_chain_proto_depIdxs = []int32{ 22, // 42: ethereum.eth.v1alpha1.BeaconChain.ListValidatorAssignments:input_type -> ethereum.eth.v1alpha1.ListValidatorAssignmentsRequest 24, // 43: ethereum.eth.v1alpha1.BeaconChain.GetValidatorParticipation:input_type -> ethereum.eth.v1alpha1.GetValidatorParticipationRequest 57, // 44: ethereum.eth.v1alpha1.BeaconChain.GetBeaconConfig:input_type -> google.protobuf.Empty - 58, // 45: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashings:input_type -> ethereum.eth.v1alpha1.AttesterSlashing + 58, // 45: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashing:input_type -> ethereum.eth.v1alpha1.AttesterSlashing 59, // 46: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashingElectra:input_type -> ethereum.eth.v1alpha1.AttesterSlashingElectra 60, // 47: ethereum.eth.v1alpha1.BeaconChain.SubmitProposerSlashing:input_type -> ethereum.eth.v1alpha1.ProposerSlashing 31, // 48: ethereum.eth.v1alpha1.BeaconChain.GetIndividualVotes:input_type -> ethereum.eth.v1alpha1.IndividualVotesRequest @@ -4519,7 +4519,7 @@ var file_proto_prysm_v1alpha1_beacon_chain_proto_depIdxs = []int32{ 23, // 64: ethereum.eth.v1alpha1.BeaconChain.ListValidatorAssignments:output_type -> ethereum.eth.v1alpha1.ValidatorAssignments 25, // 65: ethereum.eth.v1alpha1.BeaconChain.GetValidatorParticipation:output_type -> ethereum.eth.v1alpha1.ValidatorParticipationResponse 29, // 66: ethereum.eth.v1alpha1.BeaconChain.GetBeaconConfig:output_type -> ethereum.eth.v1alpha1.BeaconConfig - 30, // 67: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashings:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse + 30, // 67: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashing:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 30, // 68: ethereum.eth.v1alpha1.BeaconChain.SubmitAttesterSlashingElectra:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 30, // 69: ethereum.eth.v1alpha1.BeaconChain.SubmitProposerSlashing:output_type -> ethereum.eth.v1alpha1.SubmitSlashingResponse 32, // 70: ethereum.eth.v1alpha1.BeaconChain.GetIndividualVotes:output_type -> ethereum.eth.v1alpha1.IndividualVotesRespond @@ -5294,7 +5294,7 @@ func (c *beaconChainClient) GetBeaconConfig(ctx context.Context, in *emptypb.Emp func (c *beaconChainClient) SubmitAttesterSlashing(ctx context.Context, in *AttesterSlashing, opts ...grpc.CallOption) (*SubmitSlashingResponse, error) { out := new(SubmitSlashingResponse) - err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashings", in, out, opts...) + err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashing", in, out, opts...) if err != nil { return nil, err } @@ -5415,7 +5415,7 @@ func (*UnimplementedBeaconChainServer) GetBeaconConfig(context.Context, *emptypb return nil, status.Errorf(codes.Unimplemented, "method GetBeaconConfig not implemented") } func (*UnimplementedBeaconChainServer) SubmitAttesterSlashing(context.Context, *AttesterSlashing) (*SubmitSlashingResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashings not implemented") + return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashing not implemented") } func (*UnimplementedBeaconChainServer) SubmitAttesterSlashingElectra(context.Context, *AttesterSlashingElectra) (*SubmitSlashingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitAttesterSlashingElectra not implemented") @@ -5765,7 +5765,7 @@ func _BeaconChain_SubmitAttesterSlashing_Handler(srv interface{}, ctx context.Co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashings", + FullMethod: "/ethereum.eth.v1alpha1.BeaconChain/SubmitAttesterSlashing", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(BeaconChainServer).SubmitAttesterSlashing(ctx, req.(*AttesterSlashing)) diff --git a/testing/mock/beacon_service_mock.go b/testing/mock/beacon_service_mock.go index f5e506ba34d2..49b8b66d46d9 100644 --- a/testing/mock/beacon_service_mock.go +++ b/testing/mock/beacon_service_mock.go @@ -429,7 +429,7 @@ func (m *MockBeaconChainClient) SubmitAttesterSlashing(arg0 context.Context, arg for _, a := range arg2 { varargs = append(varargs, a) } - ret := m.ctrl.Call(m, "SubmitAttesterSlashing", varargs...) + ret := m.ctrl.Call(m, "SubmitAttesterSlashings", varargs...) ret0, _ := ret[0].(*eth.SubmitSlashingResponse) ret1, _ := ret[1].(error) return ret0, ret1 @@ -439,7 +439,7 @@ func (m *MockBeaconChainClient) SubmitAttesterSlashing(arg0 context.Context, arg func (mr *MockBeaconChainClientMockRecorder) SubmitAttesterSlashing(arg0, arg1 any, arg2 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttesterSlashing", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitAttesterSlashing), varargs...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttesterSlashings", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitAttesterSlashing), varargs...) } // SubmitAttesterSlashingElectra mocks base method. From 5f3486e5123a7692c354b1be1da527a8d35da0a1 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Wed, 9 Oct 2024 17:12:25 +0200 Subject: [PATCH 10/13] fix test --- beacon-chain/rpc/eth/beacon/handlers_pool_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index bfe09ada4c3a..75ddb8fd5c0f 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1236,8 +1236,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) assert.Equal(t, true, ok) }) @@ -1299,8 +1299,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) assert.Equal(t, true, ok) }) @@ -1389,8 +1389,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { require.Equal(t, http.StatusOK, writer.Code) pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, ebs, true) require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, electraSlashing, pendingSlashings[0]) require.Equal(t, 1, broadcaster.NumMessages()) + assert.DeepEqual(t, electraSlashing, pendingSlashings[0]) assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) assert.Equal(t, true, ok) @@ -1454,8 +1454,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) require.Equal(t, 1, len(pendingSlashings)) assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) require.Equal(t, 1, broadcaster.NumMessages()) + assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) assert.Equal(t, true, ok) }) From 9c4de22071f2c06d9244cd5730b8e780e5f6e256 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 10 Oct 2024 09:41:57 +0200 Subject: [PATCH 11/13] fix test --- beacon-chain/rpc/eth/beacon/handlers_pool_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index 75ddb8fd5c0f..b9bced8d7029 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1151,7 +1151,6 @@ func TestSubmitAttesterSlashings(t *testing.T) { defer transition.SkipSlotCache.Enable() attestationData1 := ðpbv1alpha1.AttestationData{ - Slot: 1, CommitteeIndex: 1, BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32), Source: ðpbv1alpha1.Checkpoint{ @@ -1164,7 +1163,6 @@ func TestSubmitAttesterSlashings(t *testing.T) { }, } attestationData2 := ðpbv1alpha1.AttestationData{ - Slot: 1, CommitteeIndex: 1, BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), Source: ðpbv1alpha1.Checkpoint{ @@ -1179,6 +1177,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { t.Run("V1", func(t *testing.T) { t.Run("ok", func(t *testing.T) { + attestationData1.Slot = 1 + attestationData2.Slot = 1 slashing := ðpbv1alpha1.AttesterSlashing{ Attestation_1: ðpbv1alpha1.IndexedAttestation{ AttestingIndices: []uint64{0}, @@ -1332,6 +1332,8 @@ func TestSubmitAttesterSlashings(t *testing.T) { }) t.Run("V2", func(t *testing.T) { t.Run("ok", func(t *testing.T) { + attestationData1.Slot = 1 + attestationData2.Slot = 1 electraSlashing := ðpbv1alpha1.AttesterSlashingElectra{ Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ AttestingIndices: []uint64{0}, From 57e4f0a2a300e54972ae484eba1612ac3594780b Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 10 Oct 2024 13:19:22 +0200 Subject: [PATCH 12/13] Radek' review --- beacon-chain/rpc/eth/beacon/handlers_pool.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 7b334a049b6c..0d92b94b0bb3 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -505,7 +505,7 @@ func (s *Server) SubmitAttesterSlashings(w http.ResponseWriter, r *http.Request) httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) return } - s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + s.submitAttesterSlashing(w, ctx, slashing) } // SubmitAttesterSlashingsV2 submits an attester slashing object to node's pool and @@ -541,7 +541,7 @@ func (s *Server) SubmitAttesterSlashingsV2(w http.ResponseWriter, r *http.Reques httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) return } - s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + s.submitAttesterSlashing(w, ctx, slashing) } else { var req structs.AttesterSlashing err := json.NewDecoder(r.Body).Decode(&req) @@ -559,14 +559,13 @@ func (s *Server) SubmitAttesterSlashingsV2(w http.ResponseWriter, r *http.Reques httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest) return } - s.attesterSlashing(w, ctx, slashing.Attestation_1.Data.Slot, slashing) + s.submitAttesterSlashing(w, ctx, slashing) } } -func (s *Server) attesterSlashing( +func (s *Server) submitAttesterSlashing( w http.ResponseWriter, ctx context.Context, - slot primitives.Slot, slashing eth.AttSlashing, ) { headState, err := s.ChainInfoFetcher.HeadState(ctx) @@ -574,11 +573,12 @@ func (s *Server) attesterSlashing( httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError) return } - headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slot) + headState, err = transition.ProcessSlotsIfPossible(ctx, headState, slashing.FirstAttestation().GetData().Slot) if err != nil { httputil.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError) return } + err = blocks.VerifyAttesterSlashing(ctx, headState, slashing) if err != nil { httputil.HandleError(w, "Invalid attester slashing: "+err.Error(), http.StatusBadRequest) From 2f842794acc12114f0afcd7cfe6b8445f842b0e9 Mon Sep 17 00:00:00 2001 From: Saolyn Date: Thu, 10 Oct 2024 13:23:33 +0200 Subject: [PATCH 13/13] remove duplicate tests --- .../rpc/eth/beacon/handlers_pool_test.go | 565 ------------------ 1 file changed, 565 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index b9bced8d7029..593eb8a3d854 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -1490,571 +1490,6 @@ func TestSubmitAttesterSlashings(t *testing.T) { }) } -func TestSubmitAttesterSlashing_Ok(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - _, keys, err := util.DeterministicDepositsAndKeys(1) - require.NoError(t, err) - validator := ðpbv1alpha1.Validator{ - PublicKey: keys[0].PublicKey().Marshal(), - } - bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { - state.Validators = []*ethpbv1alpha1.Validator{validator} - return nil - }) - require.NoError(t, err) - - slashing := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - 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: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, - }, - Signature: make([]byte, 96), - }, - } - - for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashings(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashingV2_Ok(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - _, keys, err := util.DeterministicDepositsAndKeys(1) - require.NoError(t, err) - validator := ðpbv1alpha1.Validator{ - PublicKey: keys[0].PublicKey().Marshal(), - } - bs, err := util.NewBeaconStateElectra(func(state *ethpbv1alpha1.BeaconStateElectra) error { - state.Validators = []*ethpbv1alpha1.Validator{validator} - return nil - }) - require.NoError(t, err) - - slashing := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{0}, - 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: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: 1, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, - }, - Signature: make([]byte, 96), - }, - } - - for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(bs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - request.Header.Set(api.VersionHeader, version.String(version.Electra)) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashingsV2(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - require.Equal(t, 1, broadcaster.NumMessages()) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - params.OverrideBeaconConfig(config) - - bs, keys := util.DeterministicGenesisState(t, 1) - - slashing := ðpbv1alpha1.AttesterSlashing{ - Attestation_1: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - 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: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestation{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, - }, - Signature: make([]byte, 96), - }, - } - - newBs := bs.Copy() - newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) - require.NoError(t, err) - - for _, att := range []*ethpbv1alpha1.IndexedAttestation{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashings(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashingV2_AcrossFork(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - params.OverrideBeaconConfig(config) - - bs, keys := util.DeterministicGenesisState(t, 1) - - slashing := ðpbv1alpha1.AttesterSlashingElectra{ - Attestation_1: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - 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: make([]byte, 96), - }, - Attestation_2: ðpbv1alpha1.IndexedAttestationElectra{ - AttestingIndices: []uint64{0}, - Data: ðpbv1alpha1.AttestationData{ - Slot: params.BeaconConfig().SlotsPerEpoch, - CommitteeIndex: 1, - BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32), - Source: ðpbv1alpha1.Checkpoint{ - Epoch: 1, - Root: bytesutil.PadTo([]byte("sourceroot2"), 32), - }, - Target: ðpbv1alpha1.Checkpoint{ - Epoch: 10, - Root: bytesutil.PadTo([]byte("targetroot2"), 32), - }, - }, - Signature: make([]byte, 96), - }, - } - - newBs := bs.Copy() - newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) - require.NoError(t, err) - - for _, att := range []*ethpbv1alpha1.IndexedAttestationElectra{slashing.Attestation_1, slashing.Attestation_2} { - sb, err := signing.ComputeDomainAndSign(newBs, att.Data.Target.Epoch, att.Data, params.BeaconConfig().DomainBeaconAttester, keys[0]) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - att.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.AttesterSlashingsElectraFromConsensus([]*ethpbv1alpha1.AttesterSlashingElectra{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - request.Header.Set(api.VersionHeader, version.String(version.Electra)) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashingsV2(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - require.Equal(t, 1, broadcaster.NumMessages()) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.AttesterSlashingElectra) - assert.Equal(t, true, ok) -} - -func TestSubmitAttesterSlashing_InvalidSlashing(t *testing.T) { - bs, err := util.NewBeaconState() - require.NoError(t, err) - - broadcaster := &p2pMock.MockBroadcaster{} - s := &Server{ - ChainInfoFetcher: &blockchainmock.ChainService{State: bs}, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - } - - var body bytes.Buffer - _, err = body.WriteString(invalidAttesterSlashing) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/attester_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitAttesterSlashings(writer, request) - require.Equal(t, http.StatusBadRequest, writer.Code) - e := &httputil.DefaultJsonError{} - require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) - assert.Equal(t, http.StatusBadRequest, e.Code) - assert.StringContains(t, "Invalid attester slashing", e.Message) -} - -func TestSubmitProposerSlashing_Ok(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - _, keys, err := util.DeterministicDepositsAndKeys(1) - require.NoError(t, err) - validator := ðpbv1alpha1.Validator{ - PublicKey: keys[0].PublicKey().Marshal(), - WithdrawableEpoch: primitives.Epoch(1), - } - bs, err := util.NewBeaconState(func(state *ethpbv1alpha1.BeaconState) error { - state.Validators = []*ethpbv1alpha1.Validator{validator} - return nil - }) - require.NoError(t, err) - - slashing := ðpbv1alpha1.ProposerSlashing{ - Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot1"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot1"), 32), - }, - Signature: make([]byte, 96), - }, - Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: 1, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot2"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot2"), 32), - }, - Signature: make([]byte, 96), - }, - } - - for _, h := range []*ethpbv1alpha1.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2} { - sb, err := signing.ComputeDomainAndSign( - bs, - slots.ToEpoch(h.Header.Slot), - h.Header, - params.BeaconConfig().DomainBeaconProposer, - keys[0], - ) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - h.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/proposer_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitProposerSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing) - assert.Equal(t, true, ok) -} - -func TestSubmitProposerSlashing_AcrossFork(t *testing.T) { - ctx := context.Background() - - transition.SkipSlotCache.Disable() - defer transition.SkipSlotCache.Enable() - - params.SetupTestConfigCleanup(t) - config := params.BeaconConfig() - config.AltairForkEpoch = 1 - params.OverrideBeaconConfig(config) - - bs, keys := util.DeterministicGenesisState(t, 1) - - slashing := ðpbv1alpha1.ProposerSlashing{ - Header_1: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: params.BeaconConfig().SlotsPerEpoch, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot1"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot1"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot1"), 32), - }, - Signature: make([]byte, 96), - }, - Header_2: ðpbv1alpha1.SignedBeaconBlockHeader{ - Header: ðpbv1alpha1.BeaconBlockHeader{ - Slot: params.BeaconConfig().SlotsPerEpoch, - ProposerIndex: 0, - ParentRoot: bytesutil.PadTo([]byte("parentroot2"), 32), - StateRoot: bytesutil.PadTo([]byte("stateroot2"), 32), - BodyRoot: bytesutil.PadTo([]byte("bodyroot2"), 32), - }, - Signature: make([]byte, 96), - }, - } - - newBs := bs.Copy() - newBs, err := transition.ProcessSlots(ctx, newBs, params.BeaconConfig().SlotsPerEpoch) - require.NoError(t, err) - - for _, h := range []*ethpbv1alpha1.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2} { - sb, err := signing.ComputeDomainAndSign( - newBs, - slots.ToEpoch(h.Header.Slot), - h.Header, - params.BeaconConfig().DomainBeaconProposer, - keys[0], - ) - require.NoError(t, err) - sig, err := bls.SignatureFromBytes(sb) - require.NoError(t, err) - h.Signature = sig.Marshal() - } - - broadcaster := &p2pMock.MockBroadcaster{} - chainmock := &blockchainmock.ChainService{State: bs} - s := &Server{ - ChainInfoFetcher: chainmock, - SlashingsPool: &slashingsmock.PoolMock{}, - Broadcaster: broadcaster, - OperationNotifier: chainmock.OperationNotifier(), - } - - toSubmit := structs.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing}) - b, err := json.Marshal(toSubmit[0]) - require.NoError(t, err) - var body bytes.Buffer - _, err = body.Write(b) - require.NoError(t, err) - request := httptest.NewRequest(http.MethodPost, "http://example.com/beacon/pool/proposer_slashings", &body) - writer := httptest.NewRecorder() - writer.Body = &bytes.Buffer{} - - s.SubmitProposerSlashing(writer, request) - require.Equal(t, http.StatusOK, writer.Code) - pendingSlashings := s.SlashingsPool.PendingProposerSlashings(ctx, bs, true) - require.Equal(t, 1, len(pendingSlashings)) - assert.DeepEqual(t, slashing, pendingSlashings[0]) - assert.Equal(t, true, broadcaster.BroadcastCalled.Load()) - require.Equal(t, 1, broadcaster.NumMessages()) - _, ok := broadcaster.BroadcastMessages[0].(*ethpbv1alpha1.ProposerSlashing) - assert.Equal(t, true, ok) -} - func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) { bs, err := util.NewBeaconState() require.NoError(t, err)