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

Verify Slashing Signatures Before Putting Into Blocks #5071

Merged
merged 27 commits into from
Mar 12, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
62effe8
rem slasher proto
rauljordan Mar 3, 2020
e44fea7
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 5, 2020
3b5a515
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 5, 2020
aabb910
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 5, 2020
202e7d2
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 5, 2020
5adf2af
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 5, 2020
86eb6aa
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 6, 2020
ff839d3
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 6, 2020
3720d41
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 9, 2020
b695a33
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 10, 2020
8cb66a8
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 10, 2020
a9b3168
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 10, 2020
d2ca3b1
Merge branch 'master' of github.com:prysmaticlabs/prysm
rauljordan Mar 11, 2020
3931e0d
verify slashing
rauljordan Mar 11, 2020
55486a1
added in test for pending att slashing
rauljordan Mar 11, 2020
8347415
tests starting to apss
rauljordan Mar 11, 2020
8bdfc40
sig failed verify regression test
rauljordan Mar 11, 2020
7f87a9b
tests passing for ops pool
rauljordan Mar 11, 2020
c287271
Update beacon-chain/operations/slashings/service.go
rauljordan Mar 11, 2020
b2d63d5
Merge refs/heads/master into verify-slash-sig
prylabs-bulldozer[bot] Mar 11, 2020
a588e88
verify on insert
rauljordan Mar 11, 2020
71d91ce
tests starting to pass
rauljordan Mar 11, 2020
4247111
all code paths fixed
rauljordan Mar 11, 2020
78c3502
imports
rauljordan Mar 11, 2020
1aaa213
fix build
rauljordan Mar 11, 2020
87dbb4d
fix rpc errors
rauljordan Mar 12, 2020
d34b8e8
Merge refs/heads/master into verify-slash-sig
prylabs-bulldozer[bot] Mar 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions beacon-chain/operations/slashings/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/state:go_default_library",
"//shared/params:go_default_library",
"//shared/sliceutil:go_default_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",
"@io_opencensus_go//trace:go_default_library",
],
)

