diff --git a/beacon-chain/blockchain/chain_info.go b/beacon-chain/blockchain/chain_info.go index 85912e4da5..b5e2d3a7e0 100644 --- a/beacon-chain/blockchain/chain_info.go +++ b/beacon-chain/blockchain/chain_info.go @@ -6,6 +6,7 @@ import ( "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -30,6 +31,8 @@ type HeadFetcher interface { HeadRoot() []byte HeadBlock() *ethpb.BeaconBlock HeadState(ctx context.Context) (*pb.BeaconState, error) + HeadValidatorsIndices(epoch uint64) ([]uint64, error) + HeadSeed(epoch uint64) ([32]byte, error) } // CanonicalRootFetcher defines a common interface for methods in blockchain service which @@ -102,6 +105,16 @@ func (s *Service) HeadState(ctx context.Context) (*pb.BeaconState, error) { return proto.Clone(s.headState).(*pb.BeaconState), nil } +// HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch. +func (s *Service) HeadValidatorsIndices(epoch uint64) ([]uint64, error) { + return helpers.ActiveValidatorIndices(s.headState, epoch) +} + +// HeadSeed returns the seed from the head view of a given epoch. +func (s *Service) HeadSeed(epoch uint64) ([32]byte, error) { + return helpers.Seed(s.headState, epoch, params.BeaconConfig().DomainBeaconAttester) +} + // CanonicalRoot returns the canonical root of a given slot. func (s *Service) CanonicalRoot(slot uint64) []byte { s.headLock.RLock() diff --git a/beacon-chain/blockchain/forkchoice/process_attestation.go b/beacon-chain/blockchain/forkchoice/process_attestation.go index 2ca16d4319..af154c5720 100644 --- a/beacon-chain/blockchain/forkchoice/process_attestation.go +++ b/beacon-chain/blockchain/forkchoice/process_attestation.go @@ -171,7 +171,7 @@ func (s *Store) saveCheckpointState(ctx context.Context, baseState *pb.BeaconSta // verifyAttestation validates input attestation is valid. func (s *Store) verifyAttestation(ctx context.Context, baseState *pb.BeaconState, a *ethpb.Attestation) (*ethpb.IndexedAttestation, error) { - committee, err := helpers.BeaconCommittee(baseState, a.Data.Slot, a.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(baseState, a.Data.Slot, a.Data.CommitteeIndex) if err != nil { return nil, err } diff --git a/beacon-chain/blockchain/forkchoice/process_block.go b/beacon-chain/blockchain/forkchoice/process_block.go index 988c483b7e..9401d88c05 100644 --- a/beacon-chain/blockchain/forkchoice/process_block.go +++ b/beacon-chain/blockchain/forkchoice/process_block.go @@ -309,7 +309,7 @@ func (s *Store) updateBlockAttestationVote(ctx context.Context, att *ethpb.Attes if baseState == nil { return errors.New("no state found in db with attestation tgt root") } - committee, err := helpers.BeaconCommittee(baseState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(baseState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { return err } diff --git a/beacon-chain/blockchain/forkchoice/process_block_test.go b/beacon-chain/blockchain/forkchoice/process_block_test.go index 5930e12063..0e0676393c 100644 --- a/beacon-chain/blockchain/forkchoice/process_block_test.go +++ b/beacon-chain/blockchain/forkchoice/process_block_test.go @@ -141,7 +141,7 @@ func TestStore_UpdateBlockAttestationVote(t *testing.T) { t.Fatal(err) } - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { t.Error(err) } diff --git a/beacon-chain/blockchain/testing/BUILD.bazel b/beacon-chain/blockchain/testing/BUILD.bazel index 3e35577475..9ff1e24102 100644 --- a/beacon-chain/blockchain/testing/BUILD.bazel +++ b/beacon-chain/blockchain/testing/BUILD.bazel @@ -8,9 +8,11 @@ go_library( visibility = ["//beacon-chain:__subpackages__"], deps = [ "//beacon-chain/core/feed/state:go_default_library", + "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/event:go_default_library", + "//shared/params:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 95aa80f34e..7373013e1f 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -9,9 +9,11 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/event" + "github.com/prysmaticlabs/prysm/shared/params" "github.com/sirupsen/logrus" ) @@ -131,6 +133,16 @@ func (ms *ChainService) ReceiveAttestationNoPubsub(context.Context, *ethpb.Attes return nil } +// HeadValidatorsIndices mocks the same method in the chain service. +func (ms *ChainService) HeadValidatorsIndices(epoch uint64) ([]uint64, error) { + return helpers.ActiveValidatorIndices(ms.State, epoch) +} + +// HeadSeed mocks the same method in the chain service. +func (ms *ChainService) HeadSeed(epoch uint64) ([32]byte, error) { + return helpers.Seed(ms.State, epoch, params.BeaconConfig().DomainBeaconAttester) +} + // GenesisTime mocks the same method in the chain service. func (ms *ChainService) GenesisTime() time.Time { return ms.Genesis diff --git a/beacon-chain/core/blocks/block_operations.go b/beacon-chain/core/blocks/block_operations.go index 6dc7104748..9097103423 100644 --- a/beacon-chain/core/blocks/block_operations.go +++ b/beacon-chain/core/blocks/block_operations.go @@ -828,7 +828,7 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState, // VerifyAttestation converts and attestation into an indexed attestation and verifies // the signature in that attestation. func VerifyAttestation(ctx context.Context, beaconState *pb.BeaconState, att *ethpb.Attestation) error { - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { return err } diff --git a/beacon-chain/core/blocks/block_operations_test.go b/beacon-chain/core/blocks/block_operations_test.go index 78212e0e7e..ac2391aa4d 100644 --- a/beacon-chain/core/blocks/block_operations_test.go +++ b/beacon-chain/core/blocks/block_operations_test.go @@ -923,7 +923,7 @@ func TestProcessAttestations_OK(t *testing.T) { beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{} - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -982,7 +982,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{} - committee, err := helpers.BeaconCommittee(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -1016,7 +1016,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { CustodyBits: custodyBits2, } - committee, err = helpers.BeaconCommittee(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) + committee, err = helpers.BeaconCommitteeFromState(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -1065,7 +1065,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{} - committee, err := helpers.BeaconCommittee(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -1098,7 +1098,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { CustodyBits: custodyBits2, } - committee, err = helpers.BeaconCommittee(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) + committee, err = helpers.BeaconCommitteeFromState(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -1223,7 +1223,7 @@ func TestConvertToIndexed_OK(t *testing.T) { Signature: attestation.Signature, } - committee, err := helpers.BeaconCommittee(state, attestation.Data.Slot, attestation.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(state, attestation.Data.Slot, attestation.Data.CommitteeIndex) if err != nil { t.Error(err) } diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index f8181fee43..a6c7872cf3 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -339,8 +339,9 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { func unslashedAttestingIndices(state *pb.BeaconState, atts []*pb.PendingAttestation) ([]uint64, error) { var setIndices []uint64 seen := make(map[uint64]bool) + for _, att := range atts { - committee, err := helpers.BeaconCommittee(state, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { return nil, err } diff --git a/beacon-chain/core/epoch/precompute/attestation.go b/beacon-chain/core/epoch/precompute/attestation.go index 1df7f9a1aa..bd5d2fbc55 100644 --- a/beacon-chain/core/epoch/precompute/attestation.go +++ b/beacon-chain/core/epoch/precompute/attestation.go @@ -23,6 +23,7 @@ func ProcessAttestations( v := &Validator{} var err error + for _, a := range append(state.PreviousEpochAttestations, state.CurrentEpochAttestations...) { v.IsCurrentEpochAttester, v.IsCurrentEpochTargetAttester, err = AttestedCurrentEpoch(state, a) if err != nil { @@ -35,8 +36,7 @@ func ProcessAttestations( return nil, nil, errors.Wrap(err, "could not check validator attested previous epoch") } - // Get attested indices and update the pre computed fields for each attested validators. - committee, err := helpers.BeaconCommittee(state, a.Data.Slot, a.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(state, a.Data.Slot, a.Data.CommitteeIndex) if err != nil { return nil, nil, err } diff --git a/beacon-chain/core/epoch/precompute/attestation_test.go b/beacon-chain/core/epoch/precompute/attestation_test.go index 9f012020d1..f095504300 100644 --- a/beacon-chain/core/epoch/precompute/attestation_test.go +++ b/beacon-chain/core/epoch/precompute/attestation_test.go @@ -199,7 +199,8 @@ func TestProcessAttestations(t *testing.T) { if err != nil { t.Fatal(err) } - committee, err := helpers.BeaconCommittee(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) + + committee, err := helpers.BeaconCommitteeFromState(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -209,7 +210,7 @@ func TestProcessAttestations(t *testing.T) { t.Error("Not a prev epoch attester") } } - committee, err = helpers.BeaconCommittee(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) + committee, err = helpers.BeaconCommitteeFromState(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) if err != nil { t.Error(err) } diff --git a/beacon-chain/core/helpers/attestation.go b/beacon-chain/core/helpers/attestation.go index b4012d6e45..477665080c 100644 --- a/beacon-chain/core/helpers/attestation.go +++ b/beacon-chain/core/helpers/attestation.go @@ -121,21 +121,19 @@ func SlotSignature(state *pb.BeaconState, slot uint64, privKey *bls.SecretKey) ( return privKey.Sign(s[:], d), nil } -// IsAggregator returns true if the signature is from the input validator. +// IsAggregator returns true if the signature is from the input validator. The committee +// count is provided as an argument rather than direct implementation from spec. Having +// committee count as an argument allows cheaper computation at run time. // // Spec pseudocode definition: // def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool: // committee = get_beacon_committee(state, slot, index) // modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE) // return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0 -func IsAggregator(state *pb.BeaconState, slot uint64, index uint64, slotSig []byte) (bool, error) { - committee, err := BeaconCommittee(state, slot, index) - if err != nil { - return false, err - } +func IsAggregator(committeeCount uint64, slot uint64, index uint64, slotSig []byte) (bool, error) { modulo := uint64(1) - if len(committee)/int(params.BeaconConfig().TargetAggregatorsPerCommittee) > 1 { - modulo = uint64(len(committee)) / params.BeaconConfig().TargetAggregatorsPerCommittee + if committeeCount/params.BeaconConfig().TargetAggregatorsPerCommittee > 1 { + modulo = committeeCount / params.BeaconConfig().TargetAggregatorsPerCommittee } b := hashutil.Hash(slotSig) diff --git a/beacon-chain/core/helpers/attestation_test.go b/beacon-chain/core/helpers/attestation_test.go index 8276e11624..09dc096d7e 100644 --- a/beacon-chain/core/helpers/attestation_test.go +++ b/beacon-chain/core/helpers/attestation_test.go @@ -228,8 +228,13 @@ func TestSlotSignature_Verify(t *testing.T) { func TestIsAggregator_True(t *testing.T) { beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) + + committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0) + if err != nil { + t.Fatal(err) + } sig := privKeys[0].Sign([]byte{}, 0) - agg, err := helpers.IsAggregator(beaconState, 0, 0, sig.Marshal()) + agg, err := helpers.IsAggregator(uint64(len(committee)), 0, 0, sig.Marshal()) if err != nil { t.Fatal(err) } @@ -243,8 +248,12 @@ func TestIsAggregator_False(t *testing.T) { defer params.UseMainnetConfig() beaconState, privKeys := testutil.DeterministicGenesisState(t, 2048) + committee, err := helpers.BeaconCommitteeFromState(beaconState, 0, 0) + if err != nil { + t.Fatal(err) + } sig := privKeys[0].Sign([]byte{}, 0) - agg, err := helpers.IsAggregator(beaconState, 0, 0, sig.Marshal()) + agg, err := helpers.IsAggregator(uint64(len(committee)), 0, 0, sig.Marshal()) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/core/helpers/committee.go b/beacon-chain/core/helpers/committee.go index 528592ba09..c2c84c1cb6 100644 --- a/beacon-chain/core/helpers/committee.go +++ b/beacon-chain/core/helpers/committee.go @@ -17,7 +17,12 @@ import ( var committeeCache = cache.NewCommitteesCache() -// CommitteeCountAtSlot returns the number of crosslink committees of a slot. +// SlotCommitteeCount returns the number of crosslink committees of a slot. The +// active validator count is provided as an argument rather than a direct implementation +// from the spec definition. Having the active validator count as an argument allows for +// cheaper computation, instead of retrieving head state, one can retrieve the validator +// count. +// // // Spec pseudocode definition: // def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64: @@ -29,23 +34,22 @@ var committeeCache = cache.NewCommitteesCache() // MAX_COMMITTEES_PER_SLOT, // len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, // )) -func CommitteeCountAtSlot(state *pb.BeaconState, slot uint64) (uint64, error) { - epoch := SlotToEpoch(slot) - count, err := ActiveValidatorCount(state, epoch) - if err != nil { - return 0, errors.Wrap(err, "could not get active count") - } - var committeePerSlot = count / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize +func SlotCommitteeCount(activeValidatorCount uint64) uint64 { + var committeePerSlot = activeValidatorCount / params.BeaconConfig().SlotsPerEpoch / params.BeaconConfig().TargetCommitteeSize + if committeePerSlot > params.BeaconConfig().MaxCommitteesPerSlot { - return params.BeaconConfig().MaxCommitteesPerSlot, nil + return params.BeaconConfig().MaxCommitteesPerSlot } if committeePerSlot == 0 { - return 1, nil + return 1 } - return committeePerSlot, nil + + return committeePerSlot } -// BeaconCommittee returns the crosslink committee of a given slot and committee index. +// BeaconCommitteeFromState returns the crosslink committee of a given slot and committee index. This +// is a spec implementation where state is used as an argument. In case of state retrieval +// becomes expensive, consider using BeaconCommittee below. // // Spec pseudocode definition: // def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) -> Sequence[ValidatorIndex]: @@ -61,15 +65,15 @@ func CommitteeCountAtSlot(state *pb.BeaconState, slot uint64) (uint64, error) { // index=epoch_offset, // count=committees_per_slot * SLOTS_PER_EPOCH, // ) -func BeaconCommittee(state *pb.BeaconState, slot uint64, index uint64) ([]uint64, error) { +func BeaconCommitteeFromState(state *pb.BeaconState, slot uint64, committeeIndex uint64) ([]uint64, error) { epoch := SlotToEpoch(slot) - if featureconfig.Get().EnableNewCache { - seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester) - if err != nil { - return nil, errors.Wrap(err, "could not get seed") - } + seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester) + if err != nil { + return nil, errors.Wrap(err, "could not get seed") + } - indices, err := committeeCache.Committee(slot, seed, index) + if featureconfig.Get().EnableNewCache { + indices, err := committeeCache.Committee(slot, seed, committeeIndex) if err != nil { return nil, errors.Wrap(err, "could not interface with committee cache") } @@ -78,30 +82,34 @@ func BeaconCommittee(state *pb.BeaconState, slot uint64, index uint64) ([]uint64 } } - committeesPerSlot, err := CommitteeCountAtSlot(state, slot) - if err != nil { - return nil, errors.Wrap(err, "could not get committee count at slot") - } - epochOffset := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeesPerSlot - count := committeesPerSlot * params.BeaconConfig().SlotsPerEpoch - - seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester) - if err != nil { - return nil, errors.Wrap(err, "could not get seed") - } - indices, err := ActiveValidatorIndices(state, epoch) if err != nil { return nil, errors.Wrap(err, "could not get active indices") } + return BeaconCommittee(indices, seed, slot, committeeIndex) +} + +// BeaconCommittee returns the crosslink committee of a given slot and committee index. The +// validator indices and seed are provided as an argument rather than a direct implementation +// from the spec definition. Having them as an argument allows for cheaper computation run time. +func BeaconCommittee(validatorIndices []uint64, seed [32]byte, slot uint64, committeeIndex uint64) ([]uint64, error) { if featureconfig.Get().EnableNewCache { - if err := UpdateCommitteeCache(state); err != nil { - return nil, errors.Wrap(err, "could not update committee cache") + indices, err := committeeCache.Committee(slot, seed, committeeIndex) + if err != nil { + return nil, errors.Wrap(err, "could not interface with committee cache") + } + if indices != nil { + return indices, nil } } - return ComputeCommittee(indices, seed, epochOffset, count) + committeesPerSlot := SlotCommitteeCount(uint64(len(validatorIndices))) + + epochOffset := committeeIndex + (slot%params.BeaconConfig().SlotsPerEpoch)*committeesPerSlot + count := committeesPerSlot * params.BeaconConfig().SlotsPerEpoch + + return ComputeCommittee(validatorIndices, seed, epochOffset, count) } // BeaconCommitteeWithoutCache returns the crosslink committee of a given slot and committee index without the @@ -109,11 +117,11 @@ func BeaconCommittee(state *pb.BeaconState, slot uint64, index uint64) ([]uint64 // TODO(3603): Delete this function when issue 3603 closes. func BeaconCommitteeWithoutCache(state *pb.BeaconState, slot uint64, index uint64) ([]uint64, error) { epoch := SlotToEpoch(slot) - - committeesPerSlot, err := CommitteeCountAtSlot(state, slot) + activeValidatorCount, err := ActiveValidatorCount(state, epoch) if err != nil { - return nil, errors.Wrap(err, "could not get committee count at slot") + return nil, err } + committeesPerSlot := SlotCommitteeCount(activeValidatorCount) epochOffset := index + (slot%params.BeaconConfig().SlotsPerEpoch)*committeesPerSlot count := committeesPerSlot * params.BeaconConfig().SlotsPerEpoch @@ -121,7 +129,6 @@ func BeaconCommitteeWithoutCache(state *pb.BeaconState, slot uint64, index uint6 if err != nil { return nil, errors.Wrap(err, "could not get seed") } - indices, err := ActiveValidatorIndices(state, epoch) if err != nil { return nil, errors.Wrap(err, "could not get active indices") @@ -212,7 +219,7 @@ func CommitteeAssignments(state *pb.BeaconState, epoch uint64) (map[uint64]*Comm if epoch > NextEpoch(state) { return nil, nil, fmt.Errorf( "epoch %d can't be greater than next epoch %d", - epoch, + epoch, NextEpoch(state), ) } @@ -229,13 +236,13 @@ func CommitteeAssignments(state *pb.BeaconState, epoch uint64) (map[uint64]*Comm proposerIndexToSlot[i] = slot } - // Each slot in an epoch has a different set of committees. This value is derived from the - // active validator set, which does not change. - numCommitteesPerSlot, err := CommitteeCountAtSlot(state, StartSlot(epoch)) + activeValidatorIndices, err := ActiveValidatorIndices(state, epoch) if err != nil { return nil, nil, err } - + // Each slot in an epoch has a different set of committees. This value is derived from the + // active validator set, which does not change. + numCommitteesPerSlot := SlotCommitteeCount(uint64(len(activeValidatorIndices))) validatorIndexToCommittee := make(map[uint64]*CommitteeAssignmentContainer) // Compute all committees for all slots. @@ -243,7 +250,7 @@ func CommitteeAssignments(state *pb.BeaconState, epoch uint64) (map[uint64]*Comm // Compute committees. for j := uint64(0); j < numCommitteesPerSlot; j++ { slot := startSlot + i - committee, err := BeaconCommittee(state, slot, j /*committee index*/) + committee, err := BeaconCommitteeFromState(state, slot, j /*committee index*/) if err != nil { return nil, nil, err } @@ -316,13 +323,14 @@ func CommitteeAssignment( proposerIndexToSlot[i] = slot } + activeValidatorIndices, err := ActiveValidatorIndices(state, epoch) + if err != nil { + return nil, 0, 0, 0, err + } for slot := startSlot; slot < startSlot+params.BeaconConfig().SlotsPerEpoch; slot++ { - countAtSlot, err := CommitteeCountAtSlot(state, slot) - if err != nil { - return nil, 0, 0, 0, errors.Wrapf(err, "could not get committee count at slot %d", slot) - } + countAtSlot := SlotCommitteeCount(uint64(len(activeValidatorIndices))) for i := uint64(0); i < countAtSlot; i++ { - committee, err := BeaconCommittee(state, slot, i) + committee, err := BeaconCommitteeFromState(state, slot, i) if err != nil { return nil, 0, 0, 0, errors.Wrapf(err, "could not get crosslink committee at slot %d", slot) } @@ -350,8 +358,8 @@ func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error { // VerifyAttestationBitfieldLengths verifies that an attestations aggregation and custody bitfields are // a valid length matching the size of the committee. -func VerifyAttestationBitfieldLengths(bState *pb.BeaconState, att *ethpb.Attestation) error { - committee, err := BeaconCommittee(bState, att.Data.Slot, att.Data.CommitteeIndex) +func VerifyAttestationBitfieldLengths(state *pb.BeaconState, att *ethpb.Attestation) error { + committee, err := BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { return errors.Wrap(err, "could not retrieve beacon committees") } @@ -406,10 +414,9 @@ func UpdateCommitteeCache(state *pb.BeaconState) error { if err != nil { return err } - count, err := CommitteeCountAtSlot(state, StartSlot(epoch)) - if err != nil { - return err - } + + count := SlotCommitteeCount(uint64(len(shuffledIndices))) + seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester) if err != nil { return err diff --git a/beacon-chain/core/helpers/committee_test.go b/beacon-chain/core/helpers/committee_test.go index ab6deb4dfc..2da5b2fcf7 100644 --- a/beacon-chain/core/helpers/committee_test.go +++ b/beacon-chain/core/helpers/committee_test.go @@ -116,7 +116,8 @@ func TestAttestationParticipants_NoCommitteeCache(t *testing.T) { for _, tt := range tests { attestationData.Target = ðpb.Checkpoint{Epoch: 0} attestationData.Slot = tt.attestationSlot - committee, err := BeaconCommittee(state, tt.attestationSlot, 0 /* committee index */) + + committee, err := BeaconCommitteeFromState(state, tt.attestationSlot, 0 /* committee index */) if err != nil { t.Error(err) } @@ -210,7 +211,7 @@ func TestAttestationParticipants_EmptyBitfield(t *testing.T) { } attestationData := ðpb.AttestationData{Target: ðpb.Checkpoint{}} - committee, err := BeaconCommittee(state, attestationData.Slot, attestationData.CommitteeIndex) + committee, err := BeaconCommitteeFromState(state, attestationData.Slot, attestationData.CommitteeIndex) if err != nil { t.Fatal(err) } @@ -394,7 +395,7 @@ func TestCommitteeAssignments_CanRetrieve(t *testing.T) { state := &pb.BeaconState{ Validators: validators, - Slot: 2*params.BeaconConfig().SlotsPerEpoch, // epoch 2 + Slot: 2 * params.BeaconConfig().SlotsPerEpoch, // epoch 2 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), } diff --git a/beacon-chain/core/state/transition.go b/beacon-chain/core/state/transition.go index 8f42d53e06..1b97c7f00a 100644 --- a/beacon-chain/core/state/transition.go +++ b/beacon-chain/core/state/transition.go @@ -610,7 +610,7 @@ func CanProcessEpoch(state *pb.BeaconState) bool { func ProcessEpochPrecompute(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessEpoch") defer span.End() - span.AddAttributes(trace.Int64Attribute("epoch", int64(helpers.SlotToEpoch(state.Slot)))) + span.AddAttributes(trace.Int64Attribute("epoch", int64(helpers.CurrentEpoch(state)))) vp, bp := precompute.New(ctx, state) vp, bp, err := precompute.ProcessAttestations(ctx, state, vp, bp) diff --git a/beacon-chain/core/state/transition_test.go b/beacon-chain/core/state/transition_test.go index de1ad50b34..7869b0c493 100644 --- a/beacon-chain/core/state/transition_test.go +++ b/beacon-chain/core/state/transition_test.go @@ -379,7 +379,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { blockAtt := ðpb.Attestation{ Data: ðpb.AttestationData{ Slot: beaconState.Slot - 1, - Target: ðpb.Checkpoint{Epoch: helpers.SlotToEpoch(beaconState.Slot)}, + Target: ðpb.Checkpoint{Epoch: helpers.CurrentEpoch(beaconState)}, Source: ðpb.Checkpoint{ Epoch: 0, Root: []byte("hello-world"), @@ -387,7 +387,8 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { AggregationBits: aggBits, CustodyBits: custodyBits, } - committee, err := helpers.BeaconCommittee(beaconState, blockAtt.Data.Slot, blockAtt.Data.CommitteeIndex) + + committee, err := helpers.BeaconCommitteeFromState(beaconState, blockAtt.Data.Slot, blockAtt.Data.CommitteeIndex) if err != nil { t.Error(err) } @@ -633,7 +634,7 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) { // Precache the shuffled indices for i := uint64(0); i < committeeCount; i++ { - if _, err := helpers.BeaconCommittee(s, 0, i); err != nil { + if _, err := helpers.BeaconCommitteeFromState(s, 0, i); err != nil { b.Fatal(err) } } @@ -676,7 +677,7 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) { CustodyBits: custodyBits, } - committee, err := helpers.BeaconCommittee(s, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(s, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { t.Error(err) } diff --git a/beacon-chain/operations/attestation_test.go b/beacon-chain/operations/attestation_test.go index 9e4cae047a..0cf7ccd649 100644 --- a/beacon-chain/operations/attestation_test.go +++ b/beacon-chain/operations/attestation_test.go @@ -39,7 +39,7 @@ func TestHandleAttestation_Saves_NewAttestation(t *testing.T) { AggregationBits: bitfield.Bitlist{0xCF, 0xC0, 0xC0, 0xC0, 0x01}, CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, } - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { t.Fatal(err) } @@ -138,9 +138,9 @@ func TestHandleAttestation_Aggregates_LargeNumValidators(t *testing.T) { } // Next up, we compute the committee for the attestation we're testing. - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { - t.Error(err) + t.Fatal(err) } attDataRoot, err := ssz.HashTreeRoot(att.Data) if err != nil { @@ -213,9 +213,9 @@ func TestHandleAttestation_Skips_PreviouslyAggregatedAttestations(t *testing.T) CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, } - committee, err := helpers.BeaconCommittee(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) if err != nil { - t.Error(err) + t.Fatal(err) } aggregationBits := bitfield.NewBitlist(uint64(len(committee))) aggregationBits.SetBitAt(0, true) @@ -353,9 +353,9 @@ func TestRetrieveAttestations_OK(t *testing.T) { AggregationBits: aggBits, CustodyBits: custodyBits, } - committee, err := helpers.BeaconCommittee(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { - t.Error(err) + t.Fatal(err) } attestingIndices, err := helpers.AttestingIndices(att.AggregationBits, committee) if err != nil { diff --git a/beacon-chain/rpc/aggregator/BUILD.bazel b/beacon-chain/rpc/aggregator/BUILD.bazel index 211ae7e4b2..7f345f3834 100644 --- a/beacon-chain/rpc/aggregator/BUILD.bazel +++ b/beacon-chain/rpc/aggregator/BUILD.bazel @@ -8,7 +8,6 @@ go_library( deps = [ "//beacon-chain/blockchain:go_default_library", "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/core/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/sync:go_default_library", "//proto/beacon/rpc/v1:go_default_library", diff --git a/beacon-chain/rpc/aggregator/server.go b/beacon-chain/rpc/aggregator/server.go index 3a3c3e18c8..a6d3120ce9 100644 --- a/beacon-chain/rpc/aggregator/server.go +++ b/beacon-chain/rpc/aggregator/server.go @@ -5,7 +5,6 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" - "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/sync" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" @@ -40,21 +39,6 @@ func (as *Server) SubmitAggregateAndProof(ctx context.Context, req *pb.Aggregati return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") } - headState, err := as.HeadFetcher.HeadState(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) - } - - // Advance slots if it is behind the epoch start of requested slot. - // Ex: head slot is 100, req slot is 150, epoch start of 150 is 128. Advance 100 to 128. - reqEpochStartSlot := helpers.StartSlot(helpers.SlotToEpoch(req.Slot)) - if reqEpochStartSlot > headState.Slot { - headState, err = state.ProcessSlots(ctx, headState, reqEpochStartSlot) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err) - } - } - validatorIndex, exists, err := as.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey)) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get validator index from DB: %v", err) @@ -63,8 +47,22 @@ func (as *Server) SubmitAggregateAndProof(ctx context.Context, req *pb.Aggregati return nil, status.Error(codes.Internal, "Could not locate validator index in DB") } + epoch := helpers.SlotToEpoch(req.Slot) + activeValidatorIndices, err := as.HeadFetcher.HeadValidatorsIndices(epoch) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get validators: %v", err) + } + seed, err := as.HeadFetcher.HeadSeed(epoch) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get seed: %v", err) + } + committee, err := helpers.BeaconCommittee(activeValidatorIndices, seed, req.Slot, req.CommitteeIndex) + if err != nil { + return nil, err + } + // Check if the validator is an aggregator - isAggregator, err := helpers.IsAggregator(headState, req.Slot, req.CommitteeIndex, req.SlotSignature) + isAggregator, err := helpers.IsAggregator(uint64(len(committee)), req.Slot, req.CommitteeIndex, req.SlotSignature) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get aggregator status: %v", err) } diff --git a/beacon-chain/rpc/validator/assignments_test.go b/beacon-chain/rpc/validator/assignments_test.go index d0fd7ecbe2..c7cedad7bd 100644 --- a/beacon-chain/rpc/validator/assignments_test.go +++ b/beacon-chain/rpc/validator/assignments_test.go @@ -306,7 +306,7 @@ func BenchmarkCommitteeAssignment(b *testing.B) { ctx := context.Background() genesis := blk.NewGenesisBlock([]byte{}) - depChainStart := uint64(8192*2) + depChainStart := uint64(8192 * 2) deposits, _, _ := testutil.DeterministicDepositsAndKeys(depChainStart) eth1Data, err := testutil.DeterministicEth1Data(len(deposits)) if err != nil { diff --git a/shared/testutil/block.go b/shared/testutil/block.go index d3483675c3..a9989df53f 100644 --- a/shared/testutil/block.go +++ b/shared/testutil/block.go @@ -205,7 +205,7 @@ func generateAttesterSlashings( attesterSlashings := make([]*ethpb.AttesterSlashing, numSlashings) for i := uint64(0); i < numSlashings; i++ { committeeIndex := rand.Uint64() % params.BeaconConfig().MaxCommitteesPerSlot - committee, err := helpers.BeaconCommittee(bState, bState.Slot, committeeIndex) + committee, err := helpers.BeaconCommitteeFromState(bState, bState.Slot, committeeIndex) if err != nil { return nil, err } @@ -330,10 +330,11 @@ func GenerateAttestations( } } - committeesPerSlot, err := helpers.CommitteeCountAtSlot(bState, slot) + activeValidatorCount, err := helpers.ActiveValidatorCount(bState, currentEpoch) if err != nil { return nil, err } + committeesPerSlot := helpers.SlotCommitteeCount(activeValidatorCount) if numToGen < committeesPerSlot { log.Printf( @@ -361,7 +362,7 @@ func GenerateAttestations( domain := helpers.Domain(bState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconAttester) for c := uint64(0); c < committeesPerSlot && c < numToGen; c++ { - committee, err := helpers.BeaconCommittee(bState, slot, c) + committee, err := helpers.BeaconCommitteeFromState(bState, slot, c) if err != nil { return nil, err } diff --git a/tools/blocktree/main.go b/tools/blocktree/main.go index 820a1cf099..1ec4e54ddf 100644 --- a/tools/blocktree/main.go +++ b/tools/blocktree/main.go @@ -95,7 +95,7 @@ func main() { } // Retrieve attestation indices for _, att := range atts { - committee, err := helpers.BeaconCommittee(state, att.Data.Slot, att.Data.CommitteeIndex) + committee, err := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) if err != nil { panic(err) }