2020-03-26 18:31:20 +00:00
|
|
|
package rpc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
|
|
|
|
2020-05-05 05:15:32 +00:00
|
|
|
"github.com/golang/mock/gomock"
|
2020-03-26 18:31:20 +00:00
|
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
2020-05-05 05:15:32 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2020-05-08 05:51:56 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil"
|
2020-05-05 05:15:32 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
2020-05-08 05:51:56 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2020-05-05 05:15:32 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/mock"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/p2putils"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
|
|
|
"github.com/prysmaticlabs/prysm/slasher/beaconclient"
|
2020-03-26 18:31:20 +00:00
|
|
|
testDB "github.com/prysmaticlabs/prysm/slasher/db/testing"
|
|
|
|
"github.com/prysmaticlabs/prysm/slasher/detection"
|
|
|
|
)
|
|
|
|
|
2020-05-08 05:51:56 +00:00
|
|
|
func TestServer_IsSlashableAttestation(t *testing.T) {
|
2020-03-26 18:31:20 +00:00
|
|
|
db := testDB.SetupSlasherDB(t, false)
|
2020-05-05 05:15:32 +00:00
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
bClient := mock.NewMockBeaconChainClient(ctrl)
|
|
|
|
nClient := mock.NewMockNodeClient(ctrl)
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
_, keys, err := testutil.DeterministicDepositsAndKeys(4)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
wantedValidators1 := ðpb.Validators{
|
|
|
|
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
|
|
|
{
|
|
|
|
Index: 3, Validator: ðpb.Validator{PublicKey: keys[3].PublicKey().Marshal()},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
wantedValidators2 := ðpb.Validators{
|
|
|
|
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
|
|
|
{
|
|
|
|
Index: 3, Validator: ðpb.Validator{PublicKey: keys[3].PublicKey().Marshal()},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Index: 1, Validator: ðpb.Validator{PublicKey: keys[1].PublicKey().Marshal()},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
bClient.EXPECT().ListValidators(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(wantedValidators1, nil)
|
2020-03-26 18:31:20 +00:00
|
|
|
|
2020-05-05 05:15:32 +00:00
|
|
|
wantedGenesis := ðpb.Genesis{
|
|
|
|
GenesisValidatorsRoot: []byte("I am genesis"),
|
|
|
|
}
|
|
|
|
nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil)
|
2020-03-26 18:31:20 +00:00
|
|
|
savedAttestation := ðpb.IndexedAttestation{
|
|
|
|
AttestingIndices: []uint64{3},
|
|
|
|
Data: ðpb.AttestationData{
|
|
|
|
Source: ðpb.Checkpoint{Epoch: 3},
|
|
|
|
Target: ðpb.Checkpoint{Epoch: 4},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
incomingAtt := ðpb.IndexedAttestation{
|
2020-05-05 05:15:32 +00:00
|
|
|
AttestingIndices: []uint64{1, 3},
|
2020-03-26 18:31:20 +00:00
|
|
|
Data: ðpb.AttestationData{
|
|
|
|
Source: ðpb.Checkpoint{Epoch: 2},
|
|
|
|
Target: ðpb.Checkpoint{Epoch: 4},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cfg := &detection.Config{
|
|
|
|
SlasherDB: db,
|
|
|
|
}
|
2020-05-05 05:15:32 +00:00
|
|
|
fork, err := p2putils.Fork(incomingAtt.Data.Target.Epoch)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
domain, err := helpers.Domain(fork, incomingAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
root, err := helpers.ComputeSigningRoot(incomingAtt.Data, domain)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
var sig []*bls.Signature
|
|
|
|
for _, idx := range incomingAtt.AttestingIndices {
|
|
|
|
validatorSig := keys[idx].Sign(root[:])
|
|
|
|
sig = append(sig, validatorSig)
|
|
|
|
}
|
|
|
|
aggSig := bls.AggregateSignatures(sig)
|
|
|
|
marshalledSig := aggSig.Marshal()
|
|
|
|
|
|
|
|
incomingAtt.Signature = marshalledSig
|
|
|
|
|
2020-05-08 05:51:56 +00:00
|
|
|
domain, err = helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot)
|
2020-05-05 05:15:32 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
root, err = helpers.ComputeSigningRoot(savedAttestation.Data, domain)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
sig = []*bls.Signature{}
|
|
|
|
for _, idx := range savedAttestation.AttestingIndices {
|
|
|
|
validatorSig := keys[idx].Sign(root[:])
|
|
|
|
sig = append(sig, validatorSig)
|
|
|
|
}
|
|
|
|
aggSig = bls.AggregateSignatures(sig)
|
|
|
|
marshalledSig = aggSig.Marshal()
|
|
|
|
|
|
|
|
savedAttestation.Signature = marshalledSig
|
|
|
|
|
|
|
|
bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
|
|
|
|
bs, err := beaconclient.NewBeaconClientService(ctx, bcCfg)
|
2020-03-26 18:31:20 +00:00
|
|
|
ds := detection.NewDetectionService(ctx, cfg)
|
2020-05-05 05:15:32 +00:00
|
|
|
server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
|
2020-03-26 18:31:20 +00:00
|
|
|
slashings, err := server.IsSlashableAttestation(ctx, savedAttestation)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("got error while trying to detect slashing: %v", err)
|
|
|
|
}
|
|
|
|
if len(slashings.AttesterSlashing) != 0 {
|
|
|
|
t.Fatalf("Found slashings while no slashing should have been found on first attestation: %v slashing found: %v", savedAttestation, slashings)
|
|
|
|
}
|
2020-05-05 05:15:32 +00:00
|
|
|
bClient.EXPECT().ListValidators(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(wantedValidators2, nil)
|
2020-03-26 18:31:20 +00:00
|
|
|
slashing, err := server.IsSlashableAttestation(ctx, incomingAtt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("got error while trying to detect slashing: %v", err)
|
|
|
|
}
|
|
|
|
if len(slashing.AttesterSlashing) != 1 {
|
|
|
|
t.Fatalf("only one slashing should have been found. got: %v", len(slashing.AttesterSlashing))
|
|
|
|
}
|
|
|
|
}
|
2020-05-08 05:51:56 +00:00
|
|
|
|
|
|
|
func TestServer_IsSlashableBlock(t *testing.T) {
|
|
|
|
db := testDB.SetupSlasherDB(t, false)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
bClient := mock.NewMockBeaconChainClient(ctrl)
|
|
|
|
nClient := mock.NewMockNodeClient(ctrl)
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
_, keys, err := testutil.DeterministicDepositsAndKeys(4)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
wantedValidators := ðpb.Validators{
|
|
|
|
ValidatorList: []*ethpb.Validators_ValidatorContainer{
|
|
|
|
{
|
|
|
|
Index: 1, Validator: ðpb.Validator{PublicKey: keys[1].PublicKey().Marshal()},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
bClient.EXPECT().ListValidators(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(wantedValidators, nil)
|
|
|
|
|
|
|
|
wantedGenesis := ðpb.Genesis{
|
|
|
|
GenesisValidatorsRoot: []byte("I am genesis"),
|
|
|
|
}
|
|
|
|
nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil)
|
|
|
|
savedBlock := ðpb.SignedBeaconBlockHeader{
|
|
|
|
Header: ðpb.BeaconBlockHeader{
|
|
|
|
Slot: 1,
|
|
|
|
ProposerIndex: 1,
|
|
|
|
BodyRoot: bytesutil.PadTo([]byte("body root"), 32),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
incomingBlock := ðpb.SignedBeaconBlockHeader{
|
|
|
|
Header: ðpb.BeaconBlockHeader{
|
|
|
|
Slot: 1,
|
|
|
|
ProposerIndex: 1,
|
|
|
|
BodyRoot: bytesutil.PadTo([]byte("body root2"), 32),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cfg := &detection.Config{
|
|
|
|
SlasherDB: db,
|
|
|
|
}
|
|
|
|
incomingBlockEpoch := helpers.SlotToEpoch(incomingBlock.Header.Slot)
|
|
|
|
fork, err := p2putils.Fork(incomingBlockEpoch)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
domain, err := helpers.Domain(fork, incomingBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
bhr, err := stateutil.BlockHeaderRoot(incomingBlock.Header)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
root, err := helpers.ComputeSigningRoot(bhr, domain)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
blockSig := keys[incomingBlock.Header.ProposerIndex].Sign(root[:])
|
|
|
|
marshalledSig := blockSig.Marshal()
|
|
|
|
incomingBlock.Signature = marshalledSig
|
|
|
|
|
|
|
|
savedBlockEpoch := helpers.SlotToEpoch(savedBlock.Header.Slot)
|
|
|
|
domain, err = helpers.Domain(fork, savedBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
bhr, err = stateutil.BlockHeaderRoot(savedBlock.Header)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
root, err = helpers.ComputeSigningRoot(bhr, domain)
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
blockSig = keys[savedBlock.Header.ProposerIndex].Sign(root[:])
|
|
|
|
marshalledSig = blockSig.Marshal()
|
|
|
|
savedBlock.Signature = marshalledSig
|
|
|
|
bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
|
|
|
|
bs, err := beaconclient.NewBeaconClientService(ctx, bcCfg)
|
|
|
|
ds := detection.NewDetectionService(ctx, cfg)
|
|
|
|
server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
|
|
|
|
slashings, err := server.IsSlashableBlock(ctx, savedBlock)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("got error while trying to detect slashing: %v", err)
|
|
|
|
}
|
|
|
|
if len(slashings.ProposerSlashing) != 0 {
|
|
|
|
t.Fatalf("Found slashings while no slashing should have been found on first block: %v slashing found: %v", savedBlock, slashings)
|
|
|
|
}
|
|
|
|
slashing, err := server.IsSlashableBlock(ctx, incomingBlock)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("got error while trying to detect slashing: %v", err)
|
|
|
|
}
|
|
|
|
if len(slashing.ProposerSlashing) != 1 {
|
|
|
|
t.Fatalf("only one slashing should have been found. got: %v", len(slashing.ProposerSlashing))
|
|
|
|
}
|
|
|
|
}
|