package rpc import ( "context" "testing" "github.com/golang/mock/gomock" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/bytesutil" "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" testDB "github.com/prysmaticlabs/prysm/slasher/db/testing" "github.com/prysmaticlabs/prysm/slasher/detection" ) func TestServer_IsSlashableAttestation(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) } 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) wantedGenesis := ðpb.Genesis{ GenesisValidatorsRoot: []byte("I am genesis"), } nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil) savedAttestation := ðpb.IndexedAttestation{ AttestingIndices: []uint64{3}, Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 3}, Target: ðpb.Checkpoint{Epoch: 4}, }, } incomingAtt := ðpb.IndexedAttestation{ AttestingIndices: []uint64{1, 3}, Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 2}, Target: ðpb.Checkpoint{Epoch: 4}, }, } cfg := &detection.Config{ SlasherDB: db, } 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 domain, err = helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot) 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) ds := detection.NewDetectionService(ctx, cfg) server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs} 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) } bClient.EXPECT().ListValidators( gomock.Any(), gomock.Any(), ).Return(wantedValidators2, nil) 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)) } } 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)) } }