Expand All @@ -33,6 +35,7 @@ go_test(
"//beacon-chain/state:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
Expand Down
12 changes: 12 additions & 0 deletions beacon-chain/operations/slashings/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import (
)

var (
numPendingAttesterSlashingFailedSigVerify = promauto.NewCounter(
prometheus.CounterOpts{
Name: "pending_attester_slashing_fail_sig_verify_total",
Help: "Times an pending attester slashing fails sig verification",
},
)
numPendingAttesterSlashings = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "num_pending_attester_slashings",
Expand All @@ -24,6 +30,12 @@ var (
Help: "Times an attester slashing for an already slashed validator is received",
},
)
numPendingProposerSlashingFailedSigVerify = promauto.NewCounter(
prometheus.CounterOpts{
Name: "pending_proposer_slashing_fail_sig_verify_total",
Help: "Times an pending proposer slashing fails sig verification",
},
)
numPendingProposerSlashings = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "num_pending_proposer_slashings",
Expand Down
29 changes: 25 additions & 4 deletions beacon-chain/operations/slashings/service.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package slashings

import (
"context"
"errors"
"fmt"
"sort"

ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"go.opencensus.io/trace"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove space

rauljordan marked this conversation as resolved.
Show resolved Hide resolved
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/shared/params"
Expand All @@ -23,9 +27,11 @@ func NewPool() *Pool {

// PendingAttesterSlashings returns attester slashings that are able to be included into a block.
// This method will not return more than the block enforced MaxAttesterSlashings.
func (p *Pool) PendingAttesterSlashings() []*ethpb.AttesterSlashing {
func (p *Pool) PendingAttesterSlashings(ctx context.Context, st *beaconstate.BeaconState) []*ethpb.AttesterSlashing {
p.lock.RLock()
defer p.lock.RUnlock()
ctx, span := trace.StartSpan(ctx, "operations.PendingAttesterSlashing")
defer span.End()

// Update prom metric.
numPendingAttesterSlashings.Set(float64(len(p.pendingAttesterSlashing)))
Expand All @@ -44,17 +50,26 @@ func (p *Pool) PendingAttesterSlashings() []*ethpb.AttesterSlashing {
for _, idx := range slashedVal {
included[idx] = true
}
pending = append(pending, attSlashing)

if err := blocks.VerifyAttesterSlashing(ctx, st, attSlashing); err == nil {
pending = append(pending, attSlashing)
} else {
numPendingAttesterSlashingFailedSigVerify.Inc()
// Else, we clear the attester slashing from the pool.
p.pendingAttesterSlashing = append(p.pendingAttesterSlashing[:i], p.pendingAttesterSlashing[i+1:]...)
}
}

return pending
}

// PendingProposerSlashings returns proposer slashings that are able to be included into a block.
// This method will not return more than the block enforced MaxProposerSlashings.
func (p *Pool) PendingProposerSlashings() []*ethpb.ProposerSlashing {
func (p *Pool) PendingProposerSlashings(ctx context.Context, st *beaconstate.BeaconState) []*ethpb.ProposerSlashing {
p.lock.RLock()
defer p.lock.RUnlock()
ctx, span := trace.StartSpan(ctx, "operations.PendingProposerSlashing")
defer span.End()

// Update prom metric.
numPendingProposerSlashings.Set(float64(len(p.pendingProposerSlashing)))
Expand All @@ -64,7 +79,13 @@ func (p *Pool) PendingProposerSlashings() []*ethpb.ProposerSlashing {
if i >= int(params.BeaconConfig().MaxProposerSlashings) {
break
}
pending = append(pending, slashing)
if err := blocks.VerifyProposerSlashing(st, slashing); err == nil {
pending = append(pending, slashing)
} else {
numPendingProposerSlashingFailedSigVerify.Inc()
// Else, we clear the proposer slashing from the pool.
p.pendingProposerSlashing = append(p.pendingProposerSlashing[:i], p.pendingProposerSlashing[i+1:]...)
}
}
return pending
}
Expand Down
163 changes: 88 additions & 75 deletions beacon-chain/operations/slashings/service_attester_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package slashings

import (
"context"
"reflect"
"strings"
"testing"
Expand All @@ -10,6 +11,7 @@ import (
beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state"
p2ppb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
)

func attesterSlashingForValIdx(valIdx ...uint64) *ethpb.AttesterSlashing {
Expand Down Expand Up @@ -450,6 +452,20 @@ func TestPool_PendingAttesterSlashings(t *testing.T) {
type fields struct {
pending []*PendingAttesterSlashing
}
beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
pendingSlashings := make([]*PendingAttesterSlashing, 20)
slashings := make([]*ethpb.AttesterSlashing, 20)
for i := 0; i < len(pendingSlashings); i++ {
sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], uint64(i))
if err != nil {
t.Fatal(err)
}
pendingSlashings[i] = &PendingAttesterSlashing{
attesterSlashing: sl,
validatorToSlash: uint64(i),
}
slashings[i] = sl
}
tests := []struct {
name string
fields fields
Expand All @@ -465,105 +481,102 @@ func TestPool_PendingAttesterSlashings(t *testing.T) {
{
name: "All eligible",
fields: fields{
pending: generateNPendingSlashings(1),
pending: pendingSlashings,
},
want: generateNAttSlashings(1),
want: slashings[0:1],
},
{
name: "Multiple indices",
fields: fields{
pending: []*PendingAttesterSlashing{
pendingSlashingForValIdx(1, 5, 8),
},
},
want: []*ethpb.AttesterSlashing{
attesterSlashingForValIdx(1, 5, 8),
pending: pendingSlashings[3:6],
},
},
{
name: "All eligible, over max",
fields: fields{
pending: generateNPendingSlashings(6),
},
want: generateNAttSlashings(1),
},
{
name: "No duplicate slashings for grouped",
fields: fields{
pending: generateNPendingSlashings(16),
},
want: generateNAttSlashings(1),
want: slashings[3:4],
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Pool{
pendingAttesterSlashing: tt.fields.pending,
}
if got := p.PendingAttesterSlashings(); !reflect.DeepEqual(tt.want, got) {
if got := p.PendingAttesterSlashings(
context.Background(),
beaconState,
); !reflect.DeepEqual(tt.want, got) {
t.Errorf("Unexpected return from PendingAttesterSlashings, wanted %v, received %v", tt.want, got)
}
})
}
}

func TestPool_PendingAttesterSlashings_2Max(t *testing.T) {
func TestPool_PendingAttesterSlashings_NoDuplicates(t *testing.T) {
conf := params.BeaconConfig()
conf.MaxAttesterSlashings = 2
params.OverrideBeaconConfig(conf)
beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
pendingSlashings := make([]*PendingAttesterSlashing, 3)
slashings := make([]*ethpb.AttesterSlashing, 3)
for i := 0; i < 2; i++ {
sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], uint64(i))
if err != nil {
t.Fatal(err)
}
pendingSlashings[i] = &PendingAttesterSlashing{
attesterSlashing: sl,
validatorToSlash: uint64(i),
}
slashings[i] = sl
}
// We duplicate the last slashing.
pendingSlashings[2] = pendingSlashings[1]
slashings[2] = slashings[1]
p := &Pool{
pendingAttesterSlashing: pendingSlashings,
}
want := slashings[0:2]
if got := p.PendingAttesterSlashings(
context.Background(),
beaconState,
); !reflect.DeepEqual(want, got) {
t.Errorf("Unexpected return from PendingAttesterSlashings, wanted %v, received %v", want, got)
}
}

type fields struct {
pending []*PendingAttesterSlashing
func TestPool_PendingAttesterSlashings_SigFailsVerify_ClearPool(t *testing.T) {
conf := params.BeaconConfig()
conf.MaxAttesterSlashings = 2
params.OverrideBeaconConfig(conf)
beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
pendingSlashings := make([]*PendingAttesterSlashing, 2)
slashings := make([]*ethpb.AttesterSlashing, 2)
for i := 0; i < 2; i++ {
sl, err := testutil.GenerateAttesterSlashingForValidator(beaconState, privKeys[i], uint64(i))
if err != nil {
t.Fatal(err)
}
pendingSlashings[i] = &PendingAttesterSlashing{
attesterSlashing: sl,
validatorToSlash: uint64(i),
}
slashings[i] = sl
}
tests := []struct {
name string
fields fields
want []*ethpb.AttesterSlashing
}{
{
name: "No duplicates with grouped att slashings",
fields: fields{
pending: []*PendingAttesterSlashing{
{
attesterSlashing: attesterSlashingForValIdx(4, 12, 40),
validatorToSlash: 4,
},
{
attesterSlashing: attesterSlashingForValIdx(6, 8, 24),
validatorToSlash: 6,
},
{
attesterSlashing: attesterSlashingForValIdx(6, 8, 24),
validatorToSlash: 8,
},
{
attesterSlashing: attesterSlashingForValIdx(4, 12, 40),
validatorToSlash: 12,
},
{
attesterSlashing: attesterSlashingForValIdx(6, 8, 24),
validatorToSlash: 24,
},
{
attesterSlashing: attesterSlashingForValIdx(4, 12, 40),
validatorToSlash: 40,
},
},
},
want: []*ethpb.AttesterSlashing{
attesterSlashingForValIdx(4, 12, 40),
attesterSlashingForValIdx(6, 8, 24),
},
},
// We mess up the signature of the second slashing.
badSig := make([]byte, 96)
copy(badSig, "muahaha")
pendingSlashings[1].attesterSlashing.Attestation_1.Signature = badSig
slashings[1].Attestation_1.Signature = badSig
p := &Pool{
pendingAttesterSlashing: pendingSlashings,
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Pool{
pendingAttesterSlashing: tt.fields.pending,
}
if got := p.PendingAttesterSlashings(); !reflect.DeepEqual(tt.want, got) {
t.Errorf("Unexpected return from PendingAttesterSlashings, wanted %v, received %v", tt.want, got)
}
})
// We only want a single attester slashing to remain.
want := slashings[0:1]
if got := p.PendingAttesterSlashings(
context.Background(),
beaconState,
); !reflect.DeepEqual(want, got) {
t.Errorf("Unexpected return from PendingAttesterSlashings, wanted %v, received %v", want, got)
}
// We expect to only have 1 pending attester slashing in the pool.
if len(p.pendingAttesterSlashing) != 1 {
t.Error("Expected failed attester slashing to have been cleared from pool")
}
}