github.com/prysmaticlabs/prysm@v1.4.4/slasher/rpc/server_test.go (about)

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
    13  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/shared/bls"
    15  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    16  	"github.com/prysmaticlabs/prysm/shared/copyutil"
    17  	"github.com/prysmaticlabs/prysm/shared/mock"
    18  	"github.com/prysmaticlabs/prysm/shared/p2putils"
    19  	"github.com/prysmaticlabs/prysm/shared/params"
    20  	"github.com/prysmaticlabs/prysm/shared/testutil"
    21  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    22  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    23  	"github.com/prysmaticlabs/prysm/slasher/beaconclient"
    24  	testDB "github.com/prysmaticlabs/prysm/slasher/db/testing"
    25  	"github.com/prysmaticlabs/prysm/slasher/detection"
    26  )
    27  
    28  func TestServer_IsSlashableAttestation(t *testing.T) {
    29  	db := testDB.SetupSlasherDB(t, false)
    30  	ctrl := gomock.NewController(t)
    31  	defer ctrl.Finish()
    32  	bClient := mock.NewMockBeaconChainClient(ctrl)
    33  	nClient := mock.NewMockNodeClient(ctrl)
    34  	ctx := context.Background()
    35  
    36  	_, keys, err := testutil.DeterministicDepositsAndKeys(4)
    37  	require.NoError(t, err)
    38  	wantedValidators1 := &ethpb.Validators{
    39  		ValidatorList: []*ethpb.Validators_ValidatorContainer{
    40  			{
    41  				Index: 3, Validator: &ethpb.Validator{PublicKey: keys[3].PublicKey().Marshal()},
    42  			},
    43  		},
    44  	}
    45  
    46  	wantedGenesis := &ethpb.Genesis{
    47  		GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32),
    48  	}
    49  
    50  	savedAttestation := &ethpb.IndexedAttestation{
    51  		AttestingIndices: []uint64{3},
    52  		Data: &ethpb.AttestationData{
    53  			Source:          &ethpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)},
    54  			Target:          &ethpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)},
    55  			BeaconBlockRoot: make([]byte, 32),
    56  		},
    57  		Signature: make([]byte, 96),
    58  	}
    59  
    60  	cfg := &detection.Config{
    61  		SlasherDB: db,
    62  	}
    63  	fork, err := p2putils.Fork(savedAttestation.Data.Target.Epoch)
    64  	require.NoError(t, err)
    65  
    66  	bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
    67  	bs, err := beaconclient.NewService(ctx, bcCfg)
    68  	require.NoError(t, err)
    69  	ds := detection.NewService(ctx, cfg)
    70  	server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
    71  	nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil).AnyTimes()
    72  	bClient.EXPECT().ListValidators(
    73  		gomock.Any(),
    74  		gomock.Any(),
    75  	).Return(wantedValidators1, nil).AnyTimes()
    76  	domain, err := helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot)
    77  	require.NoError(t, err)
    78  	wg := sync.WaitGroup{}
    79  	wg.Add(100)
    80  	var wentThrough bool
    81  	for i := types.Slot(0); i < 100; i++ {
    82  		go func(j types.Slot) {
    83  			defer wg.Done()
    84  			iatt := copyutil.CopyIndexedAttestation(savedAttestation)
    85  			iatt.Data.Slot += j
    86  			root, err := helpers.ComputeSigningRoot(iatt.Data, domain)
    87  			require.NoError(t, err)
    88  			validatorSig := keys[iatt.AttestingIndices[0]].Sign(root[:])
    89  			marshalledSig := validatorSig.Marshal()
    90  			iatt.Signature = marshalledSig
    91  			slashings, err := server.IsSlashableAttestation(ctx, iatt)
    92  			require.NoError(t, err, "Got error while trying to detect slashing")
    93  
    94  			if len(slashings.AttesterSlashing) == 0 && !wentThrough {
    95  				wentThrough = true
    96  			} else if len(slashings.AttesterSlashing) == 0 && wentThrough {
    97  				t.Fatalf("Only one attestation should go through without slashing: %v", iatt)
    98  			}
    99  		}(i)
   100  	}
   101  	wg.Wait()
   102  
   103  }
   104  
   105  func TestServer_IsSlashableAttestationNoUpdate(t *testing.T) {
   106  	db := testDB.SetupSlasherDB(t, false)
   107  	ctrl := gomock.NewController(t)
   108  	defer ctrl.Finish()
   109  	bClient := mock.NewMockBeaconChainClient(ctrl)
   110  	nClient := mock.NewMockNodeClient(ctrl)
   111  	ctx := context.Background()
   112  
   113  	_, keys, err := testutil.DeterministicDepositsAndKeys(4)
   114  	require.NoError(t, err)
   115  	wantedValidators1 := &ethpb.Validators{
   116  		ValidatorList: []*ethpb.Validators_ValidatorContainer{
   117  			{
   118  				Index: 3, Validator: &ethpb.Validator{PublicKey: keys[3].PublicKey().Marshal()},
   119  			},
   120  		},
   121  	}
   122  	bClient.EXPECT().ListValidators(
   123  		gomock.Any(),
   124  		gomock.Any(),
   125  	).Return(wantedValidators1, nil)
   126  
   127  	wantedGenesis := &ethpb.Genesis{
   128  		GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32),
   129  	}
   130  	nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil)
   131  	savedAttestation := &ethpb.IndexedAttestation{
   132  		AttestingIndices: []uint64{3},
   133  		Data: &ethpb.AttestationData{
   134  			Source:          &ethpb.Checkpoint{Epoch: 3, Root: make([]byte, 32)},
   135  			Target:          &ethpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)},
   136  			BeaconBlockRoot: make([]byte, 32),
   137  		},
   138  		Signature: make([]byte, 96),
   139  	}
   140  	incomingAtt := &ethpb.IndexedAttestation{
   141  		AttestingIndices: []uint64{1, 3},
   142  		Data: &ethpb.AttestationData{
   143  			Source:          &ethpb.Checkpoint{Epoch: 2, Root: make([]byte, 32)},
   144  			Target:          &ethpb.Checkpoint{Epoch: 4, Root: make([]byte, 32)},
   145  			BeaconBlockRoot: make([]byte, 32),
   146  		},
   147  		Signature: make([]byte, 96),
   148  	}
   149  	cfg := &detection.Config{
   150  		SlasherDB: db,
   151  	}
   152  	fork, err := p2putils.Fork(savedAttestation.Data.Target.Epoch)
   153  	require.NoError(t, err)
   154  	domain, err := helpers.Domain(fork, savedAttestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, wantedGenesis.GenesisValidatorsRoot)
   155  	require.NoError(t, err)
   156  	root, err := helpers.ComputeSigningRoot(savedAttestation.Data, domain)
   157  	require.NoError(t, err)
   158  	var sig []bls.Signature
   159  	for _, idx := range savedAttestation.AttestingIndices {
   160  		validatorSig := keys[idx].Sign(root[:])
   161  		sig = append(sig, validatorSig)
   162  	}
   163  	aggSig := bls.AggregateSignatures(sig)
   164  	marshalledSig := aggSig.Marshal()
   165  
   166  	savedAttestation.Signature = marshalledSig
   167  
   168  	bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
   169  	bs, err := beaconclient.NewService(ctx, bcCfg)
   170  	require.NoError(t, err)
   171  	ds := detection.NewService(ctx, cfg)
   172  	server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
   173  	slashings, err := server.IsSlashableAttestation(ctx, savedAttestation)
   174  	require.NoError(t, err, "Got error while trying to detect slashing")
   175  	require.Equal(t, 0, len(slashings.AttesterSlashing), "Found slashings while no slashing should have been found on first attestation")
   176  	sl, err := server.IsSlashableAttestationNoUpdate(ctx, incomingAtt)
   177  	require.NoError(t, err, "Got error while trying to detect slashing")
   178  	require.Equal(t, true, sl.Slashable, "Attestation should be found to be slashable")
   179  }
   180  
   181  func TestServer_IsSlashableBlock(t *testing.T) {
   182  	db := testDB.SetupSlasherDB(t, false)
   183  	ctrl := gomock.NewController(t)
   184  	defer ctrl.Finish()
   185  	bClient := mock.NewMockBeaconChainClient(ctrl)
   186  	nClient := mock.NewMockNodeClient(ctrl)
   187  	ctx := context.Background()
   188  
   189  	_, keys, err := testutil.DeterministicDepositsAndKeys(4)
   190  	require.NoError(t, err)
   191  	wantedValidators := &ethpb.Validators{
   192  		ValidatorList: []*ethpb.Validators_ValidatorContainer{
   193  			{
   194  				Index: 1, Validator: &ethpb.Validator{PublicKey: keys[1].PublicKey().Marshal()},
   195  			},
   196  		},
   197  	}
   198  	bClient.EXPECT().ListValidators(
   199  		gomock.Any(),
   200  		gomock.Any(),
   201  	).Return(wantedValidators, nil).AnyTimes()
   202  
   203  	wantedGenesis := &ethpb.Genesis{
   204  		GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32),
   205  	}
   206  	nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil).AnyTimes()
   207  	savedBlock := testutil.HydrateSignedBeaconHeader(&ethpb.SignedBeaconBlockHeader{
   208  		Header: &ethpb.BeaconBlockHeader{
   209  			Slot:          1,
   210  			ProposerIndex: 1,
   211  			BodyRoot:      bytesutil.PadTo([]byte("body root"), 32),
   212  		},
   213  	})
   214  
   215  	cfg := &detection.Config{
   216  		SlasherDB: db,
   217  	}
   218  	savedBlockEpoch := helpers.SlotToEpoch(savedBlock.Header.Slot)
   219  	fork, err := p2putils.Fork(savedBlockEpoch)
   220  	require.NoError(t, err)
   221  	domain, err := helpers.Domain(fork, savedBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot)
   222  	require.NoError(t, err)
   223  
   224  	bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
   225  	bs, err := beaconclient.NewService(ctx, bcCfg)
   226  	require.NoError(t, err)
   227  	ds := detection.NewService(ctx, cfg)
   228  	server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
   229  
   230  	wg := sync.WaitGroup{}
   231  	wg.Add(100)
   232  	var wentThrough bool
   233  	for i := uint64(0); i < 100; i++ {
   234  		go func(j uint64) {
   235  			defer wg.Done()
   236  			sbbh := copyutil.CopySignedBeaconBlockHeader(savedBlock)
   237  			sbbh.Header.BodyRoot = bytesutil.PadTo([]byte(fmt.Sprintf("%d", j)), 32)
   238  			bhr, err := sbbh.Header.HashTreeRoot()
   239  			assert.NoError(t, err)
   240  			sszBytes := p2ptypes.SSZBytes(bhr[:])
   241  			root, err := helpers.ComputeSigningRoot(&sszBytes, domain)
   242  			assert.NoError(t, err)
   243  			sbbh.Signature = keys[sbbh.Header.ProposerIndex].Sign(root[:]).Marshal()
   244  			slashings, err := server.IsSlashableBlock(ctx, sbbh)
   245  			require.NoError(t, err, "Got error while trying to detect slashing")
   246  			if len(slashings.ProposerSlashing) == 0 && !wentThrough {
   247  				wentThrough = true
   248  			} else if len(slashings.ProposerSlashing) == 0 && wentThrough {
   249  				t.Fatalf("Only one block should go through without slashing: %v", sbbh)
   250  			}
   251  		}(i)
   252  	}
   253  	wg.Wait()
   254  }
   255  
   256  func TestServer_IsSlashableBlockNoUpdate(t *testing.T) {
   257  	db := testDB.SetupSlasherDB(t, false)
   258  	ctrl := gomock.NewController(t)
   259  	defer ctrl.Finish()
   260  	bClient := mock.NewMockBeaconChainClient(ctrl)
   261  	nClient := mock.NewMockNodeClient(ctrl)
   262  	ctx := context.Background()
   263  
   264  	_, keys, err := testutil.DeterministicDepositsAndKeys(4)
   265  	require.NoError(t, err)
   266  	wantedValidators := &ethpb.Validators{
   267  		ValidatorList: []*ethpb.Validators_ValidatorContainer{
   268  			{
   269  				Index: 1, Validator: &ethpb.Validator{PublicKey: keys[1].PublicKey().Marshal()},
   270  			},
   271  		},
   272  	}
   273  	bClient.EXPECT().ListValidators(
   274  		gomock.Any(),
   275  		gomock.Any(),
   276  	).Return(wantedValidators, nil)
   277  
   278  	wantedGenesis := &ethpb.Genesis{
   279  		GenesisValidatorsRoot: bytesutil.PadTo([]byte("I am genesis"), 32),
   280  	}
   281  	nClient.EXPECT().GetGenesis(gomock.Any(), gomock.Any()).Return(wantedGenesis, nil)
   282  	savedBlock := &ethpb.SignedBeaconBlockHeader{
   283  		Header: &ethpb.BeaconBlockHeader{
   284  			Slot:          1,
   285  			ProposerIndex: 1,
   286  			BodyRoot:      bytesutil.PadTo([]byte("body root"), 32),
   287  			StateRoot:     bytesutil.PadTo([]byte("state root"), 32),
   288  			ParentRoot:    bytesutil.PadTo([]byte("parent root"), 32),
   289  		},
   290  		Signature: make([]byte, 96),
   291  	}
   292  	incomingBlock := &ethpb.BeaconBlockHeader{
   293  		Slot:          1,
   294  		ProposerIndex: 1,
   295  		BodyRoot:      bytesutil.PadTo([]byte("body root2"), 32),
   296  		StateRoot:     bytesutil.PadTo([]byte("state root2"), 32),
   297  		ParentRoot:    bytesutil.PadTo([]byte("parent root2"), 32),
   298  	}
   299  	cfg := &detection.Config{
   300  		SlasherDB: db,
   301  	}
   302  	savedBlockEpoch := helpers.SlotToEpoch(savedBlock.Header.Slot)
   303  	fork, err := p2putils.Fork(savedBlockEpoch)
   304  	require.NoError(t, err)
   305  	domain, err := helpers.Domain(fork, savedBlockEpoch, params.BeaconConfig().DomainBeaconProposer, wantedGenesis.GenesisValidatorsRoot)
   306  	require.NoError(t, err)
   307  	bhr, err := savedBlock.Header.HashTreeRoot()
   308  	require.NoError(t, err)
   309  	sszBytes := p2ptypes.SSZBytes(bhr[:])
   310  	root, err := helpers.ComputeSigningRoot(&sszBytes, domain)
   311  	require.NoError(t, err)
   312  	blockSig := keys[savedBlock.Header.ProposerIndex].Sign(root[:])
   313  	marshalledSig := blockSig.Marshal()
   314  	savedBlock.Signature = marshalledSig
   315  	bcCfg := &beaconclient.Config{BeaconClient: bClient, NodeClient: nClient, SlasherDB: db}
   316  	bs, err := beaconclient.NewService(ctx, bcCfg)
   317  	require.NoError(t, err)
   318  	ds := detection.NewService(ctx, cfg)
   319  	server := Server{ctx: ctx, detector: ds, slasherDB: db, beaconClient: bs}
   320  	slashings, err := server.IsSlashableBlock(ctx, savedBlock)
   321  	require.NoError(t, err, "Got error while trying to detect slashing")
   322  	require.Equal(t, 0, len(slashings.ProposerSlashing), "Found slashings while no slashing should have been found on first block")
   323  	sl, err := server.IsSlashableBlockNoUpdate(ctx, incomingBlock)
   324  	require.NoError(t, err, "Got error while trying to detect slashing")
   325  	require.Equal(t, true, sl.Slashable, "Block should be found to be slashable")
   326  }