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