-
Notifications
You must be signed in to change notification settings - Fork 26
/
proposers.go
107 lines (96 loc) · 3.02 KB
/
proposers.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
package common
import (
"encoding/binary"
"errors"
"fmt"
"github.com/protolambda/zrnt/eth2/util/hashing"
)
type ProposersEpoch struct {
Spec *Spec
Epoch Epoch
// Proposers is a slice of SLOTS_PER_EPOCH proposer indices for the epoch
Proposers []ValidatorIndex
CommitteesPerSlot uint64
}
func (epc *ProposersEpoch) GetBeaconProposer(slot Slot) (ValidatorIndex, error) {
epoch := epc.Spec.SlotToEpoch(slot)
if epoch != epc.Epoch {
return 0, fmt.Errorf("expected epoch %d for beacon proposer lookup, but lookup was at slot %d (epoch %d)", epc.Epoch, slot, epoch)
}
return epc.Proposers[slot%epc.Spec.SLOTS_PER_EPOCH], nil
}
func ComputeProposers(spec *Spec, state BeaconState, epoch Epoch, active []ValidatorIndex) (*ProposersEpoch, error) {
if len(active) == 0 {
return nil, errors.New("no active validators available to compute proposers")
}
proposers := make([]ValidatorIndex, spec.SLOTS_PER_EPOCH, spec.SLOTS_PER_EPOCH)
mixes, err := state.RandaoMixes()
if err != nil {
return nil, err
}
startSlot, err := spec.EpochStartSlot(epoch)
if err != nil {
return nil, err
}
vals, err := state.Validators()
if err != nil {
return nil, err
}
hFn := hashing.GetHashFn()
// compute beacon proposers
{
epochSeed, err := GetSeed(spec, mixes, epoch, DOMAIN_BEACON_PROPOSER)
if err != nil {
return nil, err
}
var buf [32 + 8]byte
copy(buf[0:32], epochSeed[:])
for i := Slot(0); i < spec.SLOTS_PER_EPOCH; i++ {
binary.LittleEndian.PutUint64(buf[32:], uint64(startSlot+i))
seed := hFn(buf[:])
proposer, err := ComputeProposerIndex(spec, vals, active, seed)
if err != nil {
return nil, err
}
proposers[i] = proposer
}
}
validatorsPerSlot := uint64(len(active)) / uint64(spec.SLOTS_PER_EPOCH)
committeesPerSlot := validatorsPerSlot / uint64(spec.TARGET_COMMITTEE_SIZE)
return &ProposersEpoch{
Spec: spec,
Epoch: epoch,
Proposers: proposers,
CommitteesPerSlot: committeesPerSlot,
}, nil
}
func ComputeProposerIndex(spec *Spec, registry ValidatorRegistry, active []ValidatorIndex, seed Root) (ValidatorIndex, error) {
if len(active) == 0 {
return 0, errors.New("no active validators available to compute proposer")
}
var buf [32 + 8]byte
copy(buf[0:32], seed[:])
hFn := hashing.GetHashFn()
for i := uint64(0); i < 1000; i++ {
binary.LittleEndian.PutUint64(buf[32:], i)
h := hFn(buf[:])
for j := uint64(0); j < 32; j++ {
randomByte := h[j]
absI := ValidatorIndex(((i << 5) | j) % uint64(len(active)))
shuffledI := PermuteIndex(uint8(spec.SHUFFLE_ROUND_COUNT), absI, uint64(len(active)), seed)
candidateIndex := active[int(shuffledI)]
validator, err := registry.Validator(candidateIndex)
if err != nil {
return 0, err
}
effectiveBalance, err := validator.EffectiveBalance()
if err != nil {
return 0, err
}
if effectiveBalance*0xff >= spec.MAX_EFFECTIVE_BALANCE*Gwei(randomByte) {
return candidateIndex, nil
}
}
}
return 0, errors.New("random (but balance-biased) infinite scrolling should always find a proposer")
}