diff --git a/WORKSPACE b/WORKSPACE index 4bd73d4e61b..7883ad49722 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1272,7 +1272,7 @@ go_repository( go_repository( name = "com_github_prysmaticlabs_ethereumapis", - commit = "79d7a99b999d1873431b2ae56f1e6ea6e730242f", + commit = "b7452dde4ca361809def4ed5924ab3cb7ad1299a", importpath = "github.com/prysmaticlabs/ethereumapis", patch_args = ["-p1"], patches = [ diff --git a/beacon-chain/node/BUILD.bazel b/beacon-chain/node/BUILD.bazel index fbcefc74884..42665c62b33 100644 --- a/beacon-chain/node/BUILD.bazel +++ b/beacon-chain/node/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//beacon-chain/gateway:go_default_library", "//beacon-chain/interop-cold-start:go_default_library", "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/operations/voluntaryexits:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index 61441723627..34493a3be59 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -26,6 +26,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/gateway" interopcoldstart "github.com/prysmaticlabs/prysm/beacon-chain/interop-cold-start" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" @@ -62,6 +63,7 @@ type BeaconNode struct { db db.Database attestationPool attestations.Pool exitPool *voluntaryexits.Pool + slashingsPool *slashings.Pool depositCache *depositcache.DepositCache stateFeed *event.Feed blockFeed *event.Feed @@ -109,6 +111,7 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) { opFeed: new(event.Feed), attestationPool: attestations.NewPool(), exitPool: voluntaryexits.NewPool(), + slashingsPool: slashings.NewPool(), } if err := beacon.startDB(ctx); err != nil { @@ -497,6 +500,7 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { GenesisTimeFetcher: chainService, AttestationsPool: b.attestationPool, ExitPool: b.exitPool, + SlashingsPool: b.slashingsPool, POWChainService: web3Service, ChainStartFetcher: chainStartFetcher, MockEth1Votes: mockEth1DataVotes, diff --git a/beacon-chain/operations/slashings/service.go b/beacon-chain/operations/slashings/service.go index eb774859d47..b8782fdefb2 100644 --- a/beacon-chain/operations/slashings/service.go +++ b/beacon-chain/operations/slashings/service.go @@ -1,6 +1,8 @@ package slashings import ( + "errors" + "fmt" "sort" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" @@ -77,7 +79,7 @@ func (p *Pool) InsertAttesterSlashing(state *beaconstate.BeaconState, slashing * // has been recently included in the pool of slashings, do not process this new // slashing. if !ok { - return nil + return fmt.Errorf("validator at index %d cannot be slashed", val) } // Check if the validator already exists in the list of slashings. @@ -117,7 +119,7 @@ func (p *Pool) InsertProposerSlashing(state *beaconstate.BeaconState, slashing * // has been recently included in the pool of slashings, do not process this new // slashing. if !ok { - return nil + return fmt.Errorf("validator at index %d cannot be slashed", idx) } // Check if the validator already exists in the list of slashings. @@ -126,7 +128,7 @@ func (p *Pool) InsertProposerSlashing(state *beaconstate.BeaconState, slashing * return p.pendingProposerSlashing[i].ProposerIndex == slashing.ProposerIndex }) if found != len(p.pendingProposerSlashing) { - return nil + return errors.New("slashing object already exists in pending proposer slashings") } // Insert into pending list and sort again. @@ -194,11 +196,7 @@ func (p *Pool) validatorSlashingPreconditionCheck( return false, nil } // Checking if the validator has already been slashed. - slashedValidators := state.Slashings() - found := sort.Search(len(slashedValidators), func(i int) bool { - return slashedValidators[i] == valIdx - }) - if found != len(slashedValidators) { + if validator.Slashed() { return false, nil } return true, nil diff --git a/beacon-chain/operations/slashings/service_attester_test.go b/beacon-chain/operations/slashings/service_attester_test.go index 92077c1af52..0e7a567b1c1 100644 --- a/beacon-chain/operations/slashings/service_attester_test.go +++ b/beacon-chain/operations/slashings/service_attester_test.go @@ -2,6 +2,7 @@ package slashings import ( "reflect" + "strings" "testing" "github.com/gogo/protobuf/proto" @@ -52,6 +53,8 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { type fields struct { pending []*PendingAttesterSlashing included map[uint64]bool + wantErr bool + err string } type args struct { slashing *ethpb.AttesterSlashing @@ -179,7 +182,7 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { }, }, { - name: "Slashing for exited validator ", + name: "Slashing for exited validator", fields: fields{ pending: []*PendingAttesterSlashing{}, included: make(map[uint64]bool), @@ -190,7 +193,7 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { want: []*PendingAttesterSlashing{}, }, { - name: "Slashing for futuristic exited validator ", + name: "Slashing for futuristic exited validator", fields: fields{ pending: []*PendingAttesterSlashing{}, included: make(map[uint64]bool), @@ -203,10 +206,12 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { }, }, { - name: "Slashing for slashed validator ", + name: "Slashing for slashed validator", fields: fields{ pending: []*PendingAttesterSlashing{}, included: make(map[uint64]bool), + wantErr: true, + err: "cannot be slashed", }, args: args{ slashing: attesterSlashingForValIdx(5), @@ -259,6 +264,7 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { }, { // 5 - Slashed. ExitEpoch: params.BeaconConfig().FarFutureEpoch, + Slashed: true, }, } for _, tt := range tests { @@ -267,13 +273,17 @@ func TestPool_InsertAttesterSlashing(t *testing.T) { pendingAttesterSlashing: tt.fields.pending, included: tt.fields.included, } - s, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators}) + s, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{ + Slot: 16 * params.BeaconConfig().SlotsPerEpoch, + Validators: validators, + }) if err != nil { t.Fatal(err) } - s.SetSlot(16 * params.BeaconConfig().SlotsPerEpoch) - s.SetSlashings([]uint64{5}) - p.InsertAttesterSlashing(s, tt.args.slashing) + err = p.InsertAttesterSlashing(s, tt.args.slashing) + if err != nil && tt.fields.wantErr && !strings.Contains(err.Error(), tt.fields.err) { + t.Fatalf("Wanted err: %v, received %v", tt.fields.err, err) + } if len(p.pendingAttesterSlashing) != len(tt.want) { t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pendingAttesterSlashing), len(tt.want)) } diff --git a/beacon-chain/operations/slashings/service_proposer_test.go b/beacon-chain/operations/slashings/service_proposer_test.go index 9c1c3ee367f..283080bea7f 100644 --- a/beacon-chain/operations/slashings/service_proposer_test.go +++ b/beacon-chain/operations/slashings/service_proposer_test.go @@ -2,6 +2,7 @@ package slashings import ( "reflect" + "strings" "testing" "github.com/gogo/protobuf/proto" @@ -27,6 +28,8 @@ func generateNProposerSlashings(n uint64) []*ethpb.ProposerSlashing { func TestPool_InsertProposerSlashing(t *testing.T) { type fields struct { + wantErr bool + err string pending []*ethpb.ProposerSlashing included map[uint64]bool } @@ -90,6 +93,8 @@ func TestPool_InsertProposerSlashing(t *testing.T) { fields: fields{ pending: []*ethpb.ProposerSlashing{}, included: make(map[uint64]bool), + wantErr: true, + err: "cannot be slashed", }, args: args{ slashing: proposerSlashingForValIdx(5), @@ -103,6 +108,8 @@ func TestPool_InsertProposerSlashing(t *testing.T) { included: map[uint64]bool{ 1: true, }, + wantErr: true, + err: "cannot be slashed", }, args: args{ slashing: proposerSlashingForValIdx(1), @@ -146,6 +153,7 @@ func TestPool_InsertProposerSlashing(t *testing.T) { }, { // 5 - Slashed. ExitEpoch: params.BeaconConfig().FarFutureEpoch, + Slashed: true, }, } for _, tt := range tests { @@ -154,13 +162,17 @@ func TestPool_InsertProposerSlashing(t *testing.T) { pendingProposerSlashing: tt.fields.pending, included: tt.fields.included, } - beaconState, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{Validators: validators}) + beaconState, err := beaconstate.InitializeFromProtoUnsafe(&p2ppb.BeaconState{ + Slot: 16 * params.BeaconConfig().SlotsPerEpoch, + Validators: validators, + }) if err != nil { t.Fatal(err) } - beaconState.SetSlot(16 * params.BeaconConfig().SlotsPerEpoch) - beaconState.SetSlashings([]uint64{5}) - p.InsertProposerSlashing(beaconState, tt.args.slashing) + err = p.InsertProposerSlashing(beaconState, tt.args.slashing) + if err != nil && tt.fields.wantErr && !strings.Contains(err.Error(), tt.fields.err) { + t.Fatalf("Wanted err: %v, received %v", tt.fields.err, err) + } if len(p.pendingProposerSlashing) != len(tt.want) { t.Fatalf("Mismatched lengths of pending list. Got %d, wanted %d.", len(p.pendingProposerSlashing), len(tt.want)) } diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 0b9a43368dd..e0e18c4541c 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/operations/voluntaryexits:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index c640ae9a300..31ad6c337c0 100644 --- a/beacon-chain/rpc/beacon/BUILD.bazel +++ b/beacon-chain/rpc/beacon/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "committees.go", "config.go", "server.go", + "slashings.go", "validators.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/beacon", @@ -26,12 +27,14 @@ go_library( "//beacon-chain/db/filters:go_default_library", "//beacon-chain/flags:go_default_library", "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/powchain:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", "//shared/pagination:go_default_library", "//shared/params:go_default_library", + "//shared/sliceutil:go_default_library", "//shared/slotutil:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", "@com_github_pkg_errors//:go_default_library", @@ -50,6 +53,7 @@ go_test( "blocks_test.go", "committees_test.go", "config_test.go", + "slashings_test.go", "validators_test.go", ], embed = [":go_default_library"], @@ -65,6 +69,7 @@ go_test( "//beacon-chain/db/testing:go_default_library", "//beacon-chain/flags:go_default_library", "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/operations/slashings:go_default_library", "//beacon-chain/rpc/testing:go_default_library", "//beacon-chain/state:go_default_library", "//proto/beacon/p2p/v1:go_default_library", diff --git a/beacon-chain/rpc/beacon/attestations.go b/beacon-chain/rpc/beacon/attestations.go index 5f22a4a9bcd..dd518832b7b 100644 --- a/beacon-chain/rpc/beacon/attestations.go +++ b/beacon-chain/rpc/beacon/attestations.go @@ -119,7 +119,7 @@ func (bs *Server) StreamAttestations( for { select { case <-bs.SlotTicker.C(): - atts := bs.Pool.AggregatedAttestations() + atts := bs.AttestationsPool.AggregatedAttestations() for i := 0; i < len(atts); i++ { if err := stream.Send(atts[i]); err != nil { return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err) @@ -153,7 +153,7 @@ func (bs *Server) AttestationPool( flags.Get().MaxPageSize, ) } - atts := bs.Pool.AggregatedAttestations() + atts := bs.AttestationsPool.AggregatedAttestations() numAtts := len(atts) if numAtts == 0 { return ðpb.AttestationPoolResponse{ diff --git a/beacon-chain/rpc/beacon/attestations_test.go b/beacon-chain/rpc/beacon/attestations_test.go index be31853251d..5e6c1477bb7 100644 --- a/beacon-chain/rpc/beacon/attestations_test.go +++ b/beacon-chain/rpc/beacon/attestations_test.go @@ -551,7 +551,7 @@ func TestServer_AttestationPool_Pagination_ExceedsMaxPageSize(t *testing.T) { func TestServer_AttestationPool_Pagination_OutOfRange(t *testing.T) { ctx := context.Background() bs := &Server{ - Pool: attestations.NewPool(), + AttestationsPool: attestations.NewPool(), } atts := []*ethpb.Attestation{ @@ -559,7 +559,7 @@ func TestServer_AttestationPool_Pagination_OutOfRange(t *testing.T) { {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}}, {Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}}, } - if err := bs.Pool.SaveAggregatedAttestations(atts); err != nil { + if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil { t.Fatal(err) } @@ -576,7 +576,7 @@ func TestServer_AttestationPool_Pagination_OutOfRange(t *testing.T) { func TestServer_AttestationPool_Pagination_DefaultPageSize(t *testing.T) { ctx := context.Background() bs := &Server{ - Pool: attestations.NewPool(), + AttestationsPool: attestations.NewPool(), } atts := make([]*ethpb.Attestation, params.BeaconConfig().DefaultPageSize+1) @@ -586,7 +586,7 @@ func TestServer_AttestationPool_Pagination_DefaultPageSize(t *testing.T) { AggregationBits: bitfield.Bitlist{0b1101}, } } - if err := bs.Pool.SaveAggregatedAttestations(atts); err != nil { + if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil { t.Fatal(err) } @@ -610,7 +610,7 @@ func TestServer_AttestationPool_Pagination_DefaultPageSize(t *testing.T) { func TestServer_AttestationPool_Pagination_CustomPageSize(t *testing.T) { ctx := context.Background() bs := &Server{ - Pool: attestations.NewPool(), + AttestationsPool: attestations.NewPool(), } numAtts := 100 @@ -621,7 +621,7 @@ func TestServer_AttestationPool_Pagination_CustomPageSize(t *testing.T) { AggregationBits: bitfield.Bitlist{0b1101}, } } - if err := bs.Pool.SaveAggregatedAttestations(atts); err != nil { + if err := bs.AttestationsPool.SaveAggregatedAttestations(atts); err != nil { t.Fatal(err) } tests := []struct { @@ -716,9 +716,9 @@ func TestServer_StreamAttestations_OnSlotTick(t *testing.T) { Channel: make(chan uint64), } server := &Server{ - Ctx: ctx, - SlotTicker: ticker, - Pool: attestations.NewPool(), + Ctx: ctx, + SlotTicker: ticker, + AttestationsPool: attestations.NewPool(), } atts := []*ethpb.Attestation{ @@ -726,7 +726,7 @@ func TestServer_StreamAttestations_OnSlotTick(t *testing.T) { {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}}, {Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}}, } - if err := server.Pool.SaveAggregatedAttestations(atts); err != nil { + if err := server.AttestationsPool.SaveAggregatedAttestations(atts); err != nil { t.Fatal(err) } diff --git a/beacon-chain/rpc/beacon/server.go b/beacon-chain/rpc/beacon/server.go index 6b0152dd12c..6bfdc9fb02c 100644 --- a/beacon-chain/rpc/beacon/server.go +++ b/beacon-chain/rpc/beacon/server.go @@ -10,6 +10,7 @@ import ( statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/slotutil" @@ -27,7 +28,8 @@ type Server struct { ParticipationFetcher blockchain.ParticipationFetcher StateNotifier statefeed.Notifier BlockNotifier blockfeed.Notifier - Pool attestations.Pool + AttestationsPool attestations.Pool + SlashingsPool *slashings.Pool IncomingAttestation chan *ethpb.Attestation CanonicalStateChan chan *pbp2p.BeaconState ChainStartChan chan time.Time diff --git a/beacon-chain/rpc/beacon/slashings.go b/beacon-chain/rpc/beacon/slashings.go new file mode 100644 index 00000000000..7d2d8481399 --- /dev/null +++ b/beacon-chain/rpc/beacon/slashings.go @@ -0,0 +1,49 @@ +package beacon + +import ( + "context" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "github.com/prysmaticlabs/prysm/shared/sliceutil" +) + +// SubmitProposerSlashing receives a proposer slashing object via +// RPC and injects it into the beacon node's operations pool. +// Submission into this pool does not guarantee inclusion into a beacon block. +func (bs *Server) SubmitProposerSlashing( + ctx context.Context, + req *ethpb.ProposerSlashing, +) (*ethpb.SubmitSlashingResponse, error) { + beaconState, err := bs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) + } + if err := bs.SlashingsPool.InsertProposerSlashing(beaconState, req); err != nil { + return nil, status.Errorf(codes.Internal, "Could not insert proposer slashing into pool: %v", err) + } + return ðpb.SubmitSlashingResponse{ + SlashedIndices: []uint64{req.ProposerIndex}, + }, nil +} + +// SubmitAttesterSlashing receives an attester slashing object via +// RPC and injects it into the beacon node's operations pool. +// Submission into this pool does not guarantee inclusion into a beacon block. +func (bs *Server) SubmitAttesterSlashing( + ctx context.Context, + req *ethpb.AttesterSlashing, +) (*ethpb.SubmitSlashingResponse, error) { + beaconState, err := bs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) + } + if err := bs.SlashingsPool.InsertAttesterSlashing(beaconState, req); err != nil { + return nil, status.Errorf(codes.Internal, "Could not insert attester slashing into pool: %v", err) + } + slashedIndices := sliceutil.IntersectionUint64(req.Attestation_1.AttestingIndices, req.Attestation_2.AttestingIndices) + return ðpb.SubmitSlashingResponse{ + SlashedIndices: slashedIndices, + }, nil +} diff --git a/beacon-chain/rpc/beacon/slashings_test.go b/beacon-chain/rpc/beacon/slashings_test.go new file mode 100644 index 00000000000..415543f36bd --- /dev/null +++ b/beacon-chain/rpc/beacon/slashings_test.go @@ -0,0 +1,157 @@ +package beacon + +import ( + "context" + "strconv" + "testing" + + "github.com/gogo/protobuf/proto" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" + stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" + "github.com/prysmaticlabs/prysm/shared/params" +) + +func TestServer_SubmitProposerSlashing(t *testing.T) { + ctx := context.Background() + vals := make([]*ethpb.Validator, 10) + for i := 0; i < len(vals); i++ { + key := make([]byte, 48) + copy(key, strconv.Itoa(i)) + vals[i] = ðpb.Validator{ + PublicKey: key[:], + WithdrawalCredentials: make([]byte, 32), + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, + Slashed: false, + } + } + + // We mark the validator at index 5 as already slashed. + vals[5].Slashed = true + + st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{ + Slot: 0, + Validators: vals, + }) + if err != nil { + t.Fatal(err) + } + bs := &Server{ + HeadFetcher: &mock.ChainService{ + State: st, + }, + SlashingsPool: slashings.NewPool(), + } + + // We want a proposer slashing for validator with index 2 to + // be included in the pool. + wanted := ðpb.SubmitSlashingResponse{ + SlashedIndices: []uint64{2}, + } + slashing := ðpb.ProposerSlashing{ + ProposerIndex: 2, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ParentRoot: nil, + StateRoot: nil, + BodyRoot: nil, + }, + Signature: make([]byte, 96), + }, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ParentRoot: nil, + StateRoot: nil, + BodyRoot: nil, + }, + Signature: make([]byte, 96), + }, + } + res, err := bs.SubmitProposerSlashing(ctx, slashing) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(wanted, res) { + t.Errorf("Wanted %v, received %v", wanted, res) + } + + // We do not want a proposer slashing for an already slashed validator + // (the validator at index 5) to be included in the pool. + slashing.ProposerIndex = 5 + if _, err := bs.SubmitProposerSlashing(ctx, slashing); err == nil { + t.Error("Expected including a proposer slashing for an already slashed validator to fail") + } +} + +func TestServer_SubmitAttesterSlashing(t *testing.T) { + ctx := context.Background() + vals := make([]*ethpb.Validator, 10) + for i := 0; i < len(vals); i++ { + key := make([]byte, 48) + copy(key, strconv.Itoa(i)) + vals[i] = ðpb.Validator{ + PublicKey: key[:], + WithdrawalCredentials: make([]byte, 32), + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, + Slashed: false, + } + } + // We mark the validators at index 5, 6, 7 as already slashed. + vals[5].Slashed = true + vals[6].Slashed = true + vals[7].Slashed = true + + st, err := stateTrie.InitializeFromProto(&pbp2p.BeaconState{ + Slot: 0, + Validators: vals, + }) + if err != nil { + t.Fatal(err) + } + bs := &Server{ + HeadFetcher: &mock.ChainService{ + State: st, + }, + SlashingsPool: slashings.NewPool(), + } + + slashing := ðpb.AttesterSlashing{ + Attestation_1: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{1, 2, 3}, + }, + Attestation_2: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{2, 3, 4}, + }, + } + // We want the intersection of the slashing attesting indices + // to be slashed, so we expect validators 2 and 3 to be in the response + // slashed indices. + wanted := ðpb.SubmitSlashingResponse{ + SlashedIndices: []uint64{2, 3}, + } + res, err := bs.SubmitAttesterSlashing(ctx, slashing) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(wanted, res) { + t.Errorf("Wanted %v, received %v", wanted, res) + } + + // If any of the attesting indices in the slashing object have already + // been slashed, we should fail to insert properly into the attester slashing pool. + slashing = ðpb.AttesterSlashing{ + Attestation_1: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{5, 6, 7}, + }, + Attestation_2: ðpb.IndexedAttestation{ + AttestingIndices: []uint64{6, 7, 8}, + }, + } + if _, err := bs.SubmitAttesterSlashing(ctx, slashing); err == nil { + t.Error("Expected including a attester slashing for an already slashed validator to fail") + } +} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 3469eb7d5b9..a1d711389b5 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -21,6 +21,7 @@ import ( statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" "github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" @@ -67,6 +68,7 @@ type Service struct { mockEth1Votes bool attestationsPool attestations.Pool exitPool *voluntaryexits.Pool + slashingsPool *slashings.Pool syncService sync.Checker host string port string @@ -110,6 +112,7 @@ type Config struct { MockEth1Votes bool AttestationsPool attestations.Pool ExitPool *voluntaryexits.Pool + SlashingsPool *slashings.Pool SyncService sync.Checker Broadcaster p2p.Broadcaster PeersFetcher p2p.PeersProvider @@ -144,6 +147,7 @@ func NewService(ctx context.Context, cfg *Config) *Service { mockEth1Votes: cfg.MockEth1Votes, attestationsPool: cfg.AttestationsPool, exitPool: cfg.ExitPool, + slashingsPool: cfg.SlashingsPool, syncService: cfg.SyncService, host: cfg.Host, port: cfg.Port, @@ -241,7 +245,8 @@ func (s *Service) Start() { beaconChainServer := &beacon.Server{ Ctx: s.ctx, BeaconDB: s.beaconDB, - Pool: s.attestationsPool, + AttestationsPool: s.attestationsPool, + SlashingsPool: s.slashingsPool, HeadFetcher: s.headFetcher, FinalizationFetcher: s.finalizationFetcher, ParticipationFetcher: s.participationFetcher, diff --git a/shared/mock/beacon_chain_service_mock.go b/shared/mock/beacon_chain_service_mock.go index ad739ffd7e0..3d6e2538f6b 100644 --- a/shared/mock/beacon_chain_service_mock.go +++ b/shared/mock/beacon_chain_service_mock.go @@ -397,6 +397,46 @@ func (mr *MockBeaconChainClientMockRecorder) StreamValidatorsInfo(arg0 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamValidatorsInfo", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamValidatorsInfo), varargs...) } +// SubmitAttesterSlashing mocks base method +func (m *MockBeaconChainClient) SubmitAttesterSlashing(arg0 context.Context, arg1 *v1alpha1.AttesterSlashing, arg2 ...grpc.CallOption) (*v1alpha1.SubmitSlashingResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SubmitAttesterSlashing", varargs...) + ret0, _ := ret[0].(*v1alpha1.SubmitSlashingResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitAttesterSlashing indicates an expected call of SubmitAttesterSlashing +func (mr *MockBeaconChainClientMockRecorder) SubmitAttesterSlashing(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttesterSlashing", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitAttesterSlashing), varargs...) +} + +// SubmitProposerSlashing mocks base method +func (m *MockBeaconChainClient) SubmitProposerSlashing(arg0 context.Context, arg1 *v1alpha1.ProposerSlashing, arg2 ...grpc.CallOption) (*v1alpha1.SubmitSlashingResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SubmitProposerSlashing", varargs...) + ret0, _ := ret[0].(*v1alpha1.SubmitSlashingResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitProposerSlashing indicates an expected call of SubmitProposerSlashing +func (mr *MockBeaconChainClientMockRecorder) SubmitProposerSlashing(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposerSlashing", reflect.TypeOf((*MockBeaconChainClient)(nil).SubmitProposerSlashing), varargs...) +} + // MockBeaconChain_StreamChainHeadClient is a mock of BeaconChain_StreamChainHeadClient interface type MockBeaconChain_StreamChainHeadClient struct { ctrl *gomock.Controller diff --git a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch index a8d38cec6a6..cec7e64df6e 100644 --- a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch +++ b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch @@ -262,7 +262,7 @@ index 2ce5c34..4cbb276 100644 + bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""]; } diff --git a/eth/v1alpha1/beacon_chain.proto b/eth/v1alpha1/beacon_chain.proto -index 5ee000f..0f5f69c 100644 +index cdac301..945f8b5 100644 --- a/eth/v1alpha1/beacon_chain.proto +++ b/eth/v1alpha1/beacon_chain.proto @@ -15,6 +15,7 @@ syntax = "proto3"; @@ -273,7 +273,7 @@ index 5ee000f..0f5f69c 100644 import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/any.proto"; -@@ -344,7 +345,7 @@ message ChainHead { +@@ -358,7 +359,7 @@ message ChainHead { uint64 head_epoch = 2; // 32 byte merkle tree root of the canonical head block in the beacon node. @@ -282,7 +282,7 @@ index 5ee000f..0f5f69c 100644 // Most recent slot that contains the finalized block. uint64 finalized_slot = 4; -@@ -353,7 +354,7 @@ message ChainHead { +@@ -367,7 +368,7 @@ message ChainHead { uint64 finalized_epoch = 5; // Most recent 32 byte finalized block root. @@ -291,7 +291,7 @@ index 5ee000f..0f5f69c 100644 // Most recent slot that contains the justified block. uint64 justified_slot = 7; -@@ -362,7 +363,7 @@ message ChainHead { +@@ -376,7 +377,7 @@ message ChainHead { uint64 justified_epoch = 8; // Most recent 32 byte justified block root. @@ -300,7 +300,7 @@ index 5ee000f..0f5f69c 100644 // Most recent slot that contains the previous justified block. uint64 previous_justified_slot = 10; -@@ -371,7 +372,7 @@ message ChainHead { +@@ -385,7 +386,7 @@ message ChainHead { uint64 previous_justified_epoch = 11; // Previous 32 byte justified block root. @@ -309,7 +309,7 @@ index 5ee000f..0f5f69c 100644 } message ListCommitteesRequest { -@@ -416,7 +417,7 @@ message ListValidatorBalancesRequest { +@@ -430,7 +431,7 @@ message ListValidatorBalancesRequest { // Validator 48 byte BLS public keys to filter validators for the given // epoch. @@ -318,7 +318,7 @@ index 5ee000f..0f5f69c 100644 // Validator indices to filter validators for the given epoch. repeated uint64 indices = 4; -@@ -437,7 +438,7 @@ message ValidatorBalances { +@@ -451,7 +452,7 @@ message ValidatorBalances { message Balance { // Validator's 48 byte BLS public key. @@ -327,7 +327,7 @@ index 5ee000f..0f5f69c 100644 // Validator's index in the validator set. uint64 index = 2; -@@ -486,7 +487,7 @@ message GetValidatorRequest { +@@ -500,7 +501,7 @@ message GetValidatorRequest { uint64 index = 1; // 48 byte validator public key. @@ -336,7 +336,7 @@ index 5ee000f..0f5f69c 100644 } } -@@ -528,26 +529,25 @@ message ActiveSetChanges { +@@ -542,26 +543,25 @@ message ActiveSetChanges { uint64 epoch = 1; // 48 byte validator public keys that have been activated in the given epoch. @@ -369,7 +369,7 @@ index 5ee000f..0f5f69c 100644 // Indices of validators ejected in the given epoch. repeated uint64 ejected_indices = 9; -@@ -597,11 +597,11 @@ message ValidatorQueue { +@@ -611,11 +611,11 @@ message ValidatorQueue { // Ordered list of 48 byte public keys awaiting activation. 0th index is the // next key to be processed. @@ -383,7 +383,7 @@ index 5ee000f..0f5f69c 100644 } message ListValidatorAssignmentsRequest { -@@ -613,7 +613,7 @@ message ListValidatorAssignmentsRequest { +@@ -627,7 +627,7 @@ message ListValidatorAssignmentsRequest { bool genesis = 2; } // 48 byte validator public keys to filter assignments for the given epoch. @@ -392,7 +392,7 @@ index 5ee000f..0f5f69c 100644 // Validator indicies to filter assignments for the given epoch. repeated uint64 indices = 4; -@@ -648,7 +648,7 @@ message ValidatorAssignments { +@@ -662,7 +662,7 @@ message ValidatorAssignments { uint64 proposer_slot = 4; // 48 byte BLS public key.