prysm-pulse/beacon-chain/core/state/state.go

217 lines
7.8 KiB
Go
Raw Normal View History

package state
import (
2019-01-05 05:39:34 +00:00
"encoding/binary"
"fmt"
"github.com/gogo/protobuf/proto"
2019-01-05 05:39:34 +00:00
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
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"
pbcomm "github.com/prysmaticlabs/prysm/proto/common"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
// InitialBeaconState gets called when DepositsForChainStart count of
// full deposits were made to the deposit contract and the ChainStart log gets emitted.
func InitialBeaconState(
initialValidatorDeposits []*pb.Deposit,
genesisTime uint64,
processedPowReceiptRoot []byte) (*pb.BeaconState, error) {
latestRandaoMixes := make([][]byte,
params.BeaconConfig().LatestRandaoMixesLength)
for i := 0; i < len(latestRandaoMixes); i++ {
latestRandaoMixes[i] = params.BeaconConfig().ZeroHash[:]
}
latestVDFOutputs := make([][]byte,
params.BeaconConfig().LatestRandaoMixesLength/params.BeaconConfig().EpochLength)
for i := 0; i < len(latestVDFOutputs); i++ {
latestVDFOutputs[i] = params.BeaconConfig().ZeroHash[:]
}
latestCrosslinks := make([]*pb.CrosslinkRecord, params.BeaconConfig().ShardCount)
for i := 0; i < len(latestCrosslinks); i++ {
latestCrosslinks[i] = &pb.CrosslinkRecord{
Slot: params.BeaconConfig().InitialSlotNumber,
ShardBlockRootHash32: params.BeaconConfig().ZeroHash[:],
}
}
latestBlockRoots := make([][]byte, params.BeaconConfig().LatestBlockRootsLength)
for i := 0; i < len(latestBlockRoots); i++ {
latestBlockRoots[i] = params.BeaconConfig().ZeroHash[:]
}
state := &pb.BeaconState{
// Misc fields.
Slot: params.BeaconConfig().InitialSlotNumber,
GenesisTime: genesisTime,
ForkData: &pb.ForkData{
PreForkVersion: params.BeaconConfig().InitialForkVersion,
PostForkVersion: params.BeaconConfig().InitialForkVersion,
ForkSlot: params.BeaconConfig().InitialSlotNumber,
},
// Validator registry fields.
ValidatorRegistry: []*pb.ValidatorRecord{},
ValidatorBalances: []uint64{},
ValidatorRegistryLastChangeSlot: params.BeaconConfig().InitialSlotNumber,
ValidatorRegistryExitCount: 0,
ValidatorRegistryDeltaChainTipHash32: params.BeaconConfig().ZeroHash[:],
// Randomness and committees.
LatestRandaoMixesHash32S: latestRandaoMixes,
LatestVdfOutputs: latestVDFOutputs,
ShardAndCommitteesAtSlots: []*pb.ShardAndCommitteeArray{},
PersistentCommittees: []*pbcomm.Uint32List{},
PersistentCommitteeReassignments: []*pb.ShardReassignmentRecord{},
// Proof of custody.
// Place holder, proof of custody challenge is defined in phase 1.
// This list will remain empty through out phase 0.
PocChallenges: []*pb.ProofOfCustodyChallenge{},
// Finality.
PreviousJustifiedSlot: params.BeaconConfig().InitialSlotNumber,
JustifiedSlot: params.BeaconConfig().InitialSlotNumber,
JustificationBitfield: 0,
FinalizedSlot: params.BeaconConfig().InitialSlotNumber,
// Recent state.
LatestCrosslinks: latestCrosslinks,
LatestBlockRootHash32S: latestBlockRoots,
LatestPenalizedExitBalances: []uint64{},
LatestAttestations: []*pb.PendingAttestationRecord{},
BatchedBlockRootHash32S: [][]byte{},
// PoW receipt root.
ProcessedPowReceiptRootHash32: processedPowReceiptRoot,
CandidatePowReceiptRoots: []*pb.CandidatePoWReceiptRootRecord{},
}
// Set initial deposits and activations.
var validatorIndex uint32
var err error
for _, deposit := range initialValidatorDeposits {
2019-01-05 05:39:34 +00:00
depositData := deposit.DepositData
depositInput, err := b.DecodeDepositInput(depositData)
if err != nil {
return nil, fmt.Errorf("could not decode deposit input: %v", err)
}
// depositData consists of depositInput []byte + depositValue [8]byte +
// depositTimestamp [8]byte.
depositValue := depositData[len(depositData)-16 : len(depositData)-8]
state, validatorIndex, err = v.ProcessDeposit(
state,
2019-01-05 05:39:34 +00:00
depositInput.Pubkey,
binary.BigEndian.Uint64(depositValue),
depositInput.ProofOfPossession,
depositInput.WithdrawalCredentialsHash32,
depositInput.RandaoCommitmentHash32,
depositInput.PocCommitment,
)
if err != nil {
return nil, fmt.Errorf("could not process validator deposit: %v", err)
}
if v.EffectiveBalance(state, validatorIndex) ==
params.BeaconConfig().MaxDepositInGwei {
state, err = v.UpdateStatus(state, validatorIndex, pb.ValidatorRecord_ACTIVE)
if err != nil {
return nil, fmt.Errorf("could not update validator status: %v", err)
}
}
}
// Set initial committee shuffling.
initialShuffling, err := v.ShuffleValidatorRegistryToCommittees(
params.BeaconConfig().ZeroHash,
state.ValidatorRegistry,
0,
)
if err != nil {
return nil, fmt.Errorf("could not shuffle initial committee: %v", err)
}
state.ShardAndCommitteesAtSlots = append(initialShuffling, initialShuffling...)
// Set initial persistent shuffling.
activeValidatorIndices := v.ActiveValidatorIndices(state.ValidatorRegistry)
committees := utils.SplitIndices(activeValidatorIndices, params.BeaconConfig().ShardCount)
persistentCommittees := make([]*pbcomm.Uint32List, params.BeaconConfig().ShardCount)
for i := 0; i < len(persistentCommittees); i++ {
persistentCommittees[i] = &pbcomm.Uint32List{List: committees[i]}
}
state.PersistentCommittees = persistentCommittees
return state, nil
}
// Hash the beacon state data structure.
func Hash(state *pb.BeaconState) ([32]byte, error) {
data, err := proto.Marshal(state)
if err != nil {
return [32]byte{}, fmt.Errorf("could not marshal beacon state: %v", err)
}
return hashutil.Hash(data), nil
}
// CalculateNewBlockHashes builds a new slice of recent block hashes with the
// provided block and the parent slot number.
//
// The algorithm is:
// 1) shift the array by block.SlotNumber - parentSlot (i.e. truncate the
// first by the number of slots that have occurred between the block and
// its parent).
//
// 2) fill the array with the parent block hash for all values between the parent
// slot and the block slot.
//
// Computation of the state hash depends on this feature that slots with
// missing blocks have the block hash of the next block hash in the chain.
//
// For example, if we have a segment of recent block hashes that look like this
// [0xF, 0x7, 0x0, 0x0, 0x5]
//
// Where 0x0 is an empty or missing hash where no block was produced in the
// alloted slot. When storing the list (or at least when computing the hash of
// the active state), the list should be back-filled as such:
//
// [0xF, 0x7, 0x5, 0x5, 0x5]
func CalculateNewBlockHashes(state *pb.BeaconState, block *pb.BeaconBlock, parentSlot uint64) ([][]byte, error) {
distance := block.Slot - parentSlot
existing := state.LatestBlockRootHash32S
update := existing[distance:]
for len(update) < 2*int(params.BeaconConfig().CycleLength) {
update = append(update, block.ParentRootHash32)
}
return update, nil
}
// IsValidatorSetChange checks if a validator set change transition can be processed. At that point,
// validator shuffle will occur.
func IsValidatorSetChange(state *pb.BeaconState, slotNumber uint64) bool {
if state.FinalizedSlot <= state.ValidatorRegistryLastChangeSlot {
return false
}
if slotNumber-state.ValidatorRegistryLastChangeSlot < params.BeaconConfig().MinValidatorSetChangeInterval {
return false
}
shardProcessed := map[uint64]bool{}
for _, shardAndCommittee := range state.ShardAndCommitteesAtSlots {
for _, committee := range shardAndCommittee.ArrayShardAndCommittee {
shardProcessed[committee.Shard] = true
}
}
crosslinks := state.LatestCrosslinks
for shard := range shardProcessed {
if state.ValidatorRegistryLastChangeSlot >= crosslinks[shard].Slot {
return false
}
}
return true
}