Aligning ETH2.0 Spec - Implemented Slot & Epoch Helpers (#1447)

* starting to use SlotToEpoch...

* updated exisiting functions...

* fixed epoch tests

* fixed rest of the tests

* tests for newly added helper functions

* fixed visiblity

* added PrevEpoch and NextEpoch helpers

* lint

* removed file from other commit
This commit is contained in:
terence tsao
2019-02-01 17:52:35 +01:00
committed by Nishant Das
parent 72542d5fc7
commit cdf50e2fa2
16 changed files with 278 additions and 133 deletions

View File

@@ -6,6 +6,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/attestations",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",

View File

@@ -6,6 +6,7 @@ package attestations
import (
"encoding/binary"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
@@ -27,16 +28,15 @@ func Key(att *pb.AttestationData) [32]byte {
// def is_double_vote(attestation_data_1: AttestationData,
// attestation_data_2: AttestationData) -> bool
// """
// Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
// Returns True if the provided ``AttestationData`` are slashable
// due to a 'double vote'.
// Checks if the two ``AttestationData`` have the same target.
// """
// target_epoch_1 = attestation_data_1.slot // EPOCH_LENGTH
// target_epoch_2 = attestation_data_2.slot // EPOCH_LENGTH
// target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
// target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
// return target_epoch_1 == target_epoch_2
func IsDoubleVote(attestation1 *pb.AttestationData, attestation2 *pb.AttestationData) bool {
epochLength := params.BeaconConfig().EpochLength
return attestation1.Slot/epochLength == attestation2.Slot/epochLength
targetEpoch1 := helpers.SlotToEpoch(attestation1.Slot)
targetEpoch2 := helpers.SlotToEpoch(attestation2.Slot)
return targetEpoch1 == targetEpoch2
}
// IsSurroundVote checks if the data provided by the attestations fulfill the conditions for
@@ -45,29 +45,20 @@ func IsDoubleVote(attestation1 *pb.AttestationData, attestation2 *pb.Attestation
// def is_surround_vote(attestation_data_1: AttestationData,
// attestation_data_2: AttestationData) -> bool:
// """
// Assumes ``attestation_data_1`` is distinct from ``attestation_data_2``.
// Returns True if the provided ``AttestationData`` are slashable
// due to a 'surround vote'.
// Note: parameter order matters as this function only checks
// that ``attestation_data_1`` surrounds ``attestation_data_2``.
// Checks if ``attestation_data_1`` surrounds ``attestation_data_2``.
// """
// source_epoch_1 = attestation_data_1.justified_slot // EPOCH_LENGTH
// source_epoch_2 = attestation_data_2.justified_slot // EPOCH_LENGTH
// target_epoch_1 = attestation_data_1.slot // EPOCH_LENGTH
// target_epoch_2 = attestation_data_2.slot // EPOCH_LENGTH
// return (
// (source_epoch_1 < source_epoch_2) and
// (source_epoch_2 + 1 == target_epoch_2) and
// (target_epoch_2 < target_epoch_1)
// )
// source_epoch_1 = attestation_data_1.justified_epoch
// source_epoch_2 = attestation_data_2.justified_epoch
// target_epoch_1 = slot_to_epoch(attestation_data_1.slot)
// target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
//
// return source_epoch_1 < source_epoch_2 and target_epoch_2 < target_epoch_1
func IsSurroundVote(attestation1 *pb.AttestationData, attestation2 *pb.AttestationData) bool {
epochLength := params.BeaconConfig().EpochLength
sourceEpoch1 := attestation1.JustifiedSlot / epochLength
sourceEpoch2 := attestation2.JustifiedSlot / epochLength
targetEpoch1 := attestation1.Slot / epochLength
targetEpoch2 := attestation2.Slot / epochLength
targetEpoch1 := helpers.SlotToEpoch(attestation1.Slot)
targetEpoch2 := helpers.SlotToEpoch(attestation2.Slot)
return sourceEpoch1 < sourceEpoch2 &&
sourceEpoch2+1 == targetEpoch2 &&
targetEpoch2 < targetEpoch1
return sourceEpoch1 < sourceEpoch2 && targetEpoch2 < targetEpoch1
}

View File

@@ -5,6 +5,7 @@
package balances
import (
"errors"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
@@ -148,6 +149,9 @@ func InclusionDistance(
if err != nil {
return nil, fmt.Errorf("could not get inclusion distance: %v", err)
}
if inclusionDistance == 0 {
return nil, errors.New("could not process inclusion distance: 0")
}
state.ValidatorBalances[index] +=
baseReward(state, index, baseRewardQuotient) *
config.MinAttestationInclusionDelay /

View File

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

View File

@@ -10,6 +10,7 @@ import (
"math"
block "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
b "github.com/prysmaticlabs/prysm/shared/bytesutil"
@@ -21,23 +22,14 @@ import (
// included in the chain during the epoch.
//
// Spec pseudocode definition:
// return [a for a in state.latest_attestations
// if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]
// return [a for a in state.latest_attestations if
// current_epoch == slot_to_epoch(a.data.slot)
func Attestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
epochLength := params.BeaconConfig().EpochLength
var thisEpochAttestations []*pb.PendingAttestationRecord
var earliestSlot uint64
currentEpoch := helpers.CurrentEpoch(state)
for _, attestation := range state.LatestAttestations {
// If the state slot is less than epochLength, then the earliestSlot would
// result in a negative number. Therefore we should default to
// earliestSlot = 0 in this case.
if state.Slot > epochLength {
earliestSlot = state.Slot - epochLength
}
if earliestSlot <= attestation.Data.Slot && attestation.Data.Slot < state.Slot {
if currentEpoch == helpers.SlotToEpoch(attestation.Data.Slot) {
thisEpochAttestations = append(thisEpochAttestations, attestation)
}
}
@@ -87,31 +79,14 @@ func BoundaryAttestations(
// (state.slot - 2 * EPOCH_LENGTH...state.slot - EPOCH_LENGTH).
//
// Spec pseudocode definition:
// return [a for a in state.latest_attestations
// if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]
// return [a for a in state.latest_attestations if
// previous_epoch == slot_to_epoch(a.data.slot)].
func PrevAttestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
epochLength := params.BeaconConfig().EpochLength
var prevEpochAttestations []*pb.PendingAttestationRecord
var earliestSlot uint64
var lastSlot uint64
prevEpoch := helpers.PrevEpoch(state)
for _, attestation := range state.LatestAttestations {
// If the state slot is less than 2 * epochLength, then the earliestSlot would
// result in a negative number. Therefore we should default to
// earliestSlot = 0 in this case.
if state.Slot > 2*epochLength {
earliestSlot = state.Slot - 2*epochLength
}
// If the state slot is less than epochLength, then the lastSlot would
// result in a negative number. Therefore we should default to
// lastSlot = 0 in this case.
if state.Slot > epochLength {
lastSlot = state.Slot - epochLength
}
if earliestSlot <= attestation.Data.Slot &&
attestation.Data.Slot < lastSlot {
if prevEpoch == helpers.SlotToEpoch(attestation.Data.Slot) {
prevEpochAttestations = append(prevEpochAttestations, attestation)
}
}

View File

@@ -36,7 +36,7 @@ func TestEpochAttestations(t *testing.T) {
}
var pendingAttestations []*pb.PendingAttestationRecord
for i := uint64(0); i < params.BeaconConfig().EpochLength*2; i++ {
for i := uint64(0); i < params.BeaconConfig().EpochLength*3; i++ {
pendingAttestations = append(pendingAttestations, &pb.PendingAttestationRecord{
Data: &pb.AttestationData{
Slot: i,
@@ -52,21 +52,21 @@ func TestEpochAttestations(t *testing.T) {
}{
{
stateSlot: 10,
firstAttestationSlot: 0,
firstAttestationSlot: 10 - 10%config.EpochLength,
},
{
stateSlot: 63,
firstAttestationSlot: 0,
firstAttestationSlot: 63 - 63%config.EpochLength,
},
{
stateSlot: 64,
firstAttestationSlot: 64 - params.BeaconConfig().EpochLength,
firstAttestationSlot: 64 - 64%config.EpochLength,
}, {
stateSlot: 127,
firstAttestationSlot: 127 - params.BeaconConfig().EpochLength,
firstAttestationSlot: 127 - 127%config.EpochLength,
}, {
stateSlot: 128,
firstAttestationSlot: 128 - params.BeaconConfig().EpochLength,
firstAttestationSlot: 128 - 128%config.EpochLength,
},
}
@@ -131,7 +131,7 @@ func TestPrevEpochAttestations(t *testing.T) {
}
var pendingAttestations []*pb.PendingAttestationRecord
for i := uint64(0); i < params.BeaconConfig().EpochLength*4; i++ {
for i := uint64(0); i < params.BeaconConfig().EpochLength*5; i++ {
pendingAttestations = append(pendingAttestations, &pb.PendingAttestationRecord{
Data: &pb.AttestationData{
Slot: i,
@@ -147,23 +147,23 @@ func TestPrevEpochAttestations(t *testing.T) {
}{
{
stateSlot: 127,
firstAttestationSlot: 0,
firstAttestationSlot: 127 - config.EpochLength - 127%config.EpochLength,
},
{
stateSlot: 128,
firstAttestationSlot: 0,
firstAttestationSlot: 128 - config.EpochLength - 128%config.EpochLength,
},
{
stateSlot: 383,
firstAttestationSlot: 383 - 2*params.BeaconConfig().EpochLength,
firstAttestationSlot: 383 - config.EpochLength - 383%config.EpochLength,
},
{
stateSlot: 129,
firstAttestationSlot: 129 - 2*params.BeaconConfig().EpochLength,
firstAttestationSlot: 129 - config.EpochLength - 129%config.EpochLength,
},
{
stateSlot: 256,
firstAttestationSlot: 256 - 2*params.BeaconConfig().EpochLength,
firstAttestationSlot: 256 - config.EpochLength - 256%config.EpochLength,
},
}

View File

@@ -6,6 +6,7 @@ package epoch
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
@@ -297,19 +298,11 @@ func ProcessPartialValidatorRegistry(state *pb.BeaconState) *pb.BeaconState {
// Remove any attestation in state.latest_attestations such
// that attestation.data.slot < state.slot - EPOCH_LENGTH
func CleanupAttestations(state *pb.BeaconState) *pb.BeaconState {
epochLength := config.EpochLength
var earliestSlot uint64
// If the state slot is less than epochLength, then the earliestSlot would
// result in a negative number. Therefore we should default to
// earliestSlot = 0 in this case.
if state.Slot > epochLength {
earliestSlot = state.Slot - epochLength
}
currEpoch := helpers.CurrentEpoch(state)
var latestAttestations []*pb.PendingAttestationRecord
for _, attestation := range state.LatestAttestations {
if attestation.Data.Slot >= earliestSlot {
if helpers.SlotToEpoch(attestation.Data.Slot) >= currEpoch {
latestAttestations = append(latestAttestations, attestation)
}
}

View File

@@ -418,7 +418,7 @@ func TestCleanupAttestations(t *testing.T) {
}
epochLength := config.EpochLength
state := &pb.BeaconState{
Slot: 2 * epochLength,
Slot: epochLength,
LatestAttestations: []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{Slot: 1}},
{Data: &pb.AttestationData{Slot: epochLength - 10}},
@@ -431,7 +431,7 @@ func TestCleanupAttestations(t *testing.T) {
},
}
wanted := &pb.BeaconState{
Slot: 2 * epochLength,
Slot: epochLength,
LatestAttestations: []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{Slot: epochLength}},
{Data: &pb.AttestationData{Slot: epochLength + 1}},

View File

@@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["slot_epoch.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["slot_epoch_test.go"],
embed = [":go_default_library"],
deps = ["//proto/beacon/p2p/v1:go_default_library"],
)

View File

@@ -0,0 +1,53 @@
package helpers
import (
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"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
}
// CurrentEpoch returns the current epoch number calculated from
// the slot number stored in beacon state.
//
// Spec pseudocode definition:
// def get_current_epoch(state: BeaconState) -> EpochNumber:
// return slot_to_epoch(state.slot)
func CurrentEpoch(state *pb.BeaconState) uint64 {
return SlotToEpoch(state.Slot)
}
// PrevEpoch returns the previous epoch number calculated from
// the slot number stored in beacon state. It also checks for
// underflow condition.
func PrevEpoch(state *pb.BeaconState) uint64 {
if SlotToEpoch(state.Slot) == 0 {
return 0
}
return SlotToEpoch(state.Slot) - 1
}
// NextEpoch returns the next epoch number calculated form
// the slot number stored in beacon state.
func NextEpoch(state *pb.BeaconState) uint64 {
return SlotToEpoch(state.Slot) + 1
}
// StartSlot returns the first slot number of the
// current epoch.
//
// Spec pseudocode definition:
// def get_epoch_start_slot(epoch: EpochNumber) -> SlotNumber:
// return epoch * EPOCH_LENGTH
func StartSlot(epoch uint64) uint64 {
return epoch * config.EpochLength
}

View File

@@ -0,0 +1,99 @@
package helpers
import (
"testing"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
func TestSlotToEpoch(t *testing.T) {
tests := []struct {
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},
}
for _, tt := range tests {
if tt.epoch != SlotToEpoch(tt.slot) {
t.Errorf("SlotToEpoch(%d) = %d, wanted: %d", tt.slot, SlotToEpoch(tt.slot), tt.epoch)
}
}
}
func TestCurrentEpoch(t *testing.T) {
tests := []struct {
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},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
if tt.epoch != CurrentEpoch(state) {
t.Errorf("CurrentEpoch(%d) = %d, wanted: %d", state.Slot, CurrentEpoch(state), tt.epoch)
}
}
}
func TestPrevEpoch(t *testing.T) {
tests := []struct {
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},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
if tt.epoch != PrevEpoch(state) {
t.Errorf("PrevEpoch(%d) = %d, wanted: %d", state.Slot, PrevEpoch(state), tt.epoch)
}
}
}
func TestNextEpoch(t *testing.T) {
tests := []struct {
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},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.slot}
if tt.epoch != NextEpoch(state) {
t.Errorf("NextEpoch(%d) = %d, wanted: %d", state.Slot, NextEpoch(state), tt.epoch)
}
}
}
func TestEpochStartSlot(t *testing.T) {
tests := []struct {
epoch uint64
startSlot uint64
}{
{epoch: 0, startSlot: 0 * config.EpochLength},
{epoch: 1, startSlot: 1 * config.EpochLength},
{epoch: 10, startSlot: 10 * config.EpochLength},
}
for _, tt := range tests {
state := &pb.BeaconState{Slot: tt.epoch}
if tt.startSlot != StartSlot(tt.epoch) {
t.Errorf("StartSlot(%d) = %d, wanted: %d", state.Slot, StartSlot(tt.epoch), tt.startSlot)
}
}
}

View File

@@ -31,6 +31,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/hashutil:go_default_library",

View File

@@ -2,6 +2,7 @@ package state
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"strings"
"testing"
@@ -347,7 +348,14 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) {
}
func TestProcessEpoch_PassesProcessingConditions(t *testing.T) {
validatorRegistry := validators.InitialValidatorRegistry()
var validatorRegistry []*pb.ValidatorRecord
for i := uint64(0); i < 10; i++ {
validatorRegistry = append(validatorRegistry,
&pb.ValidatorRecord{
ExitSlot: config.FarFutureSlot,
Balance: config.MaxDeposit,
})
}
validatorBalances := make([]uint64, len(validatorRegistry))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = config.MaxDeposit
@@ -362,8 +370,7 @@ func TestProcessEpoch_PassesProcessingConditions(t *testing.T) {
JustifiedSlot: 64,
JustifiedBlockRootHash32: []byte{0},
},
ParticipationBitfield: []byte{0xff},
SlotIncluded: i + config.EpochLength + 1,
SlotIncluded: i + config.EpochLength + 1,
})
}
@@ -418,7 +425,7 @@ func TestProcessEpoch_InactiveConditions(t *testing.T) {
JustifiedSlot: 64,
JustifiedBlockRootHash32: []byte{0},
},
ParticipationBitfield: []byte{0xff},
ParticipationBitfield: []byte{},
SlotIncluded: i + config.EpochLength + 1,
})
}
@@ -522,23 +529,23 @@ func TestProcessEpoch_CantGetPrevValidatorIndices(t *testing.T) {
}
want := fmt.Sprintf(
"input committee slot 1 out of bounds: %d <= slot < %d",
config.EpochLength,
config.EpochLength*3,
"input committee epoch 0 out of bounds: %d <= epoch < %d",
helpers.SlotToEpoch(config.EpochLength),
helpers.SlotToEpoch(config.EpochLength*2),
)
if _, err := ProcessEpoch(state); !strings.Contains(err.Error(), want) {
t.Errorf("Expected: %s, received: %v", want, err)
}
}
func TestProcessEpoch_CantProcessPrevBoundaryAttestations(t *testing.T) {
func TestProcessEpoch_CantProcessCurrentBoundaryAttestations(t *testing.T) {
state := &pb.BeaconState{
LatestAttestations: []*pb.PendingAttestationRecord{
{Data: &pb.AttestationData{}},
}}
want := fmt.Sprintf(
"could not get prev boundary attestations: slot %d out of bounds: %d <= slot < %d",
"could not get current boundary attestations: slot %d out of bounds: %d <= slot < %d",
state.LatestAttestations[0].Data.Slot, state.Slot, state.Slot,
)
if _, err := ProcessEpoch(state); !strings.Contains(err.Error(), want) {
@@ -574,8 +581,7 @@ func TestProcessEpoch_CantProcessEjections(t *testing.T) {
{Data: &pb.AttestationData{}, ParticipationBitfield: participationBitfield},
}}
want := fmt.Sprintf(
"validator 0 could not exit until slot %d", 320)
want := fmt.Sprintf("could not process inclusion distance: 0")
if _, err := ProcessEpoch(state); !strings.Contains(err.Error(), want) {
t.Errorf("Expected: %s, received: %v", want, err)

View File

@@ -9,6 +9,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/validators",
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",

View File

@@ -7,6 +7,7 @@ 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"
@@ -63,6 +64,7 @@ func AttestationParticipants(
break
}
}
if len(participationBitfield) != mathutil.CeilDiv8(len(committee)) {
return nil, fmt.Errorf(
"wanted participants bitfield length %d, got: %d",
@@ -103,31 +105,36 @@ func CurrCommitteesCountPerSlot(state *pb.BeaconState) uint64 {
// contains the shard associated with the committee and the validator indices
// in that committee.
// def get_crosslink_committees_at_slot(state: BeaconState,
// slot: int) -> List[Tuple[List[int], int]]:
// slot: SlotNumber) -> List[Tuple[List[ValidatorIndex], ShardNumber]]:
// """
// Returns the list of ``(committee, shard)`` tuples for the ``slot``.
// """
// state_epoch_slot = state.slot - (state.slot % EPOCH_LENGTH)
// assert state_epoch_slot <= slot + EPOCH_LENGTH
// assert slot < state_epoch_slot + EPOCH_LENGTH
// offset = slot % EPOCH_LENGTH
// 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
//
// if slot < state_epoch_slot:
// committees_per_slot = get_previous_epoch_committee_count_per_slot(state)
// shuffling = get_shuffling(
// state.previous_epoch_randao_mix,
// state.validator_registry,
// state.previous_epoch_calculation_slot,
// )
// slot_start_shard = (state.previous_epoch_start_shard + committees_per_slot * offset) % SHARD_COUNT
// 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_slot = get_current_epoch_committee_count_per_slot(state)
// shuffling = get_shuffling(
// state.current_epoch_randao_mix,
// state.validator_registry,
// state.current_epoch_calculation_slot,
// )
// slot_start_shard = (state.current_epoch_start_shard + committees_per_slot * offset) % SHARD_COUNT
// 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 [
// (
@@ -137,33 +144,27 @@ func CurrCommitteesCountPerSlot(state *pb.BeaconState) uint64 {
// for i in range(committees_per_slot)
// ]
func CrosslinkCommitteesAtSlot(state *pb.BeaconState, slot uint64) ([]*CrosslinkCommittee, error) {
var earliestSlot uint64
var countPerSlot uint64
var startShard uint64
var shuffledIndices [][]uint64
var err error
epochLength := config.EpochLength
startEpochSlot := state.Slot - (state.Slot % epochLength)
wantedEpoch := helpers.SlotToEpoch(slot)
currentEpoch := helpers.CurrentEpoch(state)
prevEpoch := helpers.PrevEpoch(state)
nextEpoch := helpers.NextEpoch(state)
// If the start epoch slot is less than epochLength, then the earliestSlot would
// result in a negative number. Therefore we should default to
// earliestSlot = 0 in this case.
if startEpochSlot > epochLength {
earliestSlot = startEpochSlot - epochLength
}
if slot < earliestSlot || slot >= startEpochSlot+epochLength {
if wantedEpoch < prevEpoch || wantedEpoch >= nextEpoch {
return nil, fmt.Errorf(
"input committee slot %d out of bounds: %d <= slot < %d",
slot,
earliestSlot,
startEpochSlot+epochLength,
"input committee epoch %d out of bounds: %d <= epoch < %d",
wantedEpoch,
prevEpoch,
currentEpoch,
)
}
offSet := slot % config.EpochLength
if slot < startEpochSlot {
if wantedEpoch < currentEpoch {
countPerSlot = prevCommitteesCountPerSlot(state)
shuffledIndices, err = Shuffling(
bytesutil.ToBytes32(state.PreviousEpochRandaoMixHash32),

View File

@@ -258,8 +258,8 @@ func TestCrosslinkCommitteesAtSlot_Ok(t *testing.T) {
func TestCrosslinkCommitteesAtSlot_OutOfBound(t *testing.T) {
want := fmt.Sprintf(
"input committee slot %d out of bounds: %d <= slot < %d",
config.EpochLength+1, 0, config.EpochLength,
"input committee epoch %d out of bounds: %d <= epoch < %d",
1, 0, 0,
)
if _, err := CrosslinkCommitteesAtSlot(&pb.BeaconState{}, config.EpochLength+1); !strings.Contains(err.Error(), want) {