github.com/prysmaticlabs/prysm@v1.4.4/endtoend/evaluators/slashing.go (about)

     1  package evaluators
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/go-bitfield"
    10  	corehelpers "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    11  	"github.com/prysmaticlabs/prysm/endtoend/policies"
    12  	e2eTypes "github.com/prysmaticlabs/prysm/endtoend/types"
    13  	eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    15  	"github.com/prysmaticlabs/prysm/shared/params"
    16  	"github.com/prysmaticlabs/prysm/shared/sliceutil"
    17  	"github.com/prysmaticlabs/prysm/shared/testutil"
    18  	"google.golang.org/grpc"
    19  	"google.golang.org/protobuf/types/known/emptypb"
    20  )
    21  
    22  // InjectDoubleVote broadcasts a double vote into the beacon node pool for the slasher to detect.
    23  var InjectDoubleVote = e2eTypes.Evaluator{
    24  	Name:       "inject_double_vote_%d",
    25  	Policy:     policies.OnEpoch(1),
    26  	Evaluation: insertDoubleAttestationIntoPool,
    27  }
    28  
    29  // ProposeDoubleBlock broadcasts a double block to the beacon node for the slasher to detect.
    30  var ProposeDoubleBlock = e2eTypes.Evaluator{
    31  	Name:       "propose_double_block_%d",
    32  	Policy:     policies.OnEpoch(1),
    33  	Evaluation: proposeDoubleBlock,
    34  }
    35  
    36  // ValidatorsSlashed ensures the expected amount of validators are slashed.
    37  var ValidatorsSlashed = e2eTypes.Evaluator{
    38  	Name:       "validators_slashed_epoch_%d",
    39  	Policy:     policies.AfterNthEpoch(1),
    40  	Evaluation: validatorsSlashed,
    41  }
    42  
    43  // SlashedValidatorsLoseBalance checks if the validators slashed lose the right balance.
    44  var SlashedValidatorsLoseBalance = e2eTypes.Evaluator{
    45  	Name:       "slashed_validators_lose_valance_epoch_%d",
    46  	Policy:     policies.AfterNthEpoch(1),
    47  	Evaluation: validatorsLoseBalance,
    48  }
    49  
    50  var slashedIndices []uint64
    51  
    52  func validatorsSlashed(conns ...*grpc.ClientConn) error {
    53  	conn := conns[0]
    54  	ctx := context.Background()
    55  	client := eth.NewBeaconChainClient(conn)
    56  	req := &eth.GetValidatorActiveSetChangesRequest{}
    57  	changes, err := client.GetValidatorActiveSetChanges(ctx, req)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	if len(changes.SlashedIndices) != len(slashedIndices) {
    62  		return fmt.Errorf("expected %d indices to be slashed, received %d", len(slashedIndices), len(changes.SlashedIndices))
    63  	}
    64  	return nil
    65  }
    66  
    67  func validatorsLoseBalance(conns ...*grpc.ClientConn) error {
    68  	conn := conns[0]
    69  	ctx := context.Background()
    70  	client := eth.NewBeaconChainClient(conn)
    71  
    72  	for i, slashedIndex := range slashedIndices {
    73  		req := &eth.GetValidatorRequest{
    74  			QueryFilter: &eth.GetValidatorRequest_Index{
    75  				Index: types.ValidatorIndex(slashedIndex),
    76  			},
    77  		}
    78  		valResp, err := client.GetValidator(ctx, req)
    79  		if err != nil {
    80  			return err
    81  		}
    82  
    83  		slashedPenalty := params.BeaconConfig().MaxEffectiveBalance / params.BeaconConfig().MinSlashingPenaltyQuotient
    84  		slashedBal := params.BeaconConfig().MaxEffectiveBalance - slashedPenalty + params.BeaconConfig().EffectiveBalanceIncrement/10
    85  		if valResp.EffectiveBalance >= slashedBal {
    86  			return fmt.Errorf(
    87  				"expected slashed validator %d to balance less than %d, received %d",
    88  				i,
    89  				slashedBal,
    90  				valResp.EffectiveBalance,
    91  			)
    92  		}
    93  
    94  	}
    95  	return nil
    96  }
    97  
    98  func insertDoubleAttestationIntoPool(conns ...*grpc.ClientConn) error {
    99  	conn := conns[0]
   100  	valClient := eth.NewBeaconNodeValidatorClient(conn)
   101  	beaconClient := eth.NewBeaconChainClient(conn)
   102  
   103  	ctx := context.Background()
   104  	chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{})
   105  	if err != nil {
   106  		return errors.Wrap(err, "could not get chain head")
   107  	}
   108  
   109  	_, privKeys, err := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	pubKeys := make([][]byte, len(privKeys))
   114  	for i, priv := range privKeys {
   115  		pubKeys[i] = priv.PublicKey().Marshal()
   116  	}
   117  	duties, err := valClient.GetDuties(ctx, &eth.DutiesRequest{
   118  		Epoch:      chainHead.HeadEpoch,
   119  		PublicKeys: pubKeys,
   120  	})
   121  	if err != nil {
   122  		return errors.Wrap(err, "could not get duties")
   123  	}
   124  
   125  	var committeeIndex types.CommitteeIndex
   126  	var committee []types.ValidatorIndex
   127  	for _, duty := range duties.Duties {
   128  		if duty.AttesterSlot == chainHead.HeadSlot-1 {
   129  			committeeIndex = duty.CommitteeIndex
   130  			committee = duty.Committee
   131  			break
   132  		}
   133  	}
   134  
   135  	attDataReq := &eth.AttestationDataRequest{
   136  		CommitteeIndex: committeeIndex,
   137  		Slot:           chainHead.HeadSlot - 1,
   138  	}
   139  
   140  	attData, err := valClient.GetAttestationData(ctx, attDataReq)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	blockRoot := bytesutil.ToBytes32([]byte("muahahahaha I'm an evil validator"))
   145  	attData.BeaconBlockRoot = blockRoot[:]
   146  
   147  	req := &eth.DomainRequest{
   148  		Epoch:  chainHead.HeadEpoch,
   149  		Domain: params.BeaconConfig().DomainBeaconAttester[:],
   150  	}
   151  	resp, err := valClient.DomainData(ctx, req)
   152  	if err != nil {
   153  		return errors.Wrap(err, "could not get domain data")
   154  	}
   155  	signingRoot, err := corehelpers.ComputeSigningRoot(attData, resp.SignatureDomain)
   156  	if err != nil {
   157  		return errors.Wrap(err, "could not compute signing root")
   158  	}
   159  
   160  	valsToSlash := uint64(2)
   161  	for i := uint64(0); i < valsToSlash && i < uint64(len(committee)); i++ {
   162  		if len(sliceutil.IntersectionUint64(slashedIndices, []uint64{uint64(committee[i])})) > 0 {
   163  			valsToSlash++
   164  			continue
   165  		}
   166  		// Set the bits of half the committee to be slashed.
   167  		attBitfield := bitfield.NewBitlist(uint64(len(committee)))
   168  		attBitfield.SetBitAt(i, true)
   169  
   170  		att := &eth.Attestation{
   171  			AggregationBits: attBitfield,
   172  			Data:            attData,
   173  			Signature:       privKeys[committee[i]].Sign(signingRoot[:]).Marshal(),
   174  		}
   175  		// We only broadcast to conns[0] here since we can trust that at least 1 node will be online.
   176  		// Only broadcasting the attestation to one node also helps test slashing propagation.
   177  		client := eth.NewBeaconNodeValidatorClient(conns[0])
   178  		if _, err = client.ProposeAttestation(ctx, att); err != nil {
   179  			return errors.Wrap(err, "could not propose attestation")
   180  		}
   181  		slashedIndices = append(slashedIndices, uint64(committee[i]))
   182  	}
   183  	return nil
   184  }
   185  
   186  func proposeDoubleBlock(conns ...*grpc.ClientConn) error {
   187  	conn := conns[0]
   188  	valClient := eth.NewBeaconNodeValidatorClient(conn)
   189  	beaconClient := eth.NewBeaconChainClient(conn)
   190  
   191  	ctx := context.Background()
   192  	chainHead, err := beaconClient.GetChainHead(ctx, &emptypb.Empty{})
   193  	if err != nil {
   194  		return errors.Wrap(err, "could not get chain head")
   195  	}
   196  
   197  	_, privKeys, err := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	pubKeys := make([][]byte, len(privKeys))
   202  	for i, priv := range privKeys {
   203  		pubKeys[i] = priv.PublicKey().Marshal()
   204  	}
   205  	duties, err := valClient.GetDuties(ctx, &eth.DutiesRequest{
   206  		Epoch:      chainHead.HeadEpoch,
   207  		PublicKeys: pubKeys,
   208  	})
   209  	if err != nil {
   210  		return errors.Wrap(err, "could not get duties")
   211  	}
   212  
   213  	var proposerIndex types.ValidatorIndex
   214  	for i, duty := range duties.CurrentEpochDuties {
   215  		if sliceutil.IsInSlots(chainHead.HeadSlot-1, duty.ProposerSlots) {
   216  			proposerIndex = types.ValidatorIndex(i)
   217  			break
   218  		}
   219  	}
   220  
   221  	hashLen := 32
   222  	blk := &eth.BeaconBlock{
   223  		Slot:          chainHead.HeadSlot - 1,
   224  		ParentRoot:    bytesutil.PadTo([]byte("bad parent root"), hashLen),
   225  		StateRoot:     bytesutil.PadTo([]byte("bad state root"), hashLen),
   226  		ProposerIndex: proposerIndex,
   227  		Body: &eth.BeaconBlockBody{
   228  			Eth1Data: &eth.Eth1Data{
   229  				BlockHash:    bytesutil.PadTo([]byte("bad block hash"), hashLen),
   230  				DepositRoot:  bytesutil.PadTo([]byte("bad deposit root"), hashLen),
   231  				DepositCount: 1,
   232  			},
   233  			RandaoReveal:      bytesutil.PadTo([]byte("bad randao"), params.BeaconConfig().BLSSignatureLength),
   234  			Graffiti:          bytesutil.PadTo([]byte("teehee"), hashLen),
   235  			ProposerSlashings: []*eth.ProposerSlashing{},
   236  			AttesterSlashings: []*eth.AttesterSlashing{},
   237  			Attestations:      []*eth.Attestation{},
   238  			Deposits:          []*eth.Deposit{},
   239  			VoluntaryExits:    []*eth.SignedVoluntaryExit{},
   240  		},
   241  	}
   242  
   243  	req := &eth.DomainRequest{
   244  		Epoch:  chainHead.HeadEpoch,
   245  		Domain: params.BeaconConfig().DomainBeaconProposer[:],
   246  	}
   247  	resp, err := valClient.DomainData(ctx, req)
   248  	if err != nil {
   249  		return errors.Wrap(err, "could not get domain data")
   250  	}
   251  	signingRoot, err := corehelpers.ComputeSigningRoot(blk, resp.SignatureDomain)
   252  	if err != nil {
   253  		return errors.Wrap(err, "could not compute signing root")
   254  	}
   255  	sig := privKeys[proposerIndex].Sign(signingRoot[:]).Marshal()
   256  	signedBlk := &eth.SignedBeaconBlock{
   257  		Block:     blk,
   258  		Signature: sig,
   259  	}
   260  
   261  	// We only broadcast to conns[0] here since we can trust that at least 1 node will be online.
   262  	// Only broadcasting the attestation to one node also helps test slashing propagation.
   263  	client := eth.NewBeaconNodeValidatorClient(conns[0])
   264  	if _, err = client.ProposeBlock(ctx, signedBlk); err == nil {
   265  		return errors.New("expected block to fail processing")
   266  	}
   267  	slashedIndices = append(slashedIndices, uint64(proposerIndex))
   268  	return nil
   269  }