-
Notifications
You must be signed in to change notification settings - Fork 997
/
validators.go
245 lines (222 loc) · 8.63 KB
/
validators.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
package helpers
import (
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
var activeIndicesCache = cache.NewActiveIndicesCache()
var activeCountCache = cache.NewActiveCountCache()
// IsActiveValidator returns the boolean value on whether the validator
// is active or not.
//
// Spec pseudocode definition:
// def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
// """
// Check if ``validator`` is active.
// """
// return validator.activation_epoch <= epoch < validator.exit_epoch
func IsActiveValidator(validator *ethpb.Validator, epoch uint64) bool {
return validator.ActivationEpoch <= epoch &&
epoch < validator.ExitEpoch
}
// IsSlashableValidator returns the boolean value on whether the validator
// is slashable or not.
//
// Spec pseudocode definition:
// def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool:
// """
// Check if ``validator`` is slashable.
// """
// return (
// validator.activation_epoch <= epoch < validator.withdrawable_epoch and
// validator.slashed is False
// )
func IsSlashableValidator(validator *ethpb.Validator, epoch uint64) bool {
active := validator.ActivationEpoch <= epoch
beforeWithdrawable := epoch < validator.WithdrawableEpoch
return beforeWithdrawable && active && !validator.Slashed
}
// ActiveValidatorIndices filters out active validators based on validator status
// and returns their indices in a list.
//
// WARNING: This method allocates a new copy of the validator index set and is
// considered to be very memory expensive. Avoid using this unless you really
// need the active validator indices for some specific reason.
//
// Spec pseudocode definition:
// def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> Sequence[ValidatorIndex]:
// """
// Return the sequence of active validator indices at ``epoch``.
// """
// return [ValidatorIndex(i) for i, v in enumerate(state.validators) if is_active_validator(v, epoch)]
func ActiveValidatorIndices(state *pb.BeaconState, epoch uint64) ([]uint64, error) {
if featureconfig.Get().EnableNewCache {
activeIndices, err := committeeCache.ActiveIndices(epoch)
if err != nil {
return nil, errors.Wrap(err, "could not interface with committee cache")
}
if activeIndices != nil {
return activeIndices, nil
}
}
indices, err := activeIndicesCache.ActiveIndicesInEpoch(epoch)
if err != nil {
return nil, errors.Wrap(err, "could not retrieve active indices from cache")
}
if indices != nil {
return indices, nil
}
for i, v := range state.Validators {
if IsActiveValidator(v, epoch) {
indices = append(indices, uint64(i))
}
}
if err := activeIndicesCache.AddActiveIndicesList(&cache.ActiveIndicesByEpoch{
Epoch: epoch,
ActiveIndices: indices,
}); err != nil {
return nil, errors.Wrap(err, "could not save active indices for cache")
}
return indices, nil
}
// ActiveValidatorCount returns the number of active validators in the state
// at the given epoch.
func ActiveValidatorCount(state *pb.BeaconState, epoch uint64) (uint64, error) {
count, err := activeCountCache.ActiveCountInEpoch(epoch)
if err != nil {
return 0, errors.Wrap(err, "could not retrieve active count from cache")
}
if count != params.BeaconConfig().FarFutureEpoch {
return count, nil
}
count = 0
for _, v := range state.Validators {
if IsActiveValidator(v, epoch) {
count++
}
}
if err := activeCountCache.AddActiveCount(&cache.ActiveCountByEpoch{
Epoch: epoch,
ActiveCount: count,
}); err != nil {
return 0, errors.Wrap(err, "could not save active count for cache")
}
return count, nil
}
// DelayedActivationExitEpoch takes in epoch number and returns when
// the validator is eligible for activation and exit.
//
// Spec pseudocode definition:
// def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
// """
// Return the epoch during which validator activations and exits initiated in ``epoch`` take effect.
// """
// return Epoch(epoch + 1 + ACTIVATION_EXIT_DELAY)
func DelayedActivationExitEpoch(epoch uint64) uint64 {
return epoch + 1 + params.BeaconConfig().MaxSeedLookhead
}
// ValidatorChurnLimit returns the number of validators that are allowed to
// enter and exit validator pool for an epoch.
//
// Spec pseudocode definition:
// def get_validator_churn_limit(state: BeaconState) -> uint64:
// """
// Return the validator churn limit for the current epoch.
// """
// active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
// return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)
func ValidatorChurnLimit(activeValidatorCount uint64) (uint64, error) {
churnLimit := activeValidatorCount / params.BeaconConfig().ChurnLimitQuotient
if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit {
churnLimit = params.BeaconConfig().MinPerEpochChurnLimit
}
return churnLimit, nil
}
// BeaconProposerIndex returns proposer index of a current slot.
//
// Spec pseudocode definition:
// def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
// """
// Return the beacon proposer index at the current slot.
// """
// epoch = get_current_epoch(state)
// seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8))
// indices = get_active_validator_indices(state, epoch)
// return compute_proposer_index(state, indices, seed)
func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) {
e := CurrentEpoch(state)
seed, err := Seed(state, e, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
return 0, errors.Wrap(err, "could not generate seed")
}
seedWithSlot := append(seed[:], bytesutil.Bytes8(state.Slot)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
indices, err := ActiveValidatorIndices(state, e)
if err != nil {
return 0, errors.Wrap(err, "could not get active indices")
}
return ComputeProposerIndex(state, indices, seedWithSlotHash)
}
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
//
// Spec pseudocode definition:
// def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex:
// """
// Return from ``indices`` a random index sampled by effective balance.
// """
// assert len(indices) > 0
// MAX_RANDOM_BYTE = 2**8 - 1
// i = 0
// while True:
// candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), seed)]
// random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
// effective_balance = state.validators[candidate_index].effective_balance
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
// return ValidatorIndex(candidate_index)
// i += 1
func ComputeProposerIndex(state *pb.BeaconState, indices []uint64, seed [32]byte) (uint64, error) {
length := uint64(len(indices))
if length == 0 {
return 0, errors.New("empty indices list")
}
maxRandomByte := uint64(1<<8 - 1)
for i := uint64(0); ; i++ {
candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true)
if err != nil {
return 0, err
}
b := append(seed[:], bytesutil.Bytes8(i/32)...)
randomByte := hashutil.Hash(b)[i%32]
effectiveBal := state.Validators[candidateIndex].EffectiveBalance
if effectiveBal*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) {
return candidateIndex, nil
}
}
}
// Domain returns the domain version for BLS private key to sign and verify.
//
// Spec pseudocode definition:
// def get_domain(state: BeaconState,
// domain_type: int,
// message_epoch: Epoch=None) -> int:
// """
// Return the signature domain (fork version concatenated with domain type) of a message.
// """
// epoch = get_current_epoch(state) if message_epoch is None else message_epoch
// fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
// return bls_domain(domain_type, fork_version)
func Domain(fork *pb.Fork, epoch uint64, domainType []byte) uint64 {
var forkVersion []byte
if epoch < fork.Epoch {
forkVersion = fork.PreviousVersion
} else {
forkVersion = fork.CurrentVersion
}
return bls.Domain(domainType, forkVersion)
}