mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-25 04:57:17 +00:00
Add ComputeProposerIndex (#6297)
This is described in
https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_proposer_index.
Part of https://github.com/ledgerwatch/erigon/issues/5965
I compared with the Prysm tests to confirm the implementation is
correct:
2e49fdb3d2/beacon-chain/core/helpers/validators_test.go (L506-L614)
This commit is contained in:
parent
cb04e1166c
commit
d1f6ed29ff
@ -4,6 +4,9 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes"
|
||||
)
|
||||
|
||||
const SHUFFLE_ROUND_COUNT = uint8(90)
|
||||
@ -50,3 +53,34 @@ func ComputeShuffledIndex(ind, ind_count uint64, seed [32]byte) (uint64, error)
|
||||
}
|
||||
return ind, nil
|
||||
}
|
||||
|
||||
func ComputePropserIndex(state *cltypes.BeaconStateBellatrix, indices []uint64, seed [32]byte) (uint64, error) {
|
||||
if len(indices) == 0 {
|
||||
return 0, fmt.Errorf("must have >0 indices")
|
||||
}
|
||||
maxRandomByte := uint64(1<<8 - 1)
|
||||
i := uint64(0)
|
||||
total := uint64(len(indices))
|
||||
hash := sha256.New()
|
||||
buf := make([]byte, 8)
|
||||
for {
|
||||
shuffled, err := ComputeShuffledIndex(i%total, total, seed)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
candidateIndex := indices[shuffled]
|
||||
if candidateIndex >= uint64(len(state.Validators)) {
|
||||
return 0, fmt.Errorf("candidate index out of range: %d for validator set of length: %d", candidateIndex, len(state.Validators))
|
||||
}
|
||||
binary.LittleEndian.PutUint64(buf, i/32)
|
||||
input := append(seed[:], buf...)
|
||||
hash.Reset()
|
||||
hash.Write(input)
|
||||
randomByte := uint64(hash.Sum(nil)[i%32])
|
||||
effectiveBalance := state.Validators[candidateIndex].EffectiveBalance
|
||||
if effectiveBalance*maxRandomByte >= clparams.MainnetBeaconConfig.MaxEffectiveBalance*randomByte {
|
||||
return candidateIndex, nil
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package transition
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes"
|
||||
)
|
||||
|
||||
func TestComputeShuffledIndex(t *testing.T) {
|
||||
@ -34,3 +36,102 @@ func TestComputeShuffledIndex(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeProposerIndex(t *testing.T) {
|
||||
seed := [32]byte{}
|
||||
copy(seed[:], []byte("seed"))
|
||||
testCases := []struct {
|
||||
description string
|
||||
state *cltypes.BeaconStateBellatrix
|
||||
indices []uint64
|
||||
seed [32]byte
|
||||
expected uint64
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
description: "success",
|
||||
state: &cltypes.BeaconStateBellatrix{
|
||||
Validators: []*cltypes.Validator{
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
},
|
||||
},
|
||||
indices: []uint64{0, 1, 2, 3, 4},
|
||||
seed: seed,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
description: "single_active_index",
|
||||
state: &cltypes.BeaconStateBellatrix{
|
||||
Validators: []*cltypes.Validator{
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
},
|
||||
},
|
||||
indices: []uint64{3},
|
||||
seed: seed,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
description: "second_half_active",
|
||||
state: &cltypes.BeaconStateBellatrix{
|
||||
Validators: []*cltypes.Validator{
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
},
|
||||
},
|
||||
indices: []uint64{5, 6, 7, 8, 9},
|
||||
seed: seed,
|
||||
expected: 7,
|
||||
},
|
||||
{
|
||||
description: "zero_active_indices",
|
||||
indices: []uint64{},
|
||||
seed: seed,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
description: "active_index_out_of_range",
|
||||
indices: []uint64{100},
|
||||
state: &cltypes.BeaconStateBellatrix{
|
||||
Validators: []*cltypes.Validator{
|
||||
{EffectiveBalance: testBeaconConfig.MaxEffectiveBalance},
|
||||
},
|
||||
},
|
||||
seed: seed,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
got, err := ComputePropserIndex(tc.state, tc.indices, tc.seed)
|
||||
if tc.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected success, wanted error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if got != tc.expected {
|
||||
t.Errorf("unexpected result: got %d, want %d", got, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user