prysm-pulse/beacon-chain/core/state/state_transition.go
2018-11-24 12:57:07 -06:00

174 lines
5.7 KiB
Go

package state
import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/incentives"
"github.com/prysmaticlabs/prysm/beacon-chain/core/types"
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
)
// NewStateTransition computes the new beacon state, given the previous beacon state
// and a beacon block. This method is called during a cycle transition.
// We also check for validator set change transition and compute for new
// committees if necessary during this transition.
func NewStateTransition(
st *types.BeaconState,
block *types.Block,
blockVoteCache utils.BlockVoteCache,
) (*types.BeaconState, error) {
var lastStateRecalculationSlotCycleBack uint64
var err error
newState := st.CopyState()
justifiedStreak := st.JustifiedStreak()
justifiedSlot := st.LastJustifiedSlot()
finalizedSlot := st.LastFinalizedSlot()
timeSinceFinality := block.SlotNumber() - newState.LastFinalizedSlot()
recentBlockHashes := st.RecentBlockHashes()
newState.SetValidators(v.CopyValidators(newState.Validators()))
if st.LastStateRecalculationSlot() < params.BeaconConfig().CycleLength {
lastStateRecalculationSlotCycleBack = 0
} else {
lastStateRecalculationSlotCycleBack = st.LastStateRecalculationSlot() - params.BeaconConfig().CycleLength
}
// walk through all the slots from LastStateRecalculationSlot - cycleLength to
// LastStateRecalculationSlot - 1.
for i := uint64(0); i < params.BeaconConfig().CycleLength; i++ {
var blockVoteBalance uint64
slot := lastStateRecalculationSlotCycleBack + i
blockHash := recentBlockHashes[i]
blockVoteBalance, validators := incentives.TallyVoteBalances(
blockHash,
blockVoteCache,
newState.Validators(),
v.ActiveValidatorIndices(newState.Validators()),
v.TotalActiveValidatorDeposit(newState.Validators()),
timeSinceFinality,
)
newState.SetValidators(validators)
justifiedSlot, finalizedSlot, justifiedStreak = FinalizeAndJustifySlots(
slot,
justifiedSlot,
finalizedSlot,
justifiedStreak,
blockVoteBalance,
v.TotalActiveValidatorDeposit(st.Validators()),
)
}
crossLinks, err := crossLinkCalculations(
newState,
st.PendingAttestations(),
block.SlotNumber(),
)
if err != nil {
return nil, err
}
newState.SetCrossLinks(crossLinks)
newState.SetLastJustifiedSlot(justifiedSlot)
newState.SetLastFinalizedSlot(finalizedSlot)
newState.SetJustifiedStreak(justifiedStreak)
newState.SetLastStateRecalculationSlot(newState.LastStateRecalculationSlot() + params.BeaconConfig().CycleLength)
// Exit the validators when their balance fall below min online deposit size.
newState.SetValidators(v.CheckValidatorMinDeposit(newState.Validators(), block.SlotNumber()))
// Entering new validator set change transition.
if newState.IsValidatorSetChange(block.SlotNumber()) {
newState.SetValidatorSetChangeSlot(newState.LastStateRecalculationSlot())
shardAndCommitteesForSlots, err := validatorSetRecalculations(
newState.ShardAndCommitteesForSlots(),
newState.Validators(),
block.ParentHash(),
)
if err != nil {
return nil, err
}
newState.SetShardAndCommitteesForSlots(shardAndCommitteesForSlots)
period := block.SlotNumber() / params.BeaconConfig().MinWithdrawalPeriod
totalPenalties := newState.PenalizedETH(period)
newState.SetValidators(v.ChangeValidators(block.SlotNumber(), totalPenalties, newState.Validators()))
}
return newState, nil
}
// crossLinkCalculations checks if the proposed shard block has recevied
// 2/3 of the votes. If yes, we update crosslink record to point to
// the proposed shard block with latest beacon chain slot numbers.
func crossLinkCalculations(
st *types.BeaconState,
pendingAttestations []*pb.AggregatedAttestation,
currentSlot uint64,
) ([]*pb.CrosslinkRecord, error) {
slot := st.LastStateRecalculationSlot() + params.BeaconConfig().CycleLength
crossLinkRecords := st.Crosslinks()
for _, attestation := range pendingAttestations {
shardCommittees, err := v.GetShardAndCommitteesForSlot(
st.ShardAndCommitteesForSlots(),
st.LastStateRecalculationSlot(),
attestation.GetSlot(),
)
indices, err := v.AttesterIndices(shardCommittees, attestation)
if err != nil {
return nil, err
}
totalBalance, voteBalance, err := v.VotedBalanceInAttestation(st.Validators(), indices, attestation)
if err != nil {
return nil, err
}
newValidatorSet, err := incentives.ApplyCrosslinkRewardsAndPenalties(
crossLinkRecords,
currentSlot,
indices,
attestation,
st.Validators(),
v.TotalActiveValidatorDeposit(st.Validators()),
totalBalance,
voteBalance,
)
if err != nil {
return nil, err
}
st.SetValidators(newValidatorSet)
crossLinkRecords = UpdateCrosslinks(slot, voteBalance, totalBalance, attestation, crossLinkRecords)
}
return crossLinkRecords, nil
}
// validatorSetRecalculation recomputes the validator set.
func validatorSetRecalculations(
shardAndCommittesForSlots []*pb.ShardAndCommitteeArray,
validators []*pb.ValidatorRecord,
seed [32]byte,
) ([]*pb.ShardAndCommitteeArray, error) {
lastSlot := len(shardAndCommittesForSlots) - 1
lastCommitteeFromLastSlot := len(shardAndCommittesForSlots[lastSlot].ArrayShardAndCommittee) - 1
crosslinkLastShard := shardAndCommittesForSlots[lastSlot].ArrayShardAndCommittee[lastCommitteeFromLastSlot].Shard
crosslinkNextShard := (crosslinkLastShard + 1) % params.BeaconConfig().ShardCount
newShardCommitteeArray, err := v.ShuffleValidatorsToCommittees(
seed,
validators,
crosslinkNextShard,
)
if err != nil {
return nil, err
}
return append(shardAndCommittesForSlots[params.BeaconConfig().CycleLength:], newShardCommitteeArray...), nil
}