Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime Optimizations #4648

Merged
merged 20 commits into from Jan 28, 2020
44 changes: 23 additions & 21 deletions beacon-chain/core/epoch/epoch_processing.go
Expand Up @@ -156,25 +156,26 @@ func ProcessSlashings(state *stateTrie.BeaconState) (*stateTrie.BeaconState, err

// Compute the sum of state slashings
slashings := state.Slashings()
vals := state.Validators()
totalSlashing := uint64(0)
for _, slashing := range slashings {
totalSlashing += slashing
}

// Compute slashing for each validator.
for index, validator := range vals {
correctEpoch := (currentEpoch + exitLength/2) == validator.WithdrawableEpoch
if validator.Slashed && correctEpoch {
// a callback is used here to apply the following actions to all validators
// below equally.
err = state.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) error {
correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch
if val.Slashed && correctEpoch {
minSlashing := mathutil.Min(totalSlashing*3, totalBalance)
increment := params.BeaconConfig().EffectiveBalanceIncrement
penaltyNumerator := validator.EffectiveBalance / increment * minSlashing
penaltyNumerator := val.EffectiveBalance / increment * minSlashing
penalty := penaltyNumerator / totalBalance * increment
if err := helpers.DecreaseBalance(state, uint64(index), penalty); err != nil {
return nil, err
if err := helpers.DecreaseBalance(state, uint64(idx), penalty); err != nil {
return err
}
}
}
return nil
})
return state, err
}

Expand Down Expand Up @@ -225,26 +226,27 @@ func ProcessFinalUpdates(state *stateTrie.BeaconState) (*stateTrie.BeaconState,
}
}

