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 := ðpb.IndexedAttestation{Data: data1} 121 att2 := ðpb.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 }