Aligning ETH2.0 spec - Make Epoch First Citizen (Shuffling and GetCrosslinkAtSlot) (#1488)

This commit is contained in:
terence tsao
2019-02-05 19:52:14 +01:00
committed by GitHub
parent 79eb9a5230
commit 506db55be5
26 changed files with 504 additions and 448 deletions

View File

@@ -347,7 +347,7 @@ func Crosslinks(
epochLength := config.EpochLength
startSlot := state.Slot - 2*epochLength
for i := startSlot; i < state.Slot; i++ {
crosslinkCommittees, err := validators.CrosslinkCommitteesAtSlot(state, i)
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(state, i, false)
if err != nil {
return nil, fmt.Errorf("could not get shard committees for slot %d: %v", i, err)
}

View File

@@ -205,10 +205,16 @@ func TestInclusionDistRewards_Ok(t *testing.T) {
ExitEpoch: config.FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(config.DepositsForChainStart / config.TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
attestation := []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{Slot: 0},
ParticipationBitfield: []byte{0xff},
ParticipationBitfield: participationBitfield,
SlotIncluded: 5},
}
@@ -433,9 +439,15 @@ func TestInactivityInclusionPenalty_Ok(t *testing.T) {
ExitEpoch: config.FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(config.DepositsForChainStart / config.TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
attestation := []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{Slot: 0},
ParticipationBitfield: []byte{0xff},
ParticipationBitfield: participationBitfield,
SlotIncluded: 5},
}
@@ -511,10 +523,15 @@ func TestAttestationInclusionRewards(t *testing.T) {
ExitEpoch: config.FarFutureEpoch,
}
}
var participationBitfield []byte
// participation byte length = number of validators / target committee size / bits in a byte.
byteLength := int(config.DepositsForChainStart / config.TargetCommitteeSize / 8)
for i := 0; i < byteLength; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
attestation := []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{Slot: 0},
ParticipationBitfield: []byte{0xff},
ParticipationBitfield: participationBitfield,
SlotIncluded: 0},
}

View File

