Add back State To ComputeProposerIndex (#5674)

* add back state
* fix tests
* change method signatures
* lint
* Merge branch 'master' into addBackState
* Merge branch 'master' into addBackState
* terence's review
* preston's review
* add back comment
This commit is contained in:
Nishant Das 2020-04-29 23:22:52 +08:00 committed by GitHub
parent 84e51a5236
commit 81a7bc7e05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 7 deletions

View File

@ -372,11 +372,10 @@ func precomputeProposerIndices(state *stateTrie.BeaconState, activeIndices []uin
return nil, errors.Wrap(err, "could not generate seed")
}
slot := StartSlot(e)
vals := state.Validators()
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
seedWithSlot := append(seed[:], bytesutil.Bytes8(slot+i)...)
seedWithSlotHash := hashFunc(seedWithSlot)
index, err := ComputeProposerIndex(vals, activeIndices, seedWithSlotHash)
index, err := ComputeProposerIndex(state, activeIndices, seedWithSlotHash)
if err != nil {
return nil, err
}

View File

@ -743,7 +743,7 @@ func TestPrecomputeProposerIndices_Ok(t *testing.T) {
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
seedWithSlot := append(seed[:], bytesutil.Bytes8(i)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
index, err := ComputeProposerIndex(state.Validators(), indices, seedWithSlotHash)
index, err := ComputeProposerIndex(state, indices, seedWithSlotHash)
if err != nil {
t.Fatal(err)
}

View File

@ -179,11 +179,63 @@ func BeaconProposerIndex(state *stateTrie.BeaconState) (uint64, error) {
return 0, errors.Wrap(err, "could not update committee cache")
}
return ComputeProposerIndex(state.Validators(), indices, seedWithSlotHash)
return ComputeProposerIndex(state, indices, seedWithSlotHash)
}
// ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer.
//
// This method is more efficient than ComputeProposerIndexWithValidators as it uses the read only validator
// abstraction to retrieve validator related data. Whereas the other method requires a whole copy of the validator
// set.
//
// 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(bState *stateTrie.BeaconState, activeIndices []uint64, seed [32]byte) (uint64, error) {
length := uint64(len(activeIndices))
if length == 0 {
return 0, errors.New("empty active indices list")
}
maxRandomByte := uint64(1<<8 - 1)
hashFunc := hashutil.CustomSHA256Hasher()
for i := uint64(0); ; i++ {
candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true /* shuffle */)
if err != nil {
return 0, err
}
candidateIndex = activeIndices[candidateIndex]
if int(candidateIndex) >= bState.NumValidators() {
return 0, errors.New("active index out of range")
}
b := append(seed[:], bytesutil.Bytes8(i/32)...)
randomByte := hashFunc(b)[i%32]
v, err := bState.ValidatorAtIndexReadOnly(candidateIndex)
if err != nil {
return 0, nil
}
effectiveBal := v.EffectiveBalance()
if effectiveBal*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) {
return candidateIndex, nil
}
}
}
// ComputeProposerIndexWithValidators returns the index sampled by effective balance, which is used to calculate proposer.
//
// Note: This method signature deviates slightly from the spec recommended definition. The full
// state object is not required to compute the proposer index.
//
@ -202,7 +254,8 @@ func BeaconProposerIndex(state *stateTrie.BeaconState) (uint64, error) {
// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
// return ValidatorIndex(candidate_index)
// i += 1
func ComputeProposerIndex(validators []*ethpb.Validator, activeIndices []uint64, seed [32]byte) (uint64, error) {
// Deprecated: Prefer using the beacon state with ComputeProposerIndex to avoid an unnecessary copy of the validator set.
func ComputeProposerIndexWithValidators(validators []*ethpb.Validator, activeIndices []uint64, seed [32]byte) (uint64, error) {
length := uint64(len(activeIndices))
if length == 0 {
return 0, errors.New("empty active indices list")

View File

@ -5,6 +5,8 @@ import (
"reflect"
"testing"
"github.com/prysmaticlabs/prysm/shared/hashutil"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@ -184,6 +186,62 @@ func TestBeaconProposerIndex_OK(t *testing.T) {
}
}
func TestComputeProposerIndex_Compatibility(t *testing.T) {
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state, err := beaconstate.InitializeFromProto(&pb.BeaconState{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
if err != nil {
t.Fatal(err)
}
indices, err := ActiveValidatorIndices(state, 0)
if err != nil {
t.Fatal(err)
}
var proposerIndices []uint64
seed, err := Seed(state, 0, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
t.Fatal(err)
}
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
seedWithSlot := append(seed[:], bytesutil.Bytes8(i)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
index, err := ComputeProposerIndex(state, indices, seedWithSlotHash)
if err != nil {
t.Fatal(err)
}
proposerIndices = append(proposerIndices, index)
}
var wantedProposerIndices []uint64
seed, err = Seed(state, 0, params.BeaconConfig().DomainBeaconProposer)
if err != nil {
t.Fatal(err)
}
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
seedWithSlot := append(seed[:], bytesutil.Bytes8(i)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
index, err := ComputeProposerIndexWithValidators(state.Validators(), indices, seedWithSlotHash)
if err != nil {
t.Fatal(err)
}
wantedProposerIndices = append(wantedProposerIndices, index)
}
if !reflect.DeepEqual(wantedProposerIndices, proposerIndices) {
t.Error("Wanted proposer indices from ComputeProposerIndexWithValidators does not match")
}
}
func TestDelayedActivationExitEpoch_OK(t *testing.T) {
epoch := uint64(9999)
got := ActivationExitEpoch(epoch)
@ -541,7 +599,13 @@ func TestComputeProposerIndex(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ComputeProposerIndex(tt.args.validators, tt.args.indices, tt.args.seed)
bState := &pb.BeaconState{Validators: tt.args.validators}
stTrie, err := beaconstate.InitializeFromProtoUnsafe(bState)
if err != nil {
t.Error(err)
return
}
got, err := ComputeProposerIndex(stTrie, tt.args.indices, tt.args.seed)
if (err != nil) != tt.wantErr {
t.Errorf("ComputeProposerIndex() error = %v, wantErr %v", err, tt.wantErr)
return

View File

@ -291,7 +291,7 @@ func archivedValidatorCommittee(
for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ {
seedWithSlot := append(proposerSeed[:], bytesutil.Bytes8(slot)...)
seedWithSlotHash := hashutil.Hash(seedWithSlot)
i, err := helpers.ComputeProposerIndex(activeVals, activeIndices, seedWithSlotHash)
i, err := helpers.ComputeProposerIndexWithValidators(activeVals, activeIndices, seedWithSlotHash)
if err != nil {
return nil, errors.Wrapf(err, "could not check proposer at slot %d", slot)
}