diff --git a/slasher/db/iface/interface.go b/slasher/db/iface/interface.go index 00c81a2771a5..441d18732af9 100644 --- a/slasher/db/iface/interface.go +++ b/slasher/db/iface/interface.go @@ -50,8 +50,8 @@ type WriteAccessDatabase interface { SetLatestEpochDetected(ctx context.Context, epoch uint64) error // BlockHeader related methods. - SaveBlockHeader(ctx context.Context, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error - DeleteBlockHeader(ctx context.Context, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error + SaveBlockHeader(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) error + DeleteBlockHeader(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) error PruneBlockHistory(ctx context.Context, currentEpoch uint64, pruningEpochAge uint64) error // IndexedAttestations related methods. diff --git a/slasher/db/kv/block_header.go b/slasher/db/kv/block_header.go index 3c3555689de7..667f1a0ef3a5 100644 --- a/slasher/db/kv/block_header.go +++ b/slasher/db/kv/block_header.go @@ -67,11 +67,11 @@ func (db *Store) HasBlockHeader(ctx context.Context, epoch uint64, validatorID u } // SaveBlockHeader accepts a block header and writes it to disk. -func (db *Store) SaveBlockHeader(ctx context.Context, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error { +func (db *Store) SaveBlockHeader(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) error { ctx, span := trace.StartSpan(ctx, "slasherDB.SaveBlockHeader") defer span.End() epoch := helpers.SlotToEpoch(blockHeader.Header.Slot) - key := encodeEpochValidatorIDSig(epoch, validatorID, blockHeader.Signature) + key := encodeEpochValidatorIDSig(epoch, blockHeader.Header.ProposerIndex, blockHeader.Signature) enc, err := proto.Marshal(blockHeader) if err != nil { return errors.Wrap(err, "failed to encode block") @@ -97,11 +97,11 @@ func (db *Store) SaveBlockHeader(ctx context.Context, validatorID uint64, blockH } // DeleteBlockHeader deletes a block header using the epoch and validator id. -func (db *Store) DeleteBlockHeader(ctx context.Context, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error { +func (db *Store) DeleteBlockHeader(ctx context.Context, blockHeader *ethpb.SignedBeaconBlockHeader) error { ctx, span := trace.StartSpan(ctx, "slasherDB.DeleteBlockHeader") defer span.End() epoch := helpers.SlotToEpoch(blockHeader.Header.Slot) - key := encodeEpochValidatorIDSig(epoch, validatorID, blockHeader.Signature) + key := encodeEpochValidatorIDSig(epoch, blockHeader.Header.ProposerIndex, blockHeader.Signature) return db.update(func(tx *bolt.Tx) error { bucket := tx.Bucket(historicBlockHeadersBucket) if err := bucket.Delete(key); err != nil { diff --git a/slasher/db/kv/block_header_test.go b/slasher/db/kv/block_header_test.go index 658b03eecf6b..6998afbf7904 100644 --- a/slasher/db/kv/block_header_test.go +++ b/slasher/db/kv/block_header_test.go @@ -43,30 +43,27 @@ func TestSaveHistoryBlkHdr(t *testing.T) { ctx := context.Background() tests := []struct { - vID uint64 - bh *ethpb.SignedBeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}}, }, { - vID: uint64(1), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}}, + + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1, ProposerIndex: 0}}, }, } for _, tt := range tests { - err := db.SaveBlockHeader(ctx, tt.vID, tt.bh) + err := db.SaveBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block failed: %v", err) } - bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if err != nil { t.Fatalf("failed to get block: %v", err) } @@ -86,32 +83,28 @@ func TestDeleteHistoryBlkHdr(t *testing.T) { ctx := context.Background() tests := []struct { - vID uint64 - bh *ethpb.SignedBeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}}, }, { - vID: uint64(1), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1, ProposerIndex: 0}}, }, } for _, tt := range tests { - err := db.SaveBlockHeader(ctx, tt.vID, tt.bh) + err := db.SaveBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block failed: %v", err) } } for _, tt := range tests { - bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if err != nil { t.Fatalf("failed to get block: %v", err) } @@ -119,11 +112,11 @@ func TestDeleteHistoryBlkHdr(t *testing.T) { if bha == nil || !reflect.DeepEqual(bha[0], tt.bh) { t.Fatalf("get should return bh: %v", bha) } - err = db.DeleteBlockHeader(ctx, tt.vID, tt.bh) + err = db.DeleteBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block failed: %v", err) } - bh, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + bh, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if err != nil { t.Fatal(err) @@ -144,40 +137,36 @@ func TestHasHistoryBlkHdr(t *testing.T) { ctx := context.Background() tests := []struct { - vID uint64 - bh *ethpb.SignedBeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}}, }, { - vID: uint64(1), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1, ProposerIndex: 0}}, }, } for _, tt := range tests { - found := db.HasBlockHeader(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + found := db.HasBlockHeader(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if found { t.Fatal("has block header should return false for block headers that are not in db") } - err := db.SaveBlockHeader(ctx, tt.vID, tt.bh) + err := db.SaveBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block failed: %v", err) } } for _, tt := range tests { - err := db.SaveBlockHeader(ctx, tt.vID, tt.bh) + err := db.SaveBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block failed: %v", err) } - found := db.HasBlockHeader(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + found := db.HasBlockHeader(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if !found { t.Fatal("has block header should return true") @@ -193,38 +182,32 @@ func TestPruneHistoryBlkHdr(t *testing.T) { ctx := context.Background() tests := []struct { - vID uint64 - bh *ethpb.SignedBeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}}, }, { - vID: uint64(1), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: ðpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1, ProposerIndex: 0}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 4th"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 4th"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1, ProposerIndex: 0}}, }, { - vID: uint64(0), - bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 5th"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*3 + 1}}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 5th"), Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*3 + 1, ProposerIndex: 0}}, }, } for _, tt := range tests { - err := db.SaveBlockHeader(ctx, tt.vID, tt.bh) + err := db.SaveBlockHeader(ctx, tt.bh) if err != nil { t.Fatalf("save block header failed: %v", err) } - bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if err != nil { t.Fatalf("failed to get block header: %v", err) } @@ -241,7 +224,7 @@ func TestPruneHistoryBlkHdr(t *testing.T) { } for _, tt := range tests { - bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.vID) + bha, err := db.BlockHeaders(ctx, helpers.SlotToEpoch(tt.bh.Header.Slot), tt.bh.Header.ProposerIndex) if err != nil { t.Fatalf("failed to get block header: %v", err) } diff --git a/slasher/detection/BUILD.bazel b/slasher/detection/BUILD.bazel index 951224399a5e..d7f1ffd91136 100644 --- a/slasher/detection/BUILD.bazel +++ b/slasher/detection/BUILD.bazel @@ -27,6 +27,7 @@ go_library( "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@io_opencensus_go//trace:go_default_library", ], @@ -46,6 +47,8 @@ go_test( "//slasher/db/testing:go_default_library", "//slasher/db/types:go_default_library", "//slasher/detection/attestations:go_default_library", + "//slasher/detection/proposals:go_default_library", + "//slasher/detection/testing:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", diff --git a/slasher/detection/detect.go b/slasher/detection/detect.go index a3197cfa0ec1..626f9cc22130 100644 --- a/slasher/detection/detect.go +++ b/slasher/detection/detect.go @@ -1,6 +1,7 @@ package detection import ( + "bytes" "context" "github.com/gogo/protobuf/proto" @@ -163,6 +164,14 @@ func (ds *Service) DetectDoubleProposals(ctx context.Context, incomingBlock *eth return ds.proposalsDetector.DetectDoublePropose(ctx, incomingBlock) } +func isDoublePropose( + incomingBlockHeader *ethpb.SignedBeaconBlockHeader, + prevBlockHeader *ethpb.SignedBeaconBlockHeader, +) bool { + return incomingBlockHeader.Header.ProposerIndex == prevBlockHeader.Header.ProposerIndex && + !bytes.Equal(incomingBlockHeader.Signature, prevBlockHeader.Signature) +} + func isDoubleVote(incomingAtt *ethpb.IndexedAttestation, prevAtt *ethpb.IndexedAttestation) bool { return !proto.Equal(incomingAtt.Data, prevAtt.Data) && incomingAtt.Data.Target.Epoch == prevAtt.Data.Target.Epoch } diff --git a/slasher/detection/detect_test.go b/slasher/detection/detect_test.go index aab8eb6f841a..c5e329998bea 100644 --- a/slasher/detection/detect_test.go +++ b/slasher/detection/detect_test.go @@ -2,6 +2,7 @@ package detection import ( "context" + "reflect" "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" @@ -9,6 +10,8 @@ import ( testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" status "github.com/prysmaticlabs/prysm/slasher/db/types" "github.com/prysmaticlabs/prysm/slasher/detection/attestations" + "github.com/prysmaticlabs/prysm/slasher/detection/proposals" + testDetect "github.com/prysmaticlabs/prysm/slasher/detection/testing" ) func TestDetect_detectAttesterSlashings_Surround(t *testing.T) { @@ -341,3 +344,80 @@ func TestDetect_detectAttesterSlashings_Double(t *testing.T) { }) } } + +func TestDetect_detectProposerSlashing(t *testing.T) { + type testStruct struct { + name string + blk *ethpb.SignedBeaconBlockHeader + incomingBlk *ethpb.SignedBeaconBlockHeader + slashing *ethpb.ProposerSlashing + } + blk1epoch0, err := testDetect.SignedBlockHeader(testDetect.StartSlot(0), 0) + if err != nil { + t.Fatal(err) + } + blk2epoch0, err := testDetect.SignedBlockHeader(testDetect.StartSlot(0)+1, 0) + if err != nil { + t.Fatal(err) + } + blk1epoch1, err := testDetect.SignedBlockHeader(testDetect.StartSlot(1), 0) + if err != nil { + t.Fatal(err) + } + tests := []testStruct{ + { + name: "same block sig dont slash", + blk: blk1epoch0, + incomingBlk: blk1epoch0, + slashing: nil, + }, + { + name: "block from different epoch dont slash", + blk: blk1epoch0, + incomingBlk: blk1epoch1, + slashing: nil, + }, + { + name: "different sig from same epoch slash", + blk: blk1epoch0, + incomingBlk: blk2epoch0, + slashing: ðpb.ProposerSlashing{Header_1: blk2epoch0, Header_2: blk1epoch0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := testDB.SetupSlasherDB(t, false) + defer testDB.TeardownSlasherDB(t, db) + ctx := context.Background() + ds := Service{ + ctx: ctx, + slasherDB: db, + proposalsDetector: proposals.NewProposeDetector(db), + } + if err := db.SaveBlockHeader(ctx, tt.blk); err != nil { + t.Fatal(err) + } + + slashing, err := ds.proposalsDetector.DetectDoublePropose(ctx, tt.incomingBlk) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(slashing, tt.slashing) { + t.Errorf("Wanted: %v, received %v", tt.slashing, slashing) + } + savedSlashings, err := db.ProposalSlashingsByStatus(ctx, status.Active) + if tt.slashing != nil && len(savedSlashings) != 1 { + t.Fatalf("Did not save slashing to db") + } + + if slashing != nil && !isDoublePropose(slashing.Header_1, slashing.Header_2) { + t.Fatalf( + "Expected slashing to be valid, received atts with target epoch %v and %v but not valid", + slashing.Header_1, + slashing.Header_2, + ) + } + + }) + } +} diff --git a/slasher/detection/listeners.go b/slasher/detection/listeners.go index 81b99e821fc1..89a519300fee 100644 --- a/slasher/detection/listeners.go +++ b/slasher/detection/listeners.go @@ -9,7 +9,9 @@ package detection import ( "context" + "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" "go.opencensus.io/trace" ) @@ -24,9 +26,14 @@ func (ds *Service) detectIncomingBlocks(ctx context.Context, ch chan *ethpb.Sign defer sub.Unsubscribe() for { select { - case <-ch: + case sblk := <-ch: log.Debug("Running detection on block...") - // TODO(#4836): Run detection function for proposer slashings. + sbh, err := signedBeaconBlockHeaderFromBlock(sblk) + if err != nil { + log.WithError(err) + } + slashing, err := ds.proposalsDetector.DetectDoublePropose(ctx, sbh) + ds.submitProposerSlashing(ctx, slashing) case <-sub.Err(): log.Error("Subscriber closed, exiting goroutine") return @@ -69,3 +76,20 @@ func (ds *Service) detectIncomingAttestations(ctx context.Context, ch chan *ethp } } } + +func signedBeaconBlockHeaderFromBlock(block *ethpb.SignedBeaconBlock) (*ethpb.SignedBeaconBlockHeader, error) { + bodyRoot, err := ssz.HashTreeRoot(block.Block.Body) + if err != nil { + return nil, errors.Wrap(err, "Failed to get signing root of block") + } + return ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: block.Block.Slot, + ProposerIndex: block.Block.ProposerIndex, + ParentRoot: block.Block.ParentRoot, + StateRoot: block.Block.StateRoot, + BodyRoot: bodyRoot[:], + }, + Signature: block.Signature, + }, nil +} diff --git a/slasher/detection/listeners_test.go b/slasher/detection/listeners_test.go index 219b959d48c8..33378ea2df32 100644 --- a/slasher/detection/listeners_test.go +++ b/slasher/detection/listeners_test.go @@ -8,7 +8,9 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/testutil" + testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" "github.com/prysmaticlabs/prysm/slasher/detection/attestations" + "github.com/prysmaticlabs/prysm/slasher/detection/proposals" "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" ) @@ -34,8 +36,11 @@ func (m *mockNotifier) ClientReadyFeed() *event.Feed { func TestService_DetectIncomingBlocks(t *testing.T) { hook := logTest.NewGlobal() + db := testDB.SetupSlasherDB(t, false) + defer testDB.TeardownSlasherDB(t, db) ds := Service{ - notifier: &mockNotifier{}, + notifier: &mockNotifier{}, + proposalsDetector: proposals.NewProposeDetector(db), } blk := ðpb.SignedBeaconBlock{ Block: ðpb.BeaconBlock{Slot: 1}, diff --git a/slasher/detection/proposals/BUILD.bazel b/slasher/detection/proposals/BUILD.bazel index 6eb9550290eb..9dc8848936a1 100644 --- a/slasher/detection/proposals/BUILD.bazel +++ b/slasher/detection/proposals/BUILD.bazel @@ -19,9 +19,9 @@ go_test( srcs = ["detector_test.go"], embed = [":go_default_library"], deps = [ - "//shared/params:go_default_library", "//slasher/db/testing:go_default_library", "//slasher/detection/proposals/iface:go_default_library", + "//slasher/detection/testing:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", ], ) diff --git a/slasher/detection/proposals/detector.go b/slasher/detection/proposals/detector.go index c4f234c2d3ca..05b6e73dd583 100644 --- a/slasher/detection/proposals/detector.go +++ b/slasher/detection/proposals/detector.go @@ -32,10 +32,7 @@ func (dd *ProposeDetector) DetectDoublePropose( ctx, span := trace.StartSpan(ctx, "detector.DetectDoublePropose") defer span.End() epoch := helpers.SlotToEpoch(incomingBlk.Header.Slot) - //TODO(#5119) remove constand and use input from block header. - //validatorIdx:=blk.Header.ProposerIndex - proposerIdx := uint64(0) - bha, err := dd.slasherDB.BlockHeaders(ctx, epoch, proposerIdx) + bha, err := dd.slasherDB.BlockHeaders(ctx, epoch, incomingBlk.Header.ProposerIndex) if err != nil { return nil, err } diff --git a/slasher/detection/proposals/detector_test.go b/slasher/detection/proposals/detector_test.go index 46718be93f8c..77302bd3d3b8 100644 --- a/slasher/detection/proposals/detector_test.go +++ b/slasher/detection/proposals/detector_test.go @@ -2,14 +2,13 @@ package proposals import ( "context" - "crypto/rand" "reflect" "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/prysm/shared/params" testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" "github.com/prysmaticlabs/prysm/slasher/detection/proposals/iface" + testDetect "github.com/prysmaticlabs/prysm/slasher/detection/testing" ) var _ = iface.ProposalsDetector(&ProposeDetector{}) @@ -21,22 +20,18 @@ func TestProposalsDetector_DetectSlashingsForBlockHeaders(t *testing.T) { incomingBlk *ethpb.SignedBeaconBlockHeader slashing *ethpb.ProposerSlashing } - blk1epoch0, err := signedBlockHeader(startSlot(0), 0) + blk1epoch0, err := testDetect.SignedBlockHeader(testDetect.StartSlot(0), 0) if err != nil { t.Fatal(err) } - blk2epoch0, err := signedBlockHeader(startSlot(0)+1, 0) + blk2epoch0, err := testDetect.SignedBlockHeader(testDetect.StartSlot(0)+1, 0) if err != nil { t.Fatal(err) } - blk1epoch1, err := signedBlockHeader(startSlot(1), 0) + blk1epoch1, err := testDetect.SignedBlockHeader(testDetect.StartSlot(1), 0) if err != nil { t.Fatal(err) } - //blk1epoch3, err := signedBlockHeader(startSlot(3), 0) - //if err != nil { - // t.Fatal(err) - //} tests := []testStruct{ { name: "same block sig dont slash", @@ -68,7 +63,7 @@ func TestProposalsDetector_DetectSlashingsForBlockHeaders(t *testing.T) { slasherDB: db, } - if err := sd.slasherDB.SaveBlockHeader(ctx, 0, tt.blk); err != nil { + if err := sd.slasherDB.SaveBlockHeader(ctx, tt.blk); err != nil { t.Fatal(err) } @@ -84,31 +79,3 @@ func TestProposalsDetector_DetectSlashingsForBlockHeaders(t *testing.T) { }) } } - -func signedBlockHeader(slot uint64, proposerIdx uint64) (*ethpb.SignedBeaconBlockHeader, error) { - sig, err := genRandomSig() - if err != nil { - return nil, err - } - root := [32]byte{1, 2, 3} - return ðpb.SignedBeaconBlockHeader{ - Header: ðpb.BeaconBlockHeader{ - //ProposerIndex proposerIndex, - Slot: slot, - ParentRoot: root[:], - StateRoot: root[:], - BodyRoot: root[:], - }, - Signature: sig, - }, nil -} - -func genRandomSig() (blk []byte, err error) { - blk = make([]byte, 96) - _, err = rand.Read(blk) - return -} - -func startSlot(epoch uint64) uint64 { - return epoch * params.BeaconConfig().SlotsPerEpoch -} diff --git a/slasher/detection/service.go b/slasher/detection/service.go index 987592649a2a..c984406cd352 100644 --- a/slasher/detection/service.go +++ b/slasher/detection/service.go @@ -161,3 +161,17 @@ func (ds *Service) submitAttesterSlashings(ctx context.Context, slashings []*eth } } } + +func (ds *Service) submitProposerSlashing(ctx context.Context, slashing *ethpb.ProposerSlashing) { + ctx, span := trace.StartSpan(ctx, "detection.submitProposerSlashing") + defer span.End() + if slashing != nil && slashing.Header_1 != nil && slashing.Header_2 != nil { + log.WithFields(logrus.Fields{ + "header1Slot": slashing.Header_1.Header.Slot, + "header2Slot": slashing.Header_2.Header.Slot, + "proposerIdxHeader1": slashing.Header_1.Header.ProposerIndex, + "proposerIdxHeader2": slashing.Header_2.Header.ProposerIndex, + }).Info("Found a proposer slashing! Submitting to beacon node") + ds.proposerSlashingsFeed.Send(slashing) + } +} diff --git a/slasher/detection/testing/BUILD.bazel b/slasher/detection/testing/BUILD.bazel new file mode 100644 index 000000000000..73aacd54ed39 --- /dev/null +++ b/slasher/detection/testing/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["utils.go"], + importpath = "github.com/prysmaticlabs/prysm/slasher/detection/testing", + visibility = ["//visibility:public"], + deps = [ + "//shared/params:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + ], +) diff --git a/slasher/detection/testing/utils.go b/slasher/detection/testing/utils.go new file mode 100644 index 000000000000..843f7b121c0b --- /dev/null +++ b/slasher/detection/testing/utils.go @@ -0,0 +1,39 @@ +package testing + +import ( + "crypto/rand" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/shared/params" +) + +// SignedBlockHeader given slot, proposer index this function generates signed block header. +// with random bytes as its signature. +func SignedBlockHeader(slot uint64, proposerIdx uint64) (*ethpb.SignedBeaconBlockHeader, error) { + sig, err := genRandomSig() + if err != nil { + return nil, err + } + root := [32]byte{1, 2, 3} + return ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + ProposerIndex: proposerIdx, + Slot: slot, + ParentRoot: root[:], + StateRoot: root[:], + BodyRoot: root[:], + }, + Signature: sig, + }, nil +} + +func genRandomSig() ([]byte, error) { + blk := make([]byte, 96) + _, err := rand.Read(blk) + return blk, err +} + +// StartSlot returns the first slot of a given epoch. +func StartSlot(epoch uint64) uint64 { + return epoch * params.BeaconConfig().SlotsPerEpoch +}