@@ -303,6 +303,10 @@ func TestHeadAttestationsNotOk(t *testing.T) {
func TestWinningRootOk(t *testing.T) {
state := buildState(0, config.DepositsForChainStart)
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0x01))
}
// Generate 10 roots ([]byte{100}...[]byte{110})
var attestations []*pb.PendingAttestationRecord
@@ -312,7 +316,7 @@ func TestWinningRootOk(t *testing.T) {
Slot: 0,
ShardBlockRootHash32: []byte{byte(i + 100)},
},
ParticipationBitfield: []byte{'A'},
ParticipationBitfield: participationBitfield,
}
attestations = append(attestations, attestation)
}
@@ -330,20 +334,6 @@ func TestWinningRootOk(t *testing.T) {
if !bytes.Equal(winnerRoot, []byte{100}) {
t.Errorf("Incorrect winner root, wanted:[100], got: %v", winnerRoot)
}
// Give root [105] one more attester
attestations[5].ParticipationBitfield = []byte{0xff}
winnerRoot, err = winningRoot(
state,
0,
attestations,
nil)
if err != nil {
t.Fatalf("Could not execute winningRoot: %v", err)
}
if !bytes.Equal(winnerRoot, []byte{105}) {
t.Errorf("Incorrect winner root, wanted:[105], got: %v", winnerRoot)
}
}
func TestWinningRootCantGetParticipantBitfield(t *testing.T) {
@@ -357,14 +347,14 @@ func TestWinningRootCantGetParticipantBitfield(t *testing.T) {
},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 1, 0)
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := winningRoot(state, 0, attestations, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestAttestingValidatorsOk(t *testing.T) {
state := buildState(0, config.DepositsForChainStart)
state := buildState(0, config.EpochLength*2)
var attestations []*pb.PendingAttestationRecord
for i := 0; i < 10; i++ {
@@ -386,8 +376,8 @@ func TestAttestingValidatorsOk(t *testing.T) {
t.Fatalf("Could not execute AttestingValidators: %v", err)
}
// Verify the winner root is attested by validator 237 224 based on shuffling.
if !reflect.DeepEqual(attestedValidators, []uint64{237, 224}) {
// Verify the winner root is attested by validator 109 97 based on shuffling.
if !reflect.DeepEqual(attestedValidators, []uint64{109, 97}) {
t.Errorf("Active validators don't match. Wanted:[237,224], Got: %v", attestedValidators)
}
}
@@ -402,7 +392,7 @@ func TestAttestingValidatorsCantGetWinningRoot(t *testing.T) {
ParticipationBitfield: []byte{},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 1, 0)
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := AttestingValidators(state, 0, []*pb.PendingAttestationRecord{attestation}, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
@@ -410,7 +400,7 @@ func TestAttestingValidatorsCantGetWinningRoot(t *testing.T) {
func TestTotalAttestingBalanceOk(t *testing.T) {
validatorsPerCommittee := uint64(2)
state := buildState(0, config.DepositsForChainStart)
state := buildState(0, 2*config.EpochLength)
// Generate 10 roots ([]byte{100}...[]byte{110})
var attestations []*pb.PendingAttestationRecord
@@ -449,7 +439,7 @@ func TestTotalAttestingBalanceCantGetWinningRoot(t *testing.T) {
ParticipationBitfield: []byte{},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 1, 0)
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := TotalAttestingBalance(state, 0, []*pb.PendingAttestationRecord{attestation}, nil); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
@@ -472,16 +462,20 @@ func TestTotalBalance(t *testing.T) {
func TestInclusionSlotOk(t *testing.T) {
state := buildState(0, config.DepositsForChainStart)
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
state.LatestAttestations = []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{},
ParticipationBitfield: []byte{0xFF},
ParticipationBitfield: participationBitfield,
SlotIncluded: 101},
{Data: &pb.AttestationData{},
ParticipationBitfield: []byte{0xFF},
ParticipationBitfield: participationBitfield,
SlotIncluded: 100},
{Data: &pb.AttestationData{},
ParticipationBitfield: []byte{0xFF},
ParticipationBitfield: participationBitfield,
SlotIncluded: 102},
}
slot, err := InclusionSlot(state, 237)
@@ -496,13 +490,14 @@ func TestInclusionSlotOk(t *testing.T) {
func TestInclusionSlotBadBitfield(t *testing.T) {
state := buildState(0, config.DepositsForChainStart)
state.LatestAttestations = []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{},
ParticipationBitfield: []byte{},
SlotIncluded: 100},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 1, 0)
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := InclusionSlot(state, 0); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
@@ -520,10 +515,14 @@ func TestInclusionSlotNotFound(t *testing.T) {
func TestInclusionDistanceOk(t *testing.T) {
state := buildState(0, config.DepositsForChainStart)
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
state.LatestAttestations = []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{},
ParticipationBitfield: []byte{0xFF},
ParticipationBitfield: participationBitfield,
SlotIncluded: 100},
}
distance, err := InclusionDistance(state, 237)
@@ -548,7 +547,7 @@ func TestInclusionDistanceBadBitfield(t *testing.T) {
SlotIncluded: 100},
}
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 1, 0)
want := fmt.Sprintf("wanted participants bitfield length %d, got: %d", 16, 0)
if _, err := InclusionDistance(state, 0); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}

View File

@@ -174,7 +174,7 @@ func ProcessCrosslinks(
}
for i := startSlot; i < state.Slot; i++ {
crosslinkCommittees, err := validators.CrosslinkCommitteesAtSlot(state, i)
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(state, i, false)
if err != nil {
return nil, fmt.Errorf("could not get committees for slot %d: %v", i, err)
}

View File

@@ -295,6 +295,10 @@ func TestProcessFinalization(t *testing.T) {
func TestProcessCrosslinksOk(t *testing.T) {
state := buildState(5, config.DepositsForChainStart)
state.LatestCrosslinks = []*pb.CrosslinkRecord{{}, {}}
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
var attestations []*pb.PendingAttestationRecord
for i := 0; i < 10; i++ {
@@ -303,7 +307,7 @@ func TestProcessCrosslinksOk(t *testing.T) {
ShardBlockRootHash32: []byte{'A'},
},
// All validators attested to the above roots.
ParticipationBitfield: []byte{0xff},
ParticipationBitfield: participationBitfield,
}
attestations = append(attestations, attestation)
}
@@ -341,7 +345,7 @@ func TestProcessCrosslinksNoParticipantsBitField(t *testing.T) {
wanted := fmt.Sprintf(
"wanted participants bitfield length %d, got: %d",
1, 0,
16, 0,
)
if _, err := ProcessCrosslinks(state, attestations, nil); !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected: %s, received: %s", wanted, err.Error())

View File

@@ -11,8 +11,11 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
],
)
@@ -26,5 +29,8 @@ go_test(
"validators_test.go",
],
embed = [":go_default_library"],
deps = ["//proto/beacon/p2p/v1:go_default_library"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
],
)

View File

@@ -1,9 +1,22 @@
package helpers
import (
"encoding/binary"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
// CrosslinkCommittee defines the validator committee of slot and shard combinations.
type CrosslinkCommittee struct {
Committee []uint64
Shard uint64
}
// EpochCommitteeCount returns the number of crosslink committees of an epoch.
//
// Spec pseudocode definition:
@@ -20,15 +33,15 @@ import (
// ) * EPOCH_LENGTH
func EpochCommitteeCount(activeValidatorCount uint64) uint64 {
var minCommitteePerSlot = uint64(1)
var maxCommitteePerSlot = config.ShardCount / config.EpochLength
var currCommitteePerSlot = activeValidatorCount / config.EpochLength / config.TargetCommitteeSize
var maxCommitteePerSlot = params.BeaconConfig().ShardCount / params.BeaconConfig().EpochLength
var currCommitteePerSlot = activeValidatorCount / params.BeaconConfig().EpochLength / params.BeaconConfig().TargetCommitteeSize
if currCommitteePerSlot > maxCommitteePerSlot {
return maxCommitteePerSlot * config.EpochLength
return maxCommitteePerSlot * params.BeaconConfig().EpochLength
}
if currCommitteePerSlot < 1 {
return minCommitteePerSlot * config.EpochLength
return minCommitteePerSlot * params.BeaconConfig().EpochLength
}
return currCommitteePerSlot * config.EpochLength
return currCommitteePerSlot * params.BeaconConfig().EpochLength
}
// CurrentEpochCommitteeCount returns the number of crosslink committees per epoch
@@ -88,3 +101,197 @@ func NextEpochCommitteeCount(state *pb.BeaconState) uint64 {
state.ValidatorRegistry, CurrentEpoch(state)+1)
return EpochCommitteeCount(uint64(len(prevActiveValidatorIndices)))
}
// CrosslinkCommitteesAtSlot returns the list of crosslink committees, it
// contains the shard associated with the committee and the validator indices
// in that committee.
// def get_crosslink_committees_at_slot(state: BeaconState,
// slot: SlotNumber,
// registry_change=False: bool) -> List[Tuple[List[ValidatorIndex], ShardNumber]]:
// """
// Return the list of ``(committee, shard)`` tuples for the ``slot``.
//
// Note: There are two possible shufflings for crosslink committees for a
// ``slot`` in the next epoch -- with and without a `registry_change`
// """
// epoch = slot_to_epoch(slot)
// current_epoch = get_current_epoch(state)
// previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch
// next_epoch = current_epoch + 1
//
// assert previous_epoch <= epoch <= next_epoch
//
// if epoch == previous_epoch:
// committees_per_epoch = get_previous_epoch_committee_count(state)
// seed = state.previous_epoch_seed
// shuffling_epoch = state.previous_calculation_epoch
// shuffling_start_shard = state.previous_epoch_start_shard
// elif epoch == current_epoch:
// committees_per_epoch = get_current_epoch_committee_count(state)
// seed = state.current_epoch_seed
// shuffling_epoch = state.current_calculation_epoch
// shuffling_start_shard = state.current_epoch_start_shard
// elif epoch == next_epoch:
// current_committees_per_epoch = get_current_epoch_committee_count(state)
// committees_per_epoch = get_next_epoch_committee_count(state)
// shuffling_epoch = next_epoch
//
// epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
// if registry_change:
// seed = generate_seed(state, next_epoch)
// shuffling_start_shard = (state.current_epoch_start_shard + current_committees_per_epoch) % SHARD_COUNT
// elif epochs_since_last_registry_update > 1 and is_power_of_two(epochs_since_last_registry_update):
// seed = generate_seed(state, next_epoch)
// shuffling_start_shard = state.current_epoch_start_shard
// else:
// seed = state.current_epoch_seed
// shuffling_start_shard = state.current_epoch_start_shard
//
// shuffling = get_shuffling(
// seed,
// state.validator_registry,
// shuffling_epoch,
// )
// offset = slot % EPOCH_LENGTH
// committees_per_slot = committees_per_epoch // EPOCH_LENGTH
// slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT
//
// return [
// (
// shuffling[committees_per_slot * offset + i],
// (slot_start_shard + i) % SHARD_COUNT,
// )
// for i in range(committees_per_slot)
// ]
func CrosslinkCommitteesAtSlot(
state *pb.BeaconState,
slot uint64,
registryChange bool) ([]*CrosslinkCommittee, error) {
var committeesPerEpoch uint64
var shufflingEpoch uint64
var shufflingStartShard uint64
var seed [32]byte
var err error
wantedEpoch := SlotToEpoch(slot)
currentEpoch := CurrentEpoch(state)
prevEpoch := PrevEpoch(state)
nextEpoch := NextEpoch(state)
if wantedEpoch < prevEpoch || wantedEpoch > nextEpoch {
return nil, fmt.Errorf(
"input committee epoch %d out of bounds: %d <= epoch <= %d",
wantedEpoch,
prevEpoch,
currentEpoch,
)
}
if wantedEpoch == prevEpoch {
committeesPerEpoch = PrevEpochCommitteeCount(state)
seed = bytesutil.ToBytes32(state.PreviousEpochSeedHash32)
shufflingEpoch = state.PreviousEpochCalculationSlot
shufflingStartShard = state.PreviousEpochStartShard
} else if wantedEpoch == currentEpoch {
committeesPerEpoch = PrevEpochCommitteeCount(state)
seed = bytesutil.ToBytes32(state.CurrentEpochSeedHash32)
shufflingEpoch = state.CurrentEpochCalculationSlot
shufflingStartShard = state.CurrentEpochStartShard
} else if wantedEpoch == nextEpoch {
currentCommitteesPerEpoch := CurrentEpochCommitteeCount(state)
committeesPerEpoch = NextEpochCommitteeCount(state)
shufflingEpoch = nextEpoch
epochsSinceLastRegistryUpdate := currentEpoch - state.ValidatorRegistryUpdateSlot
if registryChange {
seed, err = GenerateSeed(state, nextEpoch)
if err != nil {
return nil, fmt.Errorf("could not generate seed: %v", err)
}
shufflingStartShard = (state.CurrentEpochStartShard + currentCommitteesPerEpoch) %
params.BeaconConfig().ShardCount
} else if epochsSinceLastRegistryUpdate > 1 &&
mathutil.IsPowerOf2(epochsSinceLastRegistryUpdate) {
seed, err = GenerateSeed(state, nextEpoch)
if err != nil {
return nil, fmt.Errorf("could not generate seed: %v", err)
}
shufflingStartShard = state.CurrentEpochStartShard
} else {
seed = bytesutil.ToBytes32(state.CurrentEpochSeedHash32)
shufflingStartShard = state.CurrentEpochStartShard
}
}
shuffledIndices, err := Shuffling(
seed,
state.ValidatorRegistry,
shufflingEpoch)
if err != nil {
return nil, fmt.Errorf("could not shuffle epoch validators: %v", err)
}
offSet := slot % params.BeaconConfig().EpochLength
committeesPerSlot := committeesPerEpoch / params.BeaconConfig().EpochLength
slotStardShard := (shufflingStartShard + committeesPerSlot*offSet) %
params.BeaconConfig().ShardCount
var crosslinkCommittees []*CrosslinkCommittee
for i := uint64(0); i < committeesPerSlot; i++ {
crosslinkCommittees = append(crosslinkCommittees, &CrosslinkCommittee{
Committee: shuffledIndices[committeesPerSlot*offSet+i],
Shard: (slotStardShard + i) % params.BeaconConfig().ShardCount,
})
}
return crosslinkCommittees, nil
}
// Shuffling shuffles input validator indices and splits them by slot and shard.
//
// Spec pseudocode definition:
// def get_shuffling(seed: Bytes32,
// validators: List[Validator],
// epoch: EpochNumber) -> List[List[ValidatorIndex]]
// """
// Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``.
// Return a list of ``committees_per_epoch`` committees where each
// committee is itself a list of validator indices.
// """
//
// active_validator_indices = get_active_validator_indices(validators, epoch)
//
// committees_per_epoch = get_epoch_committee_count(len(active_validator_indices))
//
// # Shuffle
// seed = xor(seed, int_to_bytes32(epoch))
// shuffled_active_validator_indices = shuffle(active_validator_indices, seed)
//
// # Split the shuffled list into committees_per_epoch pieces
// return split(shuffled_active_validator_indices, committees_per_epoch)
func Shuffling(
seed [32]byte,
validators []*pb.ValidatorRecord,
slot uint64) ([][]uint64, error) {
// Normalize slot to start of epoch boundary.
slot -= slot % params.BeaconConfig().EpochLength
// Figure out how many committees can be in a single slot.
activeIndices := ActiveValidatorIndices(validators, slot)
activeCount := uint64(len(activeIndices))
committeesPerEpoch := EpochCommitteeCount(activeCount)
// Convert slot to bytes and xor it with seed.
slotInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(slotInBytes, slot)
seed = bytesutil.ToBytes32(bytesutil.Xor(seed[:], slotInBytes))
shuffledIndices, err := utils.ShuffleIndices(seed, activeIndices)
if err != nil {
return nil, err
}
// Split the shuffled list into epoch_length * committees_per_slot pieces.
return utils.SplitIndices(shuffledIndices, committeesPerEpoch), nil
}

View File

@@ -1,25 +1,41 @@
package helpers
import (
"fmt"
"reflect"
"strings"
"testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
var size = 1<<(params.BeaconConfig().RandBytes*8) - 1
var validatorsUpperBound = make([]*pb.ValidatorRecord, size)
var validator = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
func populateValidatorsMax() {
for i := 0; i < len(validatorsUpperBound); i++ {
validatorsUpperBound[i] = validator
}
}
func TestEpochCommitteeCount_Ok(t *testing.T) {
// this defines the # of validators required to have 1 committee
// per slot for epoch length.
validatorsPerEpoch := config.EpochLength * config.TargetCommitteeSize
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
tests := []struct {
validatorCount uint64
committeeCount uint64
}{
{0, config.EpochLength},
{1000, config.EpochLength},
{2 * validatorsPerEpoch, 2 * config.EpochLength},
{5 * validatorsPerEpoch, 5 * config.EpochLength},
{16 * validatorsPerEpoch, 16 * config.EpochLength},
{32 * validatorsPerEpoch, 16 * config.EpochLength},
{0, params.BeaconConfig().EpochLength},
{1000, params.BeaconConfig().EpochLength},
{2 * validatorsPerEpoch, 2 * params.BeaconConfig().EpochLength},
{5 * validatorsPerEpoch, 5 * params.BeaconConfig().EpochLength},
{16 * validatorsPerEpoch, 16 * params.BeaconConfig().EpochLength},
{32 * validatorsPerEpoch, 16 * params.BeaconConfig().EpochLength},
}
for _, test := range tests {
if test.committeeCount != EpochCommitteeCount(test.validatorCount) {
@@ -29,13 +45,13 @@ func TestEpochCommitteeCount_Ok(t *testing.T) {
}
}
func TestCurrentEpochCommitteeCount_Ok(t *testing.T) {
validatorsPerEpoch := config.EpochLength * config.TargetCommitteeSize
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(8)
// set curr epoch total validators count to 8 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: config.FarFutureEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
@@ -43,20 +59,20 @@ func TestCurrentEpochCommitteeCount_Ok(t *testing.T) {
ValidatorRegistry: validators,
}
if CurrentEpochCommitteeCount(state) != committeesPerEpoch*config.EpochLength {
if CurrentEpochCommitteeCount(state) != committeesPerEpoch*params.BeaconConfig().EpochLength {
t.Errorf("Incorrect current epoch committee count per slot. Wanted: %d, got: %d",
committeesPerEpoch, CurrentEpochCommitteeCount(state))
}
}
func TestPrevEpochCommitteeCount_Ok(t *testing.T) {
validatorsPerEpoch := config.EpochLength * config.TargetCommitteeSize
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(3)
// set prev epoch total validators count to 3 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: config.FarFutureEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
@@ -64,28 +80,133 @@ func TestPrevEpochCommitteeCount_Ok(t *testing.T) {
ValidatorRegistry: validators,
}
if PrevEpochCommitteeCount(state) != committeesPerEpoch*config.EpochLength {
if PrevEpochCommitteeCount(state) != committeesPerEpoch*params.BeaconConfig().EpochLength {
t.Errorf("Incorrect prev epoch committee count per slot. Wanted: %d, got: %d",
committeesPerEpoch, PrevEpochCommitteeCount(state))
}
}
func TestNextEpochCommitteeCount_Ok(t *testing.T) {
validatorsPerEpoch := config.EpochLength * config.TargetCommitteeSize
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(6)
// set prev epoch total validators count to 3 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: config.FarFutureEpoch,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
}
if NextEpochCommitteeCount(state) != committeesPerEpoch*config.EpochLength {
if NextEpochCommitteeCount(state) != committeesPerEpoch*params.BeaconConfig().EpochLength {
t.Errorf("Incorrect next epoch committee count per slot. Wanted: %d, got: %d",
committeesPerEpoch, NextEpochCommitteeCount(state))
}
}
func TestShuffling_Ok(t *testing.T) {
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(6)
// Set epoch total validators count to 6 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
randaoSeed := [32]byte{'A'}
slot := uint64(10)
committees, err := Shuffling(randaoSeed, validators, slot)
if err != nil {
t.Fatalf("Could not shuffle validators: %v", err)
}
// Verify shuffled list is correctly split into committees_per_slot pieces.
committeesPerEpoch = EpochCommitteeCount(uint64(len(validators)))
committeesPerSlot := committeesPerEpoch / params.BeaconConfig().EpochLength
if committeesPerSlot != committeesPerSlot {
t.Errorf("Incorrect committee count after splitting. Wanted: %d, got: %d",
committeesPerSlot, len(committees))
}
// Verify each shuffled committee is TARGET_COMMITTEE_SIZE.
for i := 0; i < len(committees); i++ {
committeeCount := uint64(len(committees[i]))
if committeeCount != params.BeaconConfig().TargetCommitteeSize {
t.Errorf("Incorrect validator count per committee. Wanted: %d, got: %d",
params.BeaconConfig().TargetCommitteeSize, committeeCount)
}
}
}
func TestShuffling_OutOfBound(t *testing.T) {
populateValidatorsMax()
if _, err := Shuffling([32]byte{}, validatorsUpperBound, 0); err == nil {
t.Fatalf("Shuffling should have failed with exceeded upper bound")
}
}
func TestCrosslinkCommitteesAtSlot_Ok(t *testing.T) {
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(6)
// Set epoch total validators count to 6 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: 200,
}
committees, err := CrosslinkCommitteesAtSlot(state, 132, false)
if err != nil {
t.Fatalf("Could not get crosslink committee: %v", err)
}
if len(committees) != int(committeesPerEpoch) {
t.Errorf("Incorrect committee count per slot. Wanted: %d, got: %d",
committeesPerEpoch, len(committees))
}
newCommittees, err := CrosslinkCommitteesAtSlot(state, 180, false)
if err != nil {
t.Fatalf("Could not get crosslink committee: %v", err)
}
if reflect.DeepEqual(committees, newCommittees) {
t.Error("Committees from different slot shall not be equal")
}
}
func TestCrosslinkCommitteesAtSlot_OutOfBound(t *testing.T) {
want := fmt.Sprintf(
"input committee epoch %d out of bounds: %d <= epoch <= %d",
2, 0, 0,
)
if _, err := CrosslinkCommitteesAtSlot(&pb.BeaconState{}, params.BeaconConfig().EpochLength*2, false); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestCrosslinkCommitteesAtSlot_ShuffleFailed(t *testing.T) {
state := &pb.BeaconState{
ValidatorRegistry: validatorsUpperBound,
Slot: 100,
}
want := fmt.Sprint(
"could not shuffle epoch validators: " +
"input list exceeded upper bound and reached modulo bias",
)
if _, err := CrosslinkCommitteesAtSlot(state, 1, false); !strings.Contains(err.Error(), want) {
t.Errorf("Expected: %s, received: %v", want, err)
}
}

View File

@@ -5,6 +5,7 @@ import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
// GenerateSeed generates the randao seed of a given epoch.
@@ -20,8 +21,8 @@ import (
// get_active_index_root(state, epoch)
// )
func GenerateSeed(state *pb.BeaconState, wantedEpoch uint64) ([32]byte, error) {
if wantedEpoch > config.SeedLookahead {
wantedEpoch -= config.SeedLookahead
if wantedEpoch > params.BeaconConfig().SeedLookahead {
wantedEpoch -= params.BeaconConfig().SeedLookahead
}
randaoMix, err := RandaoMix(state, wantedEpoch)
if err != nil {
@@ -47,14 +48,14 @@ func GenerateSeed(state *pb.BeaconState, wantedEpoch uint64) ([32]byte, error) {
func ActiveIndexRoot(state *pb.BeaconState, wantedEpoch uint64) ([]byte, error) {
var earliestEpoch uint64
currentEpoch := CurrentEpoch(state)
if currentEpoch > config.LatestIndexRootsLength+config.EntryExitDelay {
earliestEpoch = currentEpoch - (config.LatestIndexRootsLength + config.EntryExitDelay)
if currentEpoch > params.BeaconConfig().LatestIndexRootsLength+params.BeaconConfig().EntryExitDelay {
earliestEpoch = currentEpoch - (params.BeaconConfig().LatestIndexRootsLength + params.BeaconConfig().EntryExitDelay)
}
if earliestEpoch > wantedEpoch || wantedEpoch >= currentEpoch {
return nil, fmt.Errorf("input indexRoot epoch %d out of bounds: %d <= epoch < %d",
wantedEpoch, earliestEpoch, currentEpoch)
}
return state.LatestIndexRootHash32S[wantedEpoch%config.LatestIndexRootsLength], nil
return state.LatestIndexRootHash32S[wantedEpoch%params.BeaconConfig().LatestIndexRootsLength], nil
}
// RandaoMix returns the randao mix (xor'ed seed)
@@ -71,12 +72,12 @@ func ActiveIndexRoot(state *pb.BeaconState, wantedEpoch uint64) ([]byte, error)
func RandaoMix(state *pb.BeaconState, wantedEpoch uint64) ([]byte, error) {
var earliestEpoch uint64
currentEpoch := CurrentEpoch(state)
if currentEpoch > config.LatestRandaoMixesLength {
earliestEpoch = currentEpoch - config.LatestRandaoMixesLength
if currentEpoch > params.BeaconConfig().LatestRandaoMixesLength {
earliestEpoch = currentEpoch - params.BeaconConfig().LatestRandaoMixesLength
}
if earliestEpoch > wantedEpoch || wantedEpoch >= currentEpoch {
return nil, fmt.Errorf("input randaoMix epoch %d out of bounds: %d <= epoch < %d",
wantedEpoch, earliestEpoch, currentEpoch)
}
return state.LatestRandaoMixesHash32S[wantedEpoch%config.LatestRandaoMixesLength], nil
return state.LatestRandaoMixesHash32S[wantedEpoch%params.BeaconConfig().LatestRandaoMixesLength], nil
}

View File

@@ -8,10 +8,11 @@ import (
"testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestRandaoMix_Ok(t *testing.T) {
randaoMixes := make([][]byte, config.LatestRandaoMixesLength)
randaoMixes := make([][]byte, params.BeaconConfig().LatestRandaoMixesLength)
for i := 0; i < len(randaoMixes); i++ {
intInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(intInBytes, uint64(i))
@@ -32,11 +33,11 @@ func TestRandaoMix_Ok(t *testing.T) {
},
{
epoch: 99999,
randaoMix: randaoMixes[99999%config.LatestRandaoMixesLength],
randaoMix: randaoMixes[99999%params.BeaconConfig().LatestRandaoMixesLength],
},
}
for _, test := range tests {
state.Slot = (test.epoch + 1) * config.EpochLength
state.Slot = (test.epoch + 1) * params.BeaconConfig().EpochLength
mix, err := RandaoMix(state, test.epoch)
if err != nil {
t.Fatalf("Could not get randao mix: %v", err)
@@ -59,7 +60,7 @@ func TestRandaoMix_OutOfBound(t *testing.T) {
}
func TestActiveIndexRoot_Ok(t *testing.T) {
activeIndexRoots := make([][]byte, config.LatestIndexRootsLength)
activeIndexRoots := make([][]byte, params.BeaconConfig().LatestIndexRootsLength)
for i := 0; i < len(activeIndexRoots); i++ {
intInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(intInBytes, uint64(i))
@@ -80,11 +81,11 @@ func TestActiveIndexRoot_Ok(t *testing.T) {
},
{
epoch: 999999,
indexRoot: activeIndexRoots[999999%config.LatestIndexRootsLength],
indexRoot: activeIndexRoots[999999%params.BeaconConfig().LatestIndexRootsLength],
},
}
for _, test := range tests {
state.Slot = (test.epoch + 1) * config.EpochLength
state.Slot = (test.epoch + 1) * params.BeaconConfig().EpochLength
indexRoot, err := ActiveIndexRoot(state, test.epoch)
if err != nil {
t.Fatalf("Could not get index root: %v", err)
@@ -109,7 +110,7 @@ func TestActiveIndexRoot_OutOfBound(t *testing.T) {
func TestGenerateSeed_OutOfBound(t *testing.T) {
wanted := fmt.Sprintf(
"input randaoMix epoch %d out of bounds: %d <= epoch < %d",
100-config.SeedLookahead, 0, 0,
100-params.BeaconConfig().SeedLookahead, 0, 0,
)
if _, err := GenerateSeed(&pb.BeaconState{}, 100); !strings.Contains(err.Error(), wanted) {
t.Errorf("Expected: %s, received: %s", wanted, err.Error())
@@ -117,25 +118,25 @@ func TestGenerateSeed_OutOfBound(t *testing.T) {
}
func TestGenerateSeed_Ok(t *testing.T) {
activeIndexRoots := make([][]byte, config.LatestIndexRootsLength)
activeIndexRoots := make([][]byte, params.BeaconConfig().LatestIndexRootsLength)
for i := 0; i < len(activeIndexRoots); i++ {
intInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(intInBytes, uint64(i))
activeIndexRoots[i] = intInBytes
}
randaoMixes := make([][]byte, config.LatestRandaoMixesLength)
randaoMixes := make([][]byte, params.BeaconConfig().LatestRandaoMixesLength)
for i := 0; i < len(randaoMixes); i++ {
intInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(intInBytes, uint64(i))
randaoMixes[i] = intInBytes
}
slot := 10 * config.SeedLookahead * config.EpochLength
slot := 10 * params.BeaconConfig().SeedLookahead * params.BeaconConfig().EpochLength
state := &pb.BeaconState{
LatestIndexRootHash32S: activeIndexRoots,
LatestRandaoMixesHash32S: randaoMixes,
Slot: slot}
got, err := GenerateSeed(state, 10*config.EpochLength)
got, err := GenerateSeed(state, 10*params.BeaconConfig().EpochLength)
if err != nil {
t.Fatalf("Could not generate seed: %v", err)
}

View File

@@ -5,15 +5,13 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)
var config = params.BeaconConfig()
// SlotToEpoch returns the epoch number of the input slot.
//
// Spec pseudocode definition:
// def slot_to_epoch(slot: SlotNumber) -> EpochNumber:
// return slot // EPOCH_LENGTH
func SlotToEpoch(slot uint64) uint64 {
return slot / config.EpochLength
return slot / params.BeaconConfig().EpochLength
}
// CurrentEpoch returns the current epoch number calculated from
@@ -49,5 +47,5 @@ func NextEpoch(state *pb.BeaconState) uint64 {
// def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber:
// return epoch * EPOCH_LENGTH
func StartSlot(epoch uint64) uint64 {
return epoch * config.EpochLength
return epoch * params.BeaconConfig().EpochLength
}

View File

@@ -4,6 +4,7 @@ import (
"testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestSlotToEpoch(t *testing.T) {
@@ -11,11 +12,11 @@ func TestSlotToEpoch(t *testing.T) {
slot uint64
epoch uint64
}{
{slot: 0, epoch: 0 / config.EpochLength},
{slot: 50, epoch: 0 / config.EpochLength},
{slot: 64, epoch: 64 / config.EpochLength},
{slot: 128, epoch: 128 / config.EpochLength},
{slot: 200, epoch: 200 / config.EpochLength},
{slot: 0, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 50, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 64, epoch: 64 / params.BeaconConfig().EpochLength},
{slot: 128, epoch: 128 / params.BeaconConfig().EpochLength},
{slot: 200, epoch: 200 / params.BeaconConfig().EpochLength},
}
for _, tt := range tests {
if tt.epoch != SlotToEpoch(tt.slot) {
@@ -29,11 +30,11 @@ func TestCurrentEpoch(t *testing.T) {
slot uint64
epoch uint64
}{
{slot: 0, epoch: 0 / config.EpochLength},
{slot: 50, epoch: 0 / config.EpochLength},
{slot: 64, epoch: 64 / config.EpochLength},
{slot: 128, epoch: 128 / config.EpochLength},
{slot: 200, epoch: 200 / config.EpochLength},
{slot: 0, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 50, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 64, epoch: 64 / params.BeaconConfig().EpochLength},
{slot: 128, epoch: 128 / params.BeaconConfig().EpochLength},
{slot: 200, epoch: 200 / params.BeaconConfig().EpochLength},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
@@ -48,11 +49,11 @@ func TestPrevEpoch(t *testing.T) {
slot uint64
epoch uint64
}{
{slot: 0, epoch: 0 / config.EpochLength},
{slot: 50, epoch: 0 / config.EpochLength},
{slot: 64, epoch: 64/config.EpochLength - 1},
{slot: 128, epoch: 128/config.EpochLength - 1},
{slot: 200, epoch: 200/config.EpochLength - 1},
{slot: 0, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 50, epoch: 0 / params.BeaconConfig().EpochLength},
{slot: 64, epoch: 64/params.BeaconConfig().EpochLength - 1},
{slot: 128, epoch: 128/params.BeaconConfig().EpochLength - 1},
{slot: 200, epoch: 200/params.BeaconConfig().EpochLength - 1},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
@@ -67,11 +68,11 @@ func TestNextEpoch(t *testing.T) {
slot uint64
epoch uint64
}{
{slot: 0, epoch: 0/config.EpochLength + 1},
{slot: 50, epoch: 0/config.EpochLength + 1},
{slot: 64, epoch: 64/config.EpochLength + 1},
{slot: 128, epoch: 128/config.EpochLength + 1},
{slot: 200, epoch: 200/config.EpochLength + 1},
{slot: 0, epoch: 0/params.BeaconConfig().EpochLength + 1},
{slot: 50, epoch: 0/params.BeaconConfig().EpochLength + 1},
{slot: 64, epoch: 64/params.BeaconConfig().EpochLength + 1},
{slot: 128, epoch: 128/params.BeaconConfig().EpochLength + 1},
{slot: 200, epoch: 200/params.BeaconConfig().EpochLength + 1},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
@@ -86,9 +87,9 @@ func TestEpochStartSlot(t *testing.T) {
epoch uint64
startSlot uint64
}{
{epoch: 0, startSlot: 0 * config.EpochLength},
{epoch: 1, startSlot: 1 * config.EpochLength},
{epoch: 10, startSlot: 10 * config.EpochLength},
{epoch: 0, startSlot: 0 * params.BeaconConfig().EpochLength},
{epoch: 1, startSlot: 1 * params.BeaconConfig().EpochLength},
{epoch: 10, startSlot: 10 * params.BeaconConfig().EpochLength},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.epoch}

View File

@@ -2,6 +2,7 @@ package helpers
import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
// IsActiveValidator returns the boolean value on whether the validator
@@ -49,5 +50,5 @@ func ActiveValidatorIndices(validators []*pb.ValidatorRecord, epoch uint64) []ui
// """
// return epoch + 1 + ENTRY_EXIT_DELAY
func EntryExitEffectEpoch(epoch uint64) uint64 {
return epoch + 1 + config.EntryExitDelay
return epoch + 1 + params.BeaconConfig().EntryExitDelay
}

View File

@@ -548,7 +548,7 @@ func TestProcessEpoch_CantGetPrevValidatorIndices(t *testing.T) {
}
want := fmt.Sprintf(
"input committee epoch 0 out of bounds: %d <= epoch < %d",
"input committee epoch 0 out of bounds: %d <= epoch <= %d",
helpers.SlotToEpoch(config.EpochLength),
helpers.SlotToEpoch(config.EpochLength*2),
)
@@ -583,6 +583,10 @@ func TestProcessEpoch_CantProcessEjections(t *testing.T) {
for i := uint64(0); i < 4*config.EpochLength; i++ {
randaoHashes = append(randaoHashes, []byte{byte(i)})
}
var participationBitfield []byte
for i := 0; i < 16; i++ {
participationBitfield = append(participationBitfield, byte(0xff))
}
ExitEpoch := 4*config.EpochLength + 1
validatorRegistries[0].ExitEpoch = ExitEpoch
@@ -595,7 +599,7 @@ func TestProcessEpoch_CantProcessEjections(t *testing.T) {
LatestRandaoMixesHash32S: randaoHashes,
LatestCrosslinks: []*pb.CrosslinkRecord{{}},
LatestAttestations: []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{}, ParticipationBitfield: []byte{0xFF}},
{Data: &pb.AttestationData{}, ParticipationBitfield: participationBitfield},
}}
want := fmt.Sprintf("could not process inclusion distance: 0")

View File

@@ -10,7 +10,6 @@ go_library(
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bitutil:go_default_library",
"//shared/bytesutil:go_default_library",

View File

@@ -5,16 +5,12 @@
package validators
import (
"encoding/binary"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bitutil"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
// CrosslinkCommittee defines the validator committee of slot and shard combinations.
@@ -53,7 +49,7 @@ func AttestationParticipants(
participationBitfield []byte) ([]uint64, error) {
// Find the relevant committee.
crosslinkCommittees, err := CrosslinkCommitteesAtSlot(state, attestationData.Slot)
crosslinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(state, attestationData.Slot, false)
if err != nil {
return nil, err
}
@@ -85,153 +81,3 @@ func AttestationParticipants(
}
return participants, nil
}
// CrosslinkCommitteesAtSlot returns the list of crosslink committees, it
// contains the shard associated with the committee and the validator indices
// in that committee.
// def get_crosslink_committees_at_slot(state: BeaconState,
// slot: SlotNumber) -> List[Tuple[List[ValidatorIndex], ShardNumber]]:
// """
// Returns the list of ``(committee, shard)`` tuples for the ``slot``.
// """
// epoch = slot_to_epoch(slot)
// current_epoch = get_current_epoch(state)
// previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else current_epoch
// next_epoch = current_epoch + 1
//
// assert previous_epoch <= epoch < next_epoch
//
// if epoch < current_epoch:
// committees_per_epoch = get_previous_epoch_committee_count(state)
// seed = state.previous_epoch_seed
// shuffling_epoch = state.previous_calculation_epoch
// shuffling_start_shard = state.previous_epoch_start_shard
// else:
// committees_per_epoch = get_current_epoch_committee_count(state)
// seed = state.current_epoch_seed
// shuffling_epoch = state.current_calculation_epoch
// shuffling_start_shard = state.current_epoch_start_shard
//
// shuffling = get_shuffling(
// seed,
// state.validator_registry,
// shuffling_epoch,
// )
// offset = slot % EPOCH_LENGTH
// committees_per_slot = committees_per_epoch // EPOCH_LENGTH
// slot_start_shard = (shuffling_start_shard + committees_per_slot * offset) % SHARD_COUNT
//
// return [
// (
// shuffling[committees_per_slot * offset + i],
// (slot_start_shard + i) % SHARD_COUNT,
// )
// for i in range(committees_per_slot)
// ]
func CrosslinkCommitteesAtSlot(state *pb.BeaconState, slot uint64) ([]*CrosslinkCommittee, error) {
var countPerSlot uint64
var startShard uint64
var shuffledIndices [][]uint64
var err error
wantedEpoch := helpers.SlotToEpoch(slot)
currentEpoch := helpers.CurrentEpoch(state)
prevEpoch := helpers.PrevEpoch(state)
nextEpoch := helpers.NextEpoch(state)
if wantedEpoch < prevEpoch || wantedEpoch >= nextEpoch {
return nil, fmt.Errorf(
"input committee epoch %d out of bounds: %d <= epoch < %d",
wantedEpoch,
prevEpoch,
currentEpoch,
)
}
offSet := slot % params.BeaconConfig().EpochLength
if wantedEpoch < currentEpoch {
countPerSlot = helpers.PrevEpochCommitteeCount(state)
shuffledIndices, err = Shuffling(
bytesutil.ToBytes32(state.PreviousEpochSeedHash32),
state.ValidatorRegistry,
state.PreviousEpochCalculationSlot)
if err != nil {
return nil, fmt.Errorf("could not shuffle prev epoch validators: %v", err)
}
startShard = (state.PreviousEpochStartShard + countPerSlot*offSet) %
params.BeaconConfig().ShardCount
} else {
countPerSlot = helpers.CurrentEpochCommitteeCount(state)
shuffledIndices, err = Shuffling(
bytesutil.ToBytes32(state.CurrentEpochSeedHash32),
state.ValidatorRegistry,
state.CurrentEpochCalculationSlot)
if err != nil {
return nil, fmt.Errorf("could not shuffle current epoch validators: %v", err)
}
startShard = (state.CurrentEpochCalculationSlot + countPerSlot*offSet) %
params.BeaconConfig().ShardCount
}
var crosslinkCommittees []*CrosslinkCommittee
for i := uint64(0); i < countPerSlot; i++ {
crosslinkCommittees = append(crosslinkCommittees, &CrosslinkCommittee{
Committee: shuffledIndices[countPerSlot*offSet+i],
Shard: (startShard + i) % params.BeaconConfig().ShardCount,
})
}
return crosslinkCommittees, nil
}
// Shuffling shuffles input validator indices and splits them by slot and shard.
//
// Spec pseudocode definition:
// def get_shuffling(seed: Bytes32,
// validators: List[Validator],
// slot: int) -> List[List[int]]
// """
// Shuffles ``validators`` into crosslink committees seeded by ``seed`` and ``slot``.
// Returns a list of ``EPOCH_LENGTH * committees_per_slot`` committees where each
// committee is itself a list of validator indices.
// """
//
// # Normalizes slot to start of epoch boundary
// slot -= slot % EPOCH_LENGTH
//
// active_validator_indices = get_active_validator_indices(validators, slot)
//
// committees_per_slot = get_committee_count_per_slot(len(active_validator_indices))
//
// # Shuffle
// seed = xor(seed, int_to_bytes32(slot))
// shuffled_active_validator_indices = shuffle(active_validator_indices, seed)
//
// # Split the shuffled list into epoch_length * committees_per_slot pieces
// return split(shuffled_active_validator_indices, committees_per_slot * EPOCH_LENGTH)
func Shuffling(
seed [32]byte,
validators []*pb.ValidatorRecord,
slot uint64) ([][]uint64, error) {
// Normalize slot to start of epoch boundary.
slot -= slot % params.BeaconConfig().EpochLength
// Figure out how many committees can be in a single slot.
activeIndices := helpers.ActiveValidatorIndices(validators, slot)
activeCount := uint64(len(activeIndices))
committeesPerSlot := helpers.EpochCommitteeCount(activeCount)
// Convert slot to bytes and xor it with seed.
slotInBytes := make([]byte, 32)
binary.BigEndian.PutUint64(slotInBytes, slot)
seed = bytesutil.ToBytes32(bytesutil.Xor(seed[:], slotInBytes))
shuffledIndices, err := utils.ShuffleIndices(seed, activeIndices)
if err != nil {
return nil, err
}
// Split the shuffled list into epoch_length * committees_per_slot pieces.
return utils.SplitIndices(shuffledIndices, committeesPerSlot*params.BeaconConfig().EpochLength), nil
}

View File

@@ -1,15 +1,11 @@
package validators
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/params"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
func TestAttestationParticipants_ok(t *testing.T) {
@@ -17,7 +13,7 @@ func TestAttestationParticipants_ok(t *testing.T) {
t.Errorf("EpochLength should be 64 for these tests to pass")
}
validators := make([]*pb.ValidatorRecord, params.BeaconConfig().DepositsForChainStart)
validators := make([]*pb.ValidatorRecord, 2*params.BeaconConfig().EpochLength)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -40,37 +36,37 @@ func TestAttestationParticipants_ok(t *testing.T) {
{
attestationSlot: 2,
stateSlot: 5,
shard: 256,
shard: 2,
bitfield: []byte{0xFF},
wanted: []uint64{766, 752},
wanted: []uint64{11, 121},
},
{
attestationSlot: 1,
stateSlot: 10,
shard: 128,
shard: 1,
bitfield: []byte{77},
wanted: []uint64{511},
wanted: []uint64{117},
},
{
attestationSlot: 10,
stateSlot: 20,
shard: 383,
shard: 10,
bitfield: []byte{0xFF},
wanted: []uint64{3069, 2608},
wanted: []uint64{14, 30},
},
{
attestationSlot: 64,
stateSlot: 100,
shard: 0,
bitfield: []byte{0xFF},
wanted: []uint64{237, 224},
wanted: []uint64{109, 97},
},
{
attestationSlot: 999,
stateSlot: 1000,
shard: 1023,
shard: 39,
bitfield: []byte{99},
wanted: []uint64{10494},
wanted: []uint64{89},
},
}
@@ -115,124 +111,3 @@ func TestAttestationParticipants_IncorrectBitfield(t *testing.T) {
t.Error("attestation participants should have failed with incorrect bitfield")
}
}
func TestShuffling_Ok(t *testing.T) {
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(6)
// Set epoch total validators count to 6 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
randaoSeed := [32]byte{'A'}
slot := uint64(10)
committees, err := Shuffling(randaoSeed, validators, slot)
if err != nil {
t.Fatalf("Could not shuffle validators: %v", err)
}
// Verify shuffled list is correctly split into committees_per_slot pieces.
committeesPerEpoch = helpers.EpochCommitteeCount(uint64(len(validators)))
committeesPerSlot := committeesPerEpoch / params.BeaconConfig().EpochLength
if committeesPerSlot != committeesPerSlot {
t.Errorf("Incorrect committee count after splitting. Wanted: %d, got: %d",
committeesPerSlot, len(committees))
}
// Verify each shuffled committee is TARGET_COMMITTEE_SIZE.
for i := 0; i < len(committees); i++ {
committeeCount := uint64(len(committees[i]))
if committeeCount*params.BeaconConfig().EpochLength != params.BeaconConfig().TargetCommitteeSize {
t.Errorf("Incorrect validator count per committee. Wanted: %d, got: %d",
params.BeaconConfig().TargetCommitteeSize, committeeCount*params.BeaconConfig().EpochLength)
}
}
}
func TestShuffling_OutOfBound(t *testing.T) {
populateValidatorsMax()
if _, err := Shuffling([32]byte{}, validatorsUpperBound, 0); err == nil {
t.Fatalf("Shuffling should have failed with exceeded upper bound")
}
}
func TestCrosslinkCommitteesAtSlot_Ok(t *testing.T) {
validatorsPerEpoch := params.BeaconConfig().EpochLength * params.BeaconConfig().TargetCommitteeSize
committeesPerEpoch := uint64(6)
// Set epoch total validators count to 6 committees per slot.
validators := make([]*pb.ValidatorRecord, committeesPerEpoch*validatorsPerEpoch)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: 200,
}
committees, err := CrosslinkCommitteesAtSlot(state, 132)
if err != nil {
t.Fatalf("Could not get crosslink committee: %v", err)
}
if len(committees) != int(committeesPerEpoch*params.BeaconConfig().EpochLength) {
t.Errorf("Incorrect committee count per slot. Wanted: %d, got: %d",
committeesPerEpoch*params.BeaconConfig().EpochLength, len(committees))
}
newCommittees, err := CrosslinkCommitteesAtSlot(state, 180)
if err != nil {
t.Fatalf("Could not get crosslink committee: %v", err)
}
if reflect.DeepEqual(committees, newCommittees) {
t.Error("Committees from different slot shall not be equal")
}
}
func TestCrosslinkCommitteesAtSlot_OutOfBound(t *testing.T) {
want := fmt.Sprintf(
"input committee epoch %d out of bounds: %d <= epoch < %d",
1, 0, 0,
)
if _, err := CrosslinkCommitteesAtSlot(&pb.BeaconState{}, params.BeaconConfig().EpochLength+1); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}
func TestCrosslinkCommitteesAtPrevSlot_ShuffleFailed(t *testing.T) {
state := &pb.BeaconState{
ValidatorRegistry: validatorsUpperBound,
Slot: 100,
}
want := fmt.Sprint(
"could not shuffle prev epoch validators: " +
"input list exceeded upper bound and reached modulo bias",
)
if _, err := CrosslinkCommitteesAtSlot(state, 1); !strings.Contains(err.Error(), want) {
t.Errorf("Expected: %s, received: %v", want, err)
}
}
func TestCrosslinkCommitteesAtCurrSlot_ShuffleFailed(t *testing.T) {
state := &pb.BeaconState{
ValidatorRegistry: validatorsUpperBound,
Slot: 100,
}
want := fmt.Sprint(
"could not shuffle current epoch validators: " +
"input list exceeded upper bound and reached modulo bias",
)
if _, err := CrosslinkCommitteesAtSlot(state, 99); !strings.Contains(err.Error(), want) {
t.Errorf("Expected: %s, received: %v", want, err)
}
}

View File

@@ -58,7 +58,7 @@ func ActiveValidators(state *pb.BeaconState, validatorIndices []uint32) []*pb.Va
// first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0]
// return first_committee[slot % len(first_committee)]
func BeaconProposerIdx(state *pb.BeaconState, slot uint64) (uint64, error) {
committeeArray, err := CrosslinkCommitteesAtSlot(state, slot)
committeeArray, err := helpers.CrosslinkCommitteesAtSlot(state, slot, false)
if err != nil {
return 0, err
}

View File

@@ -13,18 +13,6 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)
var size = 1<<(params.BeaconConfig().RandBytes*8) - 1
var validatorsUpperBound = make([]*pb.ValidatorRecord, size)
var validator = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
func populateValidatorsMax() {
for i := 0; i < len(validatorsUpperBound); i++ {
validatorsUpperBound[i] = validator
}
}
func TestHasVoted(t *testing.T) {
// Setting bit field to 11111111.
pendingAttestation := &pb.Attestation{
@@ -189,7 +177,7 @@ func TestBoundaryAttesterIndices(t *testing.T) {
if params.BeaconConfig().EpochLength != 64 {
t.Errorf("EpochLength should be 64 for these tests to pass")
}
validators := make([]*pb.ValidatorRecord, params.BeaconConfig().DepositsForChainStart)
validators := make([]*pb.ValidatorRecord, params.BeaconConfig().EpochLength*2)
for i := 0; i < len(validators); i++ {
validators[i] = &pb.ValidatorRecord{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
@@ -210,9 +198,9 @@ func TestBoundaryAttesterIndices(t *testing.T) {
t.Fatalf("Failed to run BoundaryAttesterIndices: %v", err)
}
if !reflect.DeepEqual(attesterIndices, []uint64{237, 224}) {
if !reflect.DeepEqual(attesterIndices, []uint64{109, 97}) {
t.Errorf("Incorrect boundary attester indices. Wanted: %v, got: %v",
[]uint64{237, 224}, attesterIndices)
[]uint64{109, 97}, attesterIndices)
}
}
@@ -242,19 +230,19 @@ func TestBeaconProposerIdx(t *testing.T) {
},
{
slot: 10,
index: 2797,
index: 2807,
},
{
slot: 19,
index: 4658,
index: 5122,
},
{
slot: 30,
index: 7917,
index: 7947,
},
{
slot: 39,
index: 9778,
index: 10262,
},
}
@@ -296,39 +284,31 @@ func TestAttestingValidatorIndices_Ok(t *testing.T) {
state := &pb.BeaconState{
ValidatorRegistry: validators,
Slot: 5,
Slot: 0,
}
prevAttestation := &pb.PendingAttestationRecord{
Data: &pb.AttestationData{
Slot: 3,
Shard: 384,
ShardBlockRootHash32: []byte{'B'},
},
ParticipationBitfield: []byte{0xFF},
}
thisAttestation := &pb.PendingAttestationRecord{
Data: &pb.AttestationData{
Slot: 3,
Shard: 385,
Shard: 6,
ShardBlockRootHash32: []byte{'B'},
},
ParticipationBitfield: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
}
indices, err := AttestingValidatorIndices(
state,
384,
6,
[]byte{'B'},
[]*pb.PendingAttestationRecord{thisAttestation},
nil,
[]*pb.PendingAttestationRecord{prevAttestation})
if err != nil {
t.Fatalf("Could not execute AttestingValidatorIndices: %v", err)
}
if !reflect.DeepEqual(indices, []uint64{213, 1024}) {
if !reflect.DeepEqual(indices, []uint64{1141, 688}) {
t.Errorf("Could not get incorrect validator indices. Wanted: %v, got: %v",
[]uint64{213, 1024}, indices)
[]uint64{1141, 688}, indices)
}
}

View File

@@ -5,13 +5,11 @@ import (
"testing"
"time"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/shared/params"
)
func setupInitialDeposits(t *testing.T) []*pb.Deposit {

View File

@@ -5,17 +5,15 @@ import (
"testing"
"time"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
logTest "github.com/sirupsen/logrus/hooks/test"
)

BIN
beacon-chain/main Executable file

Binary file not shown.

View File

@@ -12,6 +12,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/db:go_default_library",

View File

@@ -4,11 +4,11 @@ import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
// ValidatorServer defines a server implementation of the gRPC Validator service,
@@ -59,7 +59,7 @@ func (vs *ValidatorServer) ValidatorEpochAssignments(
var proposerSlot uint64
for slot := req.EpochStart; slot < req.EpochStart+params.BeaconConfig().EpochLength; slot++ {
crossLinkCommittees, err := v.CrosslinkCommitteesAtSlot(beaconState, slot)
crossLinkCommittees, err := helpers.CrosslinkCommitteesAtSlot(beaconState, slot, false)
if err != nil {
return nil, err
}

View File

@@ -108,9 +108,9 @@ func TestValidatorEpochAssignments(t *testing.T) {
}
// With initial shuffling of default 16384 validators, the validator corresponding to
// public key 0 should correspond to an attester slot of 2 at shard 5.
if res.Assignment.Shard != 358 {
if res.Assignment.Shard != 5 {
t.Errorf(
"Expected validator with pubkey %#x to be assigned to shard 358, received %d",
"Expected validator with pubkey %#x to be assigned to shard 5, received %d",
req.PublicKey,
res.Assignment.Shard,
)

View File

@@ -7,10 +7,9 @@ import (
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"