diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index 72b8020e3b..1ef50ab49c 100644 --- a/beacon-chain/rpc/beacon/BUILD.bazel +++ b/beacon-chain/rpc/beacon/BUILD.bazel @@ -31,12 +31,14 @@ go_library( "//beacon-chain/flags:go_default_library", "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/operations/slashings:go_default_library", + "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//beacon-chain/state:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/attestationutil:go_default_library", "//shared/bytesutil:go_default_library", "//shared/event:go_default_library", + "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", "//shared/pagination:go_default_library", "//shared/params:go_default_library", @@ -81,10 +83,12 @@ go_test( "//beacon-chain/flags:go_default_library", "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/operations/slashings:go_default_library", + "//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/rpc/testing:go_default_library", "//beacon-chain/state:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/attestationutil:go_default_library", + "//shared/featureconfig:go_default_library", "//shared/params:go_default_library", "//shared/testutil:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", diff --git a/beacon-chain/rpc/beacon/server.go b/beacon-chain/rpc/beacon/server.go index d83595165e..484ac6c827 100644 --- a/beacon-chain/rpc/beacon/server.go +++ b/beacon-chain/rpc/beacon/server.go @@ -13,6 +13,7 @@ import ( "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/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ) @@ -33,6 +34,7 @@ type Server struct { StateNotifier statefeed.Notifier BlockNotifier blockfeed.Notifier AttestationNotifier operation.Notifier + Broadcaster p2p.Broadcaster AttestationsPool attestations.Pool SlashingsPool *slashings.Pool CanonicalStateChan chan *pbp2p.BeaconState diff --git a/beacon-chain/rpc/beacon/slashings.go b/beacon-chain/rpc/beacon/slashings.go index 50a9ba93b8..f8534acc16 100644 --- a/beacon-chain/rpc/beacon/slashings.go +++ b/beacon-chain/rpc/beacon/slashings.go @@ -4,6 +4,7 @@ import ( "context" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/sliceutil" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -23,6 +24,9 @@ func (bs *Server) SubmitProposerSlashing( if err := bs.SlashingsPool.InsertProposerSlashing(ctx, beaconState, req); err != nil { return nil, status.Errorf(codes.Internal, "Could not insert proposer slashing into pool: %v", err) } + if featureconfig.Get().BroadcastSlashings { + bs.Broadcaster.Broadcast(ctx, req) + } return ðpb.SubmitSlashingResponse{ SlashedIndices: []uint64{req.ProposerIndex}, }, nil @@ -42,6 +46,9 @@ func (bs *Server) SubmitAttesterSlashing( if err := bs.SlashingsPool.InsertAttesterSlashing(ctx, beaconState, req); err != nil { return nil, status.Errorf(codes.Internal, "Could not insert attester slashing into pool: %v", err) } + if featureconfig.Get().BroadcastSlashings { + bs.Broadcaster.Broadcast(ctx, req) + } slashedIndices := sliceutil.IntersectionUint64(req.Attestation_1.AttestingIndices, req.Attestation_2.AttestingIndices) return ðpb.SubmitSlashingResponse{ SlashedIndices: slashedIndices, diff --git a/beacon-chain/rpc/beacon/slashings_test.go b/beacon-chain/rpc/beacon/slashings_test.go index 08fff1fcdd..87dc11bc47 100644 --- a/beacon-chain/rpc/beacon/slashings_test.go +++ b/beacon-chain/rpc/beacon/slashings_test.go @@ -8,6 +8,8 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings" + mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/testutil" ) @@ -24,11 +26,13 @@ func TestServer_SubmitProposerSlashing(t *testing.T) { t.Fatal(err) } + mb := &mockp2p.MockBroadcaster{} bs := &Server{ HeadFetcher: &mock.ChainService{ State: st, }, SlashingsPool: slashings.NewPool(), + Broadcaster: mb, } // We want a proposer slashing for validator with index 2 to @@ -49,6 +53,10 @@ func TestServer_SubmitProposerSlashing(t *testing.T) { t.Errorf("Wanted %v, received %v", wanted, res) } + if mb.BroadcastCalled { + t.Errorf("Expected broadcast not to be called by default") + } + slashing, err = testutil.GenerateProposerSlashingForValidator(st, privs[5], uint64(5)) if err != nil { t.Fatal(err) @@ -61,6 +69,53 @@ func TestServer_SubmitProposerSlashing(t *testing.T) { } } +func TestServer_SubmitProposerSlashingBroadcast(t *testing.T) { + ctx := context.Background() + cfg := featureconfig.Get() + cfg.BroadcastSlashings = true + featureconfig.Init(cfg) + defer func() { + cfg.BroadcastSlashings = false + featureconfig.Init(cfg) + }() + + st, privs := testutil.DeterministicGenesisState(t, 64) + slashedVal, err := st.ValidatorAtIndex(5) + if err != nil { + t.Fatal(err) + } + // We mark the validator at index 5 as already slashed. + slashedVal.Slashed = true + if err := st.UpdateValidatorAtIndex(5, slashedVal); err != nil { + t.Fatal(err) + } + + mb := &mockp2p.MockBroadcaster{} + bs := &Server{ + HeadFetcher: &mock.ChainService{ + State: st, + }, + SlashingsPool: slashings.NewPool(), + Broadcaster: mb, + } + + // We want a proposer slashing for validator with index 2 to + // be included in the pool. + slashing, err := testutil.GenerateProposerSlashingForValidator(st, privs[2], uint64(2)) + if err != nil { + t.Fatal(err) + } + + _, err = bs.SubmitProposerSlashing(ctx, slashing) + if err != nil { + t.Fatal(err) + } + + if !mb.BroadcastCalled { + t.Errorf("Expected broadcast to be called") + } +} + func TestServer_SubmitAttesterSlashing(t *testing.T) { ctx := context.Background() // We mark the validators at index 5, 6 as already slashed. @@ -76,11 +131,13 @@ func TestServer_SubmitAttesterSlashing(t *testing.T) { t.Fatal(err) } + mb := &mockp2p.MockBroadcaster{} bs := &Server{ HeadFetcher: &mock.ChainService{ State: st, }, SlashingsPool: slashings.NewPool(), + Broadcaster: mb, } slashing, err := testutil.GenerateAttesterSlashingForValidator(st, privs[2], uint64(2)) @@ -101,6 +158,9 @@ func TestServer_SubmitAttesterSlashing(t *testing.T) { if !proto.Equal(wanted, res) { t.Errorf("Wanted %v, received %v", wanted, res) } + if mb.BroadcastCalled { + t.Errorf("Expected broadcast not to be called by default") + } slashing, err = testutil.GenerateAttesterSlashingForValidator(st, privs[5], uint64(5)) if err != nil { @@ -112,3 +172,51 @@ func TestServer_SubmitAttesterSlashing(t *testing.T) { t.Error("Expected including a attester slashing for an already slashed validator to fail") } } + +func TestServer_SubmitAttesterSlashingBroadcast(t *testing.T) { + ctx := context.Background() + cfg := featureconfig.Get() + cfg.BroadcastSlashings = true + featureconfig.Init(cfg) + defer func() { + cfg.BroadcastSlashings = false + featureconfig.Init(cfg) + }() + // We mark the validators at index 5, 6 as already slashed. + st, privs := testutil.DeterministicGenesisState(t, 64) + slashedVal, err := st.ValidatorAtIndex(5) + if err != nil { + t.Fatal(err) + } + + // We mark the validator at index 5 as already slashed. + slashedVal.Slashed = true + if err := st.UpdateValidatorAtIndex(5, slashedVal); err != nil { + t.Fatal(err) + } + + mb := &mockp2p.MockBroadcaster{} + bs := &Server{ + HeadFetcher: &mock.ChainService{ + State: st, + }, + SlashingsPool: slashings.NewPool(), + Broadcaster: mb, + } + + slashing, err := testutil.GenerateAttesterSlashingForValidator(st, privs[2], uint64(2)) + if err != nil { + t.Fatal(err) + } + + // 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. + _, err = bs.SubmitAttesterSlashing(ctx, slashing) + if err != nil { + t.Fatal(err) + } + if !mb.BroadcastCalled { + t.Errorf("Expected broadcast to be called when flag is set") + } +} diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 7fa32062ab..116d1be415 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -257,6 +257,7 @@ func (s *Service) Start() { StateNotifier: s.stateNotifier, BlockNotifier: s.blockNotifier, AttestationNotifier: s.operationNotifier, + Broadcaster: s.p2p, ReceivedAttestationsBuffer: make(chan *ethpb.Attestation, 100), CollectedAttestationsBuffer: make(chan []*ethpb.Attestation, 100), } diff --git a/shared/featureconfig/config.go b/shared/featureconfig/config.go index a2fdf1fe7f..c0cf0c29bf 100644 --- a/shared/featureconfig/config.go +++ b/shared/featureconfig/config.go @@ -53,6 +53,9 @@ type Flags struct { // as the chain head. UNSAFE, use with caution. DisableForkChoice bool + // BroadcastSlashings enables p2p broadcasting of proposer or attester slashing. + BroadcastSlashings bool + // Cache toggles. EnableSSZCache bool // EnableSSZCache see https://github.com/prysmaticlabs/prysm/pull/4558. EnableEth1DataVoteCache bool // EnableEth1DataVoteCache; see https://github.com/prysmaticlabs/prysm/issues/3106. diff --git a/shared/featureconfig/flags.go b/shared/featureconfig/flags.go index 9ccfdaca4a..90b084eedb 100644 --- a/shared/featureconfig/flags.go +++ b/shared/featureconfig/flags.go @@ -5,6 +5,10 @@ import ( ) var ( + broadcastSlashingFlag = cli.BoolFlag{ + Name: "broadcast-slashing", + Usage: "Broadcast slashings from slashing pool.", + } noCustomConfigFlag = cli.BoolFlag{ Name: "no-custom-config", Usage: "Run the beacon chain with the real parameters from phase 0.", @@ -293,6 +297,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{ checkHeadState, enableNoiseHandshake, dontPruneStateStartUp, + broadcastSlashingFlag, }...) // E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.