Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect Proposer Slashing Implementation #5139

Merged
merged 33 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
116e308
detect blocks
shayzluf Mar 19, 2020
2674f75
Merge branch 'master' of github.com:prysmaticlabs/prysm
shayzluf Mar 19, 2020
e4b582f
detect blocks
shayzluf Mar 19, 2020
e03d9c2
use stub
shayzluf Mar 19, 2020
b1be514
use stub
shayzluf Mar 19, 2020
f088275
use stub
shayzluf Mar 19, 2020
dc42ec3
todo
shayzluf Mar 19, 2020
66809b9
fix test
shayzluf Mar 19, 2020
6edcf24
add tests and utils
shayzluf Mar 23, 2020
69e2509
fix imports
shayzluf Mar 23, 2020
de3b0c7
fix imports
shayzluf Mar 23, 2020
71a70d0
fix comment
shayzluf Mar 23, 2020
1347540
todo
shayzluf Mar 23, 2020
dd4bc43
Merge branch 'v0.11' of github.com:prysmaticlabs/prysm into detect_bl…
shayzluf Apr 2, 2020
f4fb01b
proposerIndex
shayzluf Apr 2, 2020
ab35ccb
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
cc5f78e
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
9b4242e
fix broken test
rauljordan Apr 2, 2020
825dc0a
formatting and simplified if
rauljordan Apr 2, 2020
0d63e29
Update slasher/detection/service.go
rauljordan Apr 2, 2020
eb13443
Update slasher/detection/testing/utils.go
rauljordan Apr 2, 2020
c2b9aca
fixed up final comments
rauljordan Apr 2, 2020
dc00405
Merge branch 'detect_blocks' of github.com:prysmaticlabs/prysm into d…
rauljordan Apr 2, 2020
2c28be1
better naming
rauljordan Apr 2, 2020
cf68180
Update slasher/detection/service.go
terencechain Apr 2, 2020
e8ad08a
Update slasher/detection/service.go
terencechain Apr 2, 2020
ce86f8e
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
53d63a5
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
680c093
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
6020715
Update slasher/detection/service.go
rauljordan Apr 2, 2020
b9fe596
no more named args
rauljordan Apr 2, 2020
7dd035c
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
776e27d
Merge refs/heads/v0.11 into detect_blocks
prylabs-bulldozer[bot] Apr 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions slasher/db/iface/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions slasher/db/kv/block_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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 {
Expand Down
81 changes: 32 additions & 49 deletions slasher/db/kv/block_header_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}},
},
{
vID: uint64(1),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}},

bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.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)
}
Expand All @@ -86,44 +83,40 @@ func TestDeleteHistoryBlkHdr(t *testing.T) {
ctx := context.Background()

tests := []struct {
vID uint64
bh *ethpb.SignedBeaconBlockHeader
bh *ethpb.SignedBeaconBlockHeader
}{
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}},
},
{
vID: uint64(1),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.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)
}

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)
Expand All @@ -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: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}},
},
{
vID: uint64(1),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.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")
Expand All @@ -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: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 0}},
},
{
vID: uint64(1),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd"), Header: &ethpb.BeaconBlockHeader{Slot: 0, ProposerIndex: 1}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch + 1, ProposerIndex: 0}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 4th"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 4th"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*2 + 1, ProposerIndex: 0}},
},
{
vID: uint64(0),
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 5th"), Header: &ethpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch*3 + 1}},
bh: &ethpb.SignedBeaconBlockHeader{Signature: []byte("let me in 5th"), Header: &ethpb.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)
}
Expand All @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions slasher/detection/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
Expand All @@ -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",
Expand Down
9 changes: 9 additions & 0 deletions slasher/detection/detect.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package detection

import (
"bytes"
"context"

"github.com/gogo/protobuf/proto"
Expand Down Expand Up @@ -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
}
Expand Down
80 changes: 80 additions & 0 deletions slasher/detection/detect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package detection

import (
"context"
"reflect"
"testing"

ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
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) {
Expand Down Expand Up @@ -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: &ethpb.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,
)
}

})
}
}
Loading