github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/blocks/attester_slashing.go (about)

     1  package blocks
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  
     7  	"github.com/pkg/errors"
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    10  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    11  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    12  	"github.com/prysmaticlabs/prysm/shared/attestationutil"
    13  	"github.com/prysmaticlabs/prysm/shared/slashutil"
    14  	"github.com/prysmaticlabs/prysm/shared/sliceutil"
    15  )
    16  
    17  // ProcessAttesterSlashings is one of the operations performed
    18  // on each processed beacon block to slash attesters based on
    19  // Casper FFG slashing conditions if any slashable events occurred.
    20  //
    21  // Spec pseudocode definition:
    22  //   def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
    23  //    attestation_1 = attester_slashing.attestation_1
    24  //    attestation_2 = attester_slashing.attestation_2
    25  //    assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
    26  //    assert is_valid_indexed_attestation(state, attestation_1)
    27  //    assert is_valid_indexed_attestation(state, attestation_2)
    28  //
    29  //    slashed_any = False
    30  //    indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
    31  //    for index in sorted(indices):
    32  //        if is_slashable_validator(state.validators[index], get_current_epoch(state)):
    33  //            slash_validator(state, index)
    34  //            slashed_any = True
    35  //    assert slashed_any
    36  func ProcessAttesterSlashings(
    37  	ctx context.Context,
    38  	beaconState iface.BeaconState,
    39  	slashings []*ethpb.AttesterSlashing,
    40  	slashFunc slashValidatorFunc,
    41  ) (iface.BeaconState, error) {
    42  	for idx, slashing := range slashings {
    43  		if err := VerifyAttesterSlashing(ctx, beaconState, slashing); err != nil {
    44  			return nil, errors.Wrapf(err, "could not verify attester slashing %d", idx)
    45  		}
    46  		slashableIndices := slashableAttesterIndices(slashing)
    47  		sort.SliceStable(slashableIndices, func(i, j int) bool {
    48  			return slashableIndices[i] < slashableIndices[j]
    49  		})
    50  		currentEpoch := helpers.SlotToEpoch(beaconState.Slot())
    51  		var err error
    52  		var slashedAny bool
    53  		var val iface.ReadOnlyValidator
    54  		for _, validatorIndex := range slashableIndices {
    55  			val, err = beaconState.ValidatorAtIndexReadOnly(types.ValidatorIndex(validatorIndex))
    56  			if err != nil {
    57  				return nil, err
    58  			}
    59  			if helpers.IsSlashableValidator(val.ActivationEpoch(), val.WithdrawableEpoch(), val.Slashed(), currentEpoch) {
    60  				beaconState, err = slashFunc(beaconState, types.ValidatorIndex(validatorIndex))
    61  				if err != nil {
    62  					return nil, errors.Wrapf(err, "could not slash validator index %d",
    63  						validatorIndex)
    64  				}
    65  				slashedAny = true
    66  			}
    67  		}
    68  		if !slashedAny {
    69  			return nil, errors.New("unable to slash any validator despite confirmed attester slashing")
    70  		}
    71  	}
    72  	return beaconState, nil
    73  }
    74  
    75  // VerifyAttesterSlashing validates the attestation data in both attestations in the slashing object.
    76  func VerifyAttesterSlashing(ctx context.Context, beaconState iface.ReadOnlyBeaconState, slashing *ethpb.AttesterSlashing) error {
    77  	if slashing == nil {
    78  		return errors.New("nil slashing")
    79  	}
    80  	if slashing.Attestation_1 == nil || slashing.Attestation_2 == nil {
    81  		return errors.New("nil attestation")
    82  	}
    83  	if slashing.Attestation_1.Data == nil || slashing.Attestation_2.Data == nil {
    84  		return errors.New("nil attestation data")
    85  	}
    86  	att1 := slashing.Attestation_1
    87  	att2 := slashing.Attestation_2
    88  	data1 := att1.Data
    89  	data2 := att2.Data
    90  	if !IsSlashableAttestationData(data1, data2) {
    91  		return errors.New("attestations are not slashable")
    92  	}
    93  	if err := VerifyIndexedAttestation(ctx, beaconState, att1); err != nil {
    94  		return errors.Wrap(err, "could not validate indexed attestation")
    95  	}
    96  	if err := VerifyIndexedAttestation(ctx, beaconState, att2); err != nil {
    97  		return errors.Wrap(err, "could not validate indexed attestation")
    98  	}
    99  	return nil
   100  }
   101  
   102  // IsSlashableAttestationData verifies a slashing against the Casper Proof of Stake FFG rules.
   103  //
   104  // Spec pseudocode definition:
   105  //   def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationData) -> bool:
   106  //    """
   107  //    Check if ``data_1`` and ``data_2`` are slashable according to Casper FFG rules.
   108  //    """
   109  //    return (
   110  //        # Double vote
   111  //        (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
   112  //        # Surround vote
   113  //        (data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
   114  //    )
   115  func IsSlashableAttestationData(data1, data2 *ethpb.AttestationData) bool {
   116  	if data1 == nil || data2 == nil || data1.Target == nil || data2.Target == nil || data1.Source == nil || data2.Source == nil {
   117  		return false
   118  	}
   119  	isDoubleVote := !attestationutil.AttDataIsEqual(data1, data2) && data1.Target.Epoch == data2.Target.Epoch
   120  	att1 := &ethpb.IndexedAttestation{Data: data1}
   121  	att2 := &ethpb.IndexedAttestation{Data: data2}
   122  	// Check if att1 is surrounding att2.
   123  	isSurroundVote := slashutil.IsSurround(att1, att2)
   124  	return isDoubleVote || isSurroundVote
   125  }
   126  
   127  func slashableAttesterIndices(slashing *ethpb.AttesterSlashing) []uint64 {
   128  	if slashing == nil || slashing.Attestation_1 == nil || slashing.Attestation_2 == nil {
   129  		return nil
   130  	}
   131  	indices1 := slashing.Attestation_1.AttestingIndices
   132  	indices2 := slashing.Attestation_2.AttestingIndices
   133  	return sliceutil.IntersectionUint64(indices1, indices2)
   134  }