vals := state.Validators()
bals := state.Balances()
// Update effective balances with hysteresis.
for i, v := range vals {
if v == nil {
return nil, fmt.Errorf("validator %d is nil in state", i)
validatorFunc := func(idx int, val *ethpb.Validator) error {
if val == nil {
return fmt.Errorf("validator %d is nil in state", idx)
}
if i >= len(bals) {
return nil, fmt.Errorf("validator index exceeds validator length in state %d >= %d", i, len(state.Balances()))
if idx >= len(bals) {
return fmt.Errorf("validator index exceeds validator length in state %d >= %d", idx, len(state.Balances()))
}
balance := bals[i]
balance := bals[idx]
halfInc := params.BeaconConfig().EffectiveBalanceIncrement / 2
if balance < v.EffectiveBalance || v.EffectiveBalance+3*halfInc < balance {
v.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance
if v.EffectiveBalance > balance-balance%params.BeaconConfig().EffectiveBalanceIncrement {
v.EffectiveBalance = balance - balance%params.BeaconConfig().EffectiveBalanceIncrement
if balance < val.EffectiveBalance || val.EffectiveBalance+3*halfInc < balance {
val.EffectiveBalance = params.BeaconConfig().MaxEffectiveBalance
if val.EffectiveBalance > balance-balance%params.BeaconConfig().EffectiveBalanceIncrement {
val.EffectiveBalance = balance - balance%params.BeaconConfig().EffectiveBalanceIncrement
}
}
return nil
}
if err := state.SetValidators(vals); err != nil {

if err := state.ApplyToEveryValidator(validatorFunc); err != nil {
return nil, err
}

Expand Down
24 changes: 12 additions & 12 deletions beacon-chain/core/epoch/precompute/new.go
Expand Up @@ -16,37 +16,37 @@ func New(ctx context.Context, state *stateTrie.BeaconState) ([]*Validator, *Bala
ctx, span := trace.StartSpan(ctx, "precomputeEpoch.New")
defer span.End()

vals := state.Validators()
vp := make([]*Validator, len(vals))
vp := make([]*Validator, state.NumofValidators())
bp := &Balance{}

currentEpoch := helpers.CurrentEpoch(state)
prevEpoch := helpers.PrevEpoch(state)

for i, v := range vals {
state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
// Was validator withdrawable or slashed
withdrawable := currentEpoch >= v.WithdrawableEpoch
withdrawable := currentEpoch >= val.WithdrawableEpoch()
p := &Validator{
IsSlashed: v.Slashed,
IsSlashed: val.Slashed(),
IsWithdrawableCurrentEpoch: withdrawable,
CurrentEpochEffectiveBalance: v.EffectiveBalance,
CurrentEpochEffectiveBalance: val.EffectiveBalance(),
}
// Was validator active current epoch
if helpers.IsActiveValidator(v, currentEpoch) {
if helpers.IsActiveValidatorUsingTrie(val, currentEpoch) {
p.IsActiveCurrentEpoch = true
bp.CurrentEpoch += v.EffectiveBalance
bp.CurrentEpoch += val.EffectiveBalance()
}
// Was validator active previous epoch
if helpers.IsActiveValidator(v, prevEpoch) {
if helpers.IsActiveValidatorUsingTrie(val, prevEpoch) {
p.IsActivePrevEpoch = true
bp.PrevEpoch += v.EffectiveBalance
bp.PrevEpoch += val.EffectiveBalance()
}
// Set inclusion slot and inclusion distance to be max, they will be compared and replaced
// with the lower values
p.InclusionSlot = params.BeaconConfig().FarFutureEpoch
p.InclusionDistance = params.BeaconConfig().FarFutureEpoch

vp[i] = p
}
vp[idx] = p
return nil
})
return vp, bp
}
17 changes: 9 additions & 8 deletions beacon-chain/core/epoch/precompute/slashing.go
@@ -1,6 +1,7 @@
package precompute

import (
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/shared/mathutil"
Expand All @@ -20,19 +21,19 @@ func ProcessSlashingsPrecompute(state *stateTrie.BeaconState, p *Balance) error
totalSlashing += slashing
}

vals := state.Validators()
// Compute slashing for each validator.
for index, validator := range vals {
correctEpoch := (currentEpoch + exitLength/2) == validator.WithdrawableEpoch
if validator.Slashed && correctEpoch {
validatorFunc := func(idx int, val *ethpb.Validator) error {
correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch
if val.Slashed && correctEpoch {
minSlashing := mathutil.Min(totalSlashing*3, p.CurrentEpoch)
increment := params.BeaconConfig().EffectiveBalanceIncrement
penaltyNumerator := validator.EffectiveBalance / increment * minSlashing
penaltyNumerator := val.EffectiveBalance / increment * minSlashing
penalty := penaltyNumerator / p.CurrentEpoch * increment
if err := helpers.DecreaseBalance(state, uint64(index), penalty); err != nil {
if err := helpers.DecreaseBalance(state, uint64(idx), penalty); err != nil {
return err
}
}
return nil
}
return nil

return state.ApplyToEveryValidator(validatorFunc)
}
47 changes: 13 additions & 34 deletions beacon-chain/core/helpers/committee.go
Expand Up @@ -115,22 +115,18 @@ func BeaconCommittee(validatorIndices []uint64, seed [32]byte, slot uint64, comm
// TODO(3603): Delete this function when issue 3603 closes.
func BeaconCommitteeWithoutCache(state *stateTrie.BeaconState, slot uint64, index uint64) ([]uint64, error) {
epoch := SlotToEpoch(slot)
activeValidatorCount, err := ActiveValidatorCount(state, epoch)
indices, err := ActiveValidatorIndices(state, epoch)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "could not get active indices")
}
committeesPerSlot := SlotCommitteeCount(activeValidatorCount)
committeesPerSlot := SlotCommitteeCount(uint64(len(indices)))
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 ComputeCommittee(indices, seed, epochOffset, count)
}
Expand Down Expand Up @@ -161,15 +157,8 @@ func ComputeCommittee(

// Save the shuffled indices in cache, this is only needed once per epoch or once per new committee index.
shuffledIndices := make([]uint64, end-start)
for i := start; i < end; i++ {
permutedIndex, err := ShuffledIndex(i, validatorCount, seed)
if err != nil {
return []uint64{}, errors.Wrapf(err, "could not get shuffled index at index %d", i)
}
shuffledIndices[i-start] = indices[permutedIndex]
}

return shuffledIndices, nil
copy(shuffledIndices, indices[start:end])
return UnshuffleList(shuffledIndices, seed)
}

// AttestingIndices returns the attesting participants indices from the attestation data. The
Expand All @@ -187,7 +176,7 @@ func ComputeCommittee(
// return set(index for i, index in enumerate(committee) if bits[i])
func AttestingIndices(bf bitfield.Bitfield, committee []uint64) ([]uint64, error) {
indices := make([]uint64, 0, len(committee))
indicesSet := make(map[uint64]bool)
indicesSet := make(map[uint64]bool, len(committee))
for i, idx := range committee {
if !indicesSet[idx] {
if bf.BitAt(uint64(i)) {
Expand Down Expand Up @@ -385,25 +374,15 @@ func ShuffledIndices(state *stateTrie.BeaconState, epoch uint64) ([]uint64, erro
return nil, errors.Wrapf(err, "could not get seed for epoch %d", epoch)
}

vals := state.Validators()
indices := make([]uint64, 0, len(vals))
for i, v := range vals {
if IsActiveValidator(v, epoch) {
indices = append(indices, uint64(i))
indices := make([]uint64, 0, state.NumofValidators())
state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
if IsActiveValidatorUsingTrie(val, epoch) {
indices = append(indices, uint64(idx))
}
}

validatorCount := uint64(len(indices))
shuffledIndices := make([]uint64, validatorCount)
for i := 0; i < len(shuffledIndices); i++ {
permutedIndex, err := ShuffledIndex(uint64(i), validatorCount, seed)
if err != nil {
return []uint64{}, errors.Wrapf(err, "could not get shuffled index at index %d", i)
}
shuffledIndices[i] = indices[permutedIndex]
}
return nil
})

return shuffledIndices, nil
return UnshuffleList(indices, seed)
}

// UpdateCommitteeCache gets called at the beginning of every epoch to cache the committee shuffled indices
Expand Down
18 changes: 11 additions & 7 deletions beacon-chain/core/helpers/rewards_penalties.go
Expand Up @@ -14,10 +14,14 @@ import (
// """
// return Gwei(max(1, sum([state.validators[index].effective_balance for index in indices])))
func TotalBalance(state *stateTrie.BeaconState, indices []uint64) uint64 {
vals := state.Validators()
total := uint64(0)

for _, idx := range indices {
total += vals[idx].EffectiveBalance
val, err := state.ValidatorAtIndexReadOnly(idx)
if err != nil {
continue
}
total += val.EffectiveBalance()
}

// Return 1 Gwei minimum to avoid divisions by zero
Expand All @@ -38,13 +42,13 @@ func TotalBalance(state *stateTrie.BeaconState, indices []uint64) uint64 {
// """
// return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state))))
func TotalActiveBalance(state *stateTrie.BeaconState) (uint64, error) {
vals := state.Validators()
total := uint64(0)
for i, v := range vals {
if IsActiveValidator(v, SlotToEpoch(state.Slot())) {
total += vals[i].EffectiveBalance
state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
if IsActiveValidatorUsingTrie(val, SlotToEpoch(state.Slot())) {
total += val.EffectiveBalance()
}
}
return nil
})
return total, nil
}

Expand Down
16 changes: 9 additions & 7 deletions beacon-chain/core/helpers/shuffle.go
Expand Up @@ -163,6 +163,7 @@ func innerShuffleList(input []uint64, seed [32]byte, shuffle bool) ([]uint64, er
len(input))
}
rounds := uint8(params.BeaconConfig().ShuffleRoundCount)
hashfunc := hashutil.CustomSHA256Hasher()
if rounds == 0 {
return input, nil
}
Expand All @@ -175,23 +176,23 @@ func innerShuffleList(input []uint64, seed [32]byte, shuffle bool) ([]uint64, er
copy(buf[:seedSize], seed[:])
for {
buf[seedSize] = r
ph := hashutil.Hash(buf[:pivotViewSize])
ph := hashfunc(buf[:pivotViewSize])
pivot := bytesutil.FromBytes8(ph[:8]) % listSize
mirror := (pivot + 1) >> 1
binary.LittleEndian.PutUint32(buf[pivotViewSize:], uint32(pivot>>8))
source := hashutil.Hash(buf)
source := hashfunc(buf)
byteV := source[(pivot&0xff)>>3]
for i, j := uint64(0), pivot; i < mirror; i, j = i+1, j-1 {
byteV, source = swapOrNot(buf, byteV, i, input, j, source)
byteV, source = swapOrNot(buf, byteV, i, input, j, source, hashfunc)
}
// Now repeat, but for the part after the pivot.
mirror = (pivot + listSize + 1) >> 1
end := listSize - 1
binary.LittleEndian.PutUint32(buf[pivotViewSize:], uint32(end>>8))
source = hashutil.Hash(buf)
source = hashfunc(buf)
byteV = source[(end&0xff)>>3]
for i, j := pivot+1, end; i < mirror; i, j = i+1, j-1 {
byteV, source = swapOrNot(buf, byteV, i, input, j, source)
byteV, source = swapOrNot(buf, byteV, i, input, j, source, hashfunc)
}
if shuffle {
r++
Expand All @@ -210,11 +211,12 @@ func innerShuffleList(input []uint64, seed [32]byte, shuffle bool) ([]uint64, er

// swapOrNot describes the main algorithm behind the shuffle where we swap bytes in the inputted value
// depending on if the conditions are met.
func swapOrNot(buf []byte, byteV byte, i uint64, input []uint64, j uint64, source [32]byte) (byte, [32]byte) {
func swapOrNot(buf []byte, byteV byte, i uint64, input []uint64,
j uint64, source [32]byte, hashFunc func([]byte) [32]byte) (byte, [32]byte) {
if j&0xff == 0xff {
// just overwrite the last part of the buffer, reuse the start (seed, round)
binary.LittleEndian.PutUint32(buf[pivotViewSize:], uint32(j>>8))
source = hashutil.Hash(buf)
source = hashFunc(buf)
}
if j&0x7 == 0x7 {
byteV = source[(j&0xff)>>3]
Expand Down
31 changes: 19 additions & 12 deletions beacon-chain/core/helpers/validators.go
Expand Up @@ -22,8 +22,16 @@ import (
// """
// return validator.activation_epoch <= epoch < validator.exit_epoch
func IsActiveValidator(validator *ethpb.Validator, epoch uint64) bool {
return validator.ActivationEpoch <= epoch &&
epoch < validator.ExitEpoch
return checkValidatorActiveStatus(validator.ActivationEpoch, validator.ExitEpoch, epoch)
}

// IsActiveValidatorUsingTrie checks if a read only validator is active.
func IsActiveValidatorUsingTrie(validator *stateTrie.ReadOnlyValidator, epoch uint64) bool {
return checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
}

func checkValidatorActiveStatus(activationEpoch uint64, exitEpoch uint64, epoch uint64) bool {
return activationEpoch <= epoch && epoch < exitEpoch
}

// IsSlashableValidator returns the boolean value on whether the validator
Expand Down Expand Up @@ -69,13 +77,13 @@ func ActiveValidatorIndices(state *stateTrie.BeaconState, epoch uint64) ([]uint6
if activeIndices != nil {
return activeIndices, nil
}
vals := state.Validators()
var indices []uint64
for i, v := range vals {
if IsActiveValidator(v, epoch) {
indices = append(indices, uint64(i))
state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
if IsActiveValidatorUsingTrie(val, epoch) {
indices = append(indices, uint64(idx))
}
}
return nil
})

if err := UpdateCommitteeCache(state, epoch); err != nil {
return nil, errors.Wrap(err, "could not update committee cache")
Expand All @@ -87,14 +95,13 @@ func ActiveValidatorIndices(state *stateTrie.BeaconState, epoch uint64) ([]uint6
// ActiveValidatorCount returns the number of active validators in the state
// at the given epoch.
func ActiveValidatorCount(state *stateTrie.BeaconState, epoch uint64) (uint64, error) {
vals := state.Validators()
count := uint64(0)
for _, v := range vals {
if IsActiveValidator(v, epoch) {
state.ReadFromEveryValidator(func(idx int, val *stateTrie.ReadOnlyValidator) error {
if IsActiveValidatorUsingTrie(val, epoch) {
count++
}
}

return nil
})
return count, nil
}

Expand Down