func (s *State) ExitValidator(index uint32, status uint64, c *config.Config) error {
	validator := s.ValidatorRegistry[index]
	prevStatus := validator.Status
	if prevStatus == ExitedWithPenalty {
		return nil
	}
	validator.Status = status
	validator.LatestStatusChangeSlot = s.Slot
	if status == ExitedWithPenalty {
		whistleblowerIndex, err := s.GetBeaconProposerIndex(s.Slot-1, c)
		if err != nil {
			return err
		}
		whistleblowerReward := s.GetEffectiveBalance(index, c) / c.WhistleblowerRewardQuotient
		s.ValidatorBalances[whistleblowerIndex] += whistleblowerReward
		s.ValidatorBalances[index] -= whistleblowerReward
	}
	s.ValidatorRegistryExitCount++
	validator.ExitCount = s.ValidatorRegistryExitCount
	deltaChainTip, err := GetNewValidatorRegistryDeltaChainTip(s.ValidatorRegistryDeltaChainTip, index, validator.Pubkey, ExitFlag)
	if err != nil {
		return err
	}
	s.ValidatorRegistryDeltaChainTip = deltaChainTip
	for i := range s.Proposals {
		participation := s.Proposals[i].Participation
		if participation[index/8]&(1<<uint(index%8)) != 0 {
			s.Proposals[i].Participation[index/8] &= ^(1 << uint(index%8))
		}
	}
	return nil
}
ExitValidator handles state changes when a validator exits.
package primitives
import (
	"bytes"
	"fmt"
	"github.com/pkg/errors"
	"github.com/prysmaticlabs/go-ssz"
	"github.com/phoreproject/synapse/beacon/config"
	"github.com/phoreproject/synapse/bls"
	"github.com/phoreproject/synapse/chainhash"
	"github.com/phoreproject/synapse/pb"
)
type ValidatorRegistryDeltaBlock struct {
	LatestRegistryDeltaRoot chainhash.Hash
	ValidatorIndex          uint32
	Pubkey                  [96]byte
	Flag                    uint64
}
type ForkData struct {
	PreForkVersion uint64
	PostForkVersion uint64
	ForkSlotNumber uint64
}
func (f *ForkData) GetVersionForSlot(slot uint64) uint64 {
	if slot < f.ForkSlotNumber {
		return f.PreForkVersion
	}
	return f.PostForkVersion
}
func (f ForkData) Copy() ForkData {
	return ForkData{PreForkVersion: f.PreForkVersion, PostForkVersion: f.PostForkVersion, ForkSlotNumber: f.ForkSlotNumber}
}
func (f ForkData) ToProto() *pb.ForkData {
	return &pb.ForkData{
		PreForkVersion:  f.PreForkVersion,
		PostForkVersion: f.PostForkVersion,
		ForkSlot:        f.ForkSlotNumber,
	}
}
func ForkDataFromProto(data *pb.ForkData) (*ForkData, error) {
	return &ForkData{
		PreForkVersion:  data.PreForkVersion,
		PostForkVersion: data.PostForkVersion,
		ForkSlotNumber:  data.ForkSlot,
	}, nil
}
type Crosslink struct {
	Slot uint64
	ShardBlockHash chainhash.Hash
	ShardStateHash chainhash.Hash
}
func (c *Crosslink) ToProto() *pb.Crosslink {
	return &pb.Crosslink{
		Slot:           c.Slot,
		ShardBlockHash: c.ShardBlockHash[:],
		ShardStateHash: c.ShardStateHash[:],
	}
}
func CrosslinkFromProto(crosslink *pb.Crosslink) (*Crosslink, error) {
	c := &Crosslink{Slot: crosslink.Slot}
	err := c.ShardBlockHash.SetBytes(crosslink.ShardBlockHash)
	if err != nil {
		return nil, err
	}
	err = c.ShardStateHash.SetBytes(crosslink.ShardStateHash)
	if err != nil {
		return nil, err
	}
	return c, nil
}
type ShardAndCommittee struct {
	Shard uint64
	Committee []uint32
	TotalValidatorCount uint64
}
func (sc *ShardAndCommittee) Copy() ShardAndCommittee {
	newSc := *sc
	newSc.Committee = append([]uint32{}, sc.Committee...)
	return newSc
}
func (sc *ShardAndCommittee) ToProto() *pb.ShardCommittee {
	return &pb.ShardCommittee{
		Shard:               sc.Shard,
		Committee:           sc.Committee,
		TotalValidatorCount: sc.TotalValidatorCount,
	}
}
func ShardAndCommitteeFromProto(committee *pb.ShardCommittee) (*ShardAndCommittee, error) {
	sc := &ShardAndCommittee{
		Shard:               committee.Shard,
		TotalValidatorCount: committee.TotalValidatorCount,
	}
	sc.Committee = append([]uint32{}, committee.Committee...)
	return sc, nil
}
type State struct {
	Slot uint64
	EpochIndex uint64
	GenesisTime uint64
	ForkData ForkData
	ValidatorRegistry []Validator
	ValidatorBalances []uint64
	ValidatorRegistryLatestChangeEpoch uint64
	ValidatorRegistryExitCount uint64
	ValidatorRegistryDeltaChainTip chainhash.Hash
	RandaoMix chainhash.Hash
	NextRandaoMix chainhash.Hash
	ShardAndCommitteeForSlots [][]ShardAndCommittee
	PreviousJustifiedEpoch uint64
	JustifiedEpoch         uint64
	JustificationBitfield  uint64
	FinalizedEpoch         uint64
	LatestCrosslinks          []Crosslink
	PreviousCrosslinks        []Crosslink
	LatestBlockHashes         []chainhash.Hash
	CurrentEpochAttestations  []PendingAttestation
	PreviousEpochAttestations []PendingAttestation
	BatchedBlockRoots         []chainhash.Hash
	ShardRegistry []chainhash.Hash
	Proposals     []ActiveProposal
	PendingVotes  []AggregatedVote
}
func (s *State) Copy() State {
	newValidatorRegistry := make([]Validator, len(s.ValidatorRegistry))
	for i := range s.ValidatorRegistry {
		newValidatorRegistry[i] = s.ValidatorRegistry[i].Copy()
	}
	newValidatorBalances := make([]uint64, len(s.ValidatorBalances))
	for i := range s.ValidatorBalances {
		newValidatorBalances[i] = s.ValidatorBalances[i]
	}
	newShardRegistry := make([]chainhash.Hash, len(s.ShardRegistry))
	for i := range s.ShardRegistry {
		copy(newShardRegistry[i][:], s.ShardRegistry[i][:])
	}
	newProposals := make([]ActiveProposal, len(s.Proposals))
	for i := range s.Proposals {
		newProposals[i] = s.Proposals[i].Copy()
	}
	newPendingVotes := make([]AggregatedVote, len(s.PendingVotes))
	for i := range s.PendingVotes {
		newPendingVotes[i] = s.PendingVotes[i].Copy()
	}
	var newValidatorRegistryDeltaChainTip chainhash.Hash
	var newRandaoMix chainhash.Hash
	var newNextRandaoMix chainhash.Hash
	copy(newValidatorRegistryDeltaChainTip[:], s.ValidatorRegistryDeltaChainTip[:])
	copy(newRandaoMix[:], s.RandaoMix[:])
	copy(newNextRandaoMix[:], s.NextRandaoMix[:])
	newShardAndCommitteeForSlots := make([][]ShardAndCommittee, len(s.ShardAndCommitteeForSlots))
	for i, slot := range s.ShardAndCommitteeForSlots {
		newShardAndCommitteeForSlots[i] = make([]ShardAndCommittee, len(slot))
		for n, committee := range slot {
			newShardAndCommitteeForSlots[i][n] = committee.Copy()
		}
	}
	newLatestCrosslinks := make([]Crosslink, len(s.LatestCrosslinks))
	copy(newLatestCrosslinks, s.LatestCrosslinks)
	newPreviousCrosslinks := make([]Crosslink, len(s.PreviousCrosslinks))
	copy(newPreviousCrosslinks, s.PreviousCrosslinks)
	newLatestBlockHashes := make([]chainhash.Hash, len(s.LatestBlockHashes))
	copy(newLatestBlockHashes, s.LatestBlockHashes)
	newCurrentAttestations := make([]PendingAttestation, len(s.CurrentEpochAttestations))
	for i := range s.CurrentEpochAttestations {
		newCurrentAttestations[i] = s.CurrentEpochAttestations[i].Copy()
	}
	newPreviousAttestations := make([]PendingAttestation, len(s.PreviousEpochAttestations))
	for i := range s.PreviousEpochAttestations {
		newPreviousAttestations[i] = s.PreviousEpochAttestations[i].Copy()
	}
	newBatchedBlockRoots := make([]chainhash.Hash, len(s.BatchedBlockRoots))
	copy(newBatchedBlockRoots, s.BatchedBlockRoots)
	newState := State{
		Slot:                               s.Slot,
		GenesisTime:                        s.GenesisTime,
		ForkData:                           s.ForkData.Copy(),
		EpochIndex:                         s.EpochIndex,
		ValidatorRegistry:                  newValidatorRegistry,
		ValidatorBalances:                  newValidatorBalances,
		ValidatorRegistryLatestChangeEpoch: s.ValidatorRegistryLatestChangeEpoch,
		ValidatorRegistryExitCount:         s.ValidatorRegistryExitCount,
		ValidatorRegistryDeltaChainTip:     newValidatorRegistryDeltaChainTip,
		RandaoMix:                          newRandaoMix,
		NextRandaoMix:                      newNextRandaoMix,
		ShardAndCommitteeForSlots:          newShardAndCommitteeForSlots,
		PreviousJustifiedEpoch:             s.PreviousJustifiedEpoch,
		JustifiedEpoch:                     s.JustifiedEpoch,
		JustificationBitfield:              s.JustificationBitfield,
		FinalizedEpoch:                     s.FinalizedEpoch,
		LatestCrosslinks:                   newLatestCrosslinks,
		PreviousCrosslinks:                 newPreviousCrosslinks,
		LatestBlockHashes:                  newLatestBlockHashes,
		PreviousEpochAttestations:          newPreviousAttestations,
		CurrentEpochAttestations:           newCurrentAttestations,
		BatchedBlockRoots:                  newBatchedBlockRoots,
		ShardRegistry:                      newShardRegistry,
		PendingVotes:                       newPendingVotes,
		Proposals:                          newProposals,
	}
	return newState
}
func (s *State) ToProto() *pb.State {
	validatorRegistry := make([]*pb.Validator, len(s.ValidatorRegistry))
	shardCommittees := make([]*pb.ShardCommitteesForSlot, len(s.ShardAndCommitteeForSlots))
	latestCrosslinks := make([]*pb.Crosslink, len(s.LatestCrosslinks))
	previousCrosslinks := make([]*pb.Crosslink, len(s.PreviousCrosslinks))
	latestBlockHashes := make([][]byte, len(s.LatestBlockHashes))
	currentAttestations := make([]*pb.PendingAttestation, len(s.CurrentEpochAttestations))
	previousAttestations := make([]*pb.PendingAttestation, len(s.PreviousEpochAttestations))
	batchedBlockRoots := make([][]byte, len(s.BatchedBlockRoots))
	proposals := make([]*pb.ActiveProposal, len(s.Proposals))
	shardRegistry := make([][]byte, len(s.ShardRegistry))
	pendingVotes := make([]*pb.AggregatedVote, len(s.PendingVotes))
	for i := range validatorRegistry {
		validatorRegistry[i] = s.ValidatorRegistry[i].ToProto()
	}
	for i := range shardCommittees {
		committees := make([]*pb.ShardCommittee, len(s.ShardAndCommitteeForSlots[i]))
		for j := range committees {
			committees[j] = s.ShardAndCommitteeForSlots[i][j].ToProto()
		}
		shardCommittees[i] = &pb.ShardCommitteesForSlot{
			Committees: committees,
		}
	}
	for i := range latestCrosslinks {
		latestCrosslinks[i] = s.LatestCrosslinks[i].ToProto()
	}
	for i := range previousCrosslinks {
		previousCrosslinks[i] = s.PreviousCrosslinks[i].ToProto()
	}
	for i := range latestBlockHashes {
		latestBlockHashes[i] = s.LatestBlockHashes[i][:]
	}
	for i := range currentAttestations {
		currentAttestations[i] = s.CurrentEpochAttestations[i].ToProto()
	}
	for i := range previousAttestations {
		previousAttestations[i] = s.PreviousEpochAttestations[i].ToProto()
	}
	for i := range proposals {
		proposals[i] = s.Proposals[i].ToProto()
	}
	for i := range pendingVotes {
		pendingVotes[i] = s.PendingVotes[i].ToProto()
	}
	for i := range shardRegistry {
		shardRegistry[i] = s.ShardRegistry[i][:]
	}
	for i := range batchedBlockRoots {
		batchedBlockRoots[i] = s.BatchedBlockRoots[i][:]
	}
	return &pb.State{
		Slot:                               s.Slot,
		GenesisTime:                        s.GenesisTime,
		EpochIndex:                         s.EpochIndex,
		ForkData:                           s.ForkData.ToProto(),
		ValidatorRegistry:                  validatorRegistry,
		ValidatorBalances:                  s.ValidatorBalances,
		ValidatorRegistryLatestChangeEpoch: s.ValidatorRegistryLatestChangeEpoch,
		ValidatorRegistryDeltaChainTip:     s.ValidatorRegistryDeltaChainTip[:],
		ValidatorRegistryExitCount:         s.ValidatorRegistryExitCount,
		RandaoMix:                          s.RandaoMix[:],
		NextRandaoMix:                      s.NextRandaoMix[:],
		ShardCommittees:                    shardCommittees,
		PreviousJustifiedEpoch:             s.PreviousJustifiedEpoch,
		JustifiedEpoch:                     s.JustifiedEpoch,
		JustificationBitField:              s.JustificationBitfield,
		FinalizedEpoch:                     s.FinalizedEpoch,
		LatestCrosslinks:                   latestCrosslinks,
		PreviousCrosslinks:                 previousCrosslinks,
		LatestBlockHashes:                  latestBlockHashes,
		CurrentEpochAttestations:           currentAttestations,
		PreviousEpochAttestations:          previousAttestations,
		BatchedBlockRoots:                  batchedBlockRoots,
		ShardRegistry:                      shardRegistry,
		PendingVotes:                       pendingVotes,
		Proposals:                          proposals,
	}
}
func StateFromProto(s *pb.State) (*State, error) {
	validatorRegistry := make([]Validator, len(s.ValidatorRegistry))
	shardAndCommitteeForSlots := make([][]ShardAndCommittee, len(s.ShardCommittees))
	latestCrosslinks := make([]Crosslink, len(s.LatestCrosslinks))
	previousCrosslinks := make([]Crosslink, len(s.PreviousCrosslinks))
	latestBlockHashes := make([]chainhash.Hash, len(s.LatestBlockHashes))
	currentAttestations := make([]PendingAttestation, len(s.CurrentEpochAttestations))
	previousAttestations := make([]PendingAttestation, len(s.PreviousEpochAttestations))
	batchedBlockRoots := make([]chainhash.Hash, len(s.BatchedBlockRoots))
	pendingVotes := make([]AggregatedVote, len(s.PendingVotes))
	proposals := make([]ActiveProposal, len(s.Proposals))
	shardRegistry := make([]chainhash.Hash, len(s.ShardRegistry))
	fd, err := ForkDataFromProto(s.ForkData)
	if err != nil {
		return nil, err
	}
	for i := range validatorRegistry {
		v, err := ValidatorFromProto(s.ValidatorRegistry[i])
		if err != nil {
			return nil, err
		}
		validatorRegistry[i] = *v
	}
	for i := range shardAndCommitteeForSlots {
		shardAndCommitteeForSlots[i] = make([]ShardAndCommittee, len(s.ShardCommittees[i].Committees))
		for j := range shardAndCommitteeForSlots[i] {
			sc, err := ShardAndCommitteeFromProto(s.ShardCommittees[i].Committees[j])
			if err != nil {
				return nil, err
			}
			shardAndCommitteeForSlots[i][j] = *sc
		}
	}
	for i := range latestCrosslinks {
		c, err := CrosslinkFromProto(s.LatestCrosslinks[i])
		if err != nil {
			return nil, err
		}
		latestCrosslinks[i] = *c
	}
	for i := range previousCrosslinks {
		c, err := CrosslinkFromProto(s.PreviousCrosslinks[i])
		if err != nil {
			return nil, err
		}
		previousCrosslinks[i] = *c
	}
	for i := range latestBlockHashes {
		err := latestBlockHashes[i].SetBytes(s.LatestBlockHashes[i])
		if err != nil {
			return nil, err
		}
	}
	for i := range previousAttestations {
		a, err := PendingAttestationFromProto(s.PreviousEpochAttestations[i])
		if err != nil {
			return nil, err
		}
		previousAttestations[i] = *a
	}
	for i := range proposals {
		a, err := ActiveProposalFromProto(s.Proposals[i])
		if err != nil {
			return nil, err
		}
		proposals[i] = *a
	}
	for i := range pendingVotes {
		a, err := AggregatedVoteFromProto(s.PendingVotes[i])
		if err != nil {
			return nil, err
		}
		pendingVotes[i] = *a
	}
	for i := range currentAttestations {
		a, err := PendingAttestationFromProto(s.CurrentEpochAttestations[i])
		if err != nil {
			return nil, err
		}
		currentAttestations[i] = *a
	}
	for i := range batchedBlockRoots {
		err := batchedBlockRoots[i].SetBytes(s.BatchedBlockRoots[i])
		if err != nil {
			return nil, err
		}
	}
	for i := range shardRegistry {
		err := shardRegistry[i].SetBytes(s.ShardRegistry[i])
		if err != nil {
			return nil, err
		}
	}
	newState := &State{
		Slot:                               s.Slot,
		GenesisTime:                        s.GenesisTime,
		EpochIndex:                         s.EpochIndex,
		ForkData:                           *fd,
		ValidatorRegistry:                  validatorRegistry,
		ValidatorRegistryLatestChangeEpoch: s.ValidatorRegistryLatestChangeEpoch,
		ValidatorRegistryExitCount:         s.ValidatorRegistryExitCount,
		ShardAndCommitteeForSlots:          shardAndCommitteeForSlots,
		PreviousJustifiedEpoch:             s.PreviousJustifiedEpoch,
		JustifiedEpoch:                     s.JustifiedEpoch,
		JustificationBitfield:              s.JustificationBitField,
		FinalizedEpoch:                     s.FinalizedEpoch,
		LatestCrosslinks:                   latestCrosslinks,
		PreviousCrosslinks:                 previousCrosslinks,
		LatestBlockHashes:                  latestBlockHashes,
		CurrentEpochAttestations:           currentAttestations,
		PreviousEpochAttestations:          previousAttestations,
		BatchedBlockRoots:                  batchedBlockRoots,
		ShardRegistry:                      shardRegistry,
		PendingVotes:                       pendingVotes,
		Proposals:                          proposals,
	}
	err = newState.ValidatorRegistryDeltaChainTip.SetBytes(s.ValidatorRegistryDeltaChainTip)
	if err != nil {
		return nil, err
	}
	err = newState.RandaoMix.SetBytes(s.RandaoMix)
	if err != nil {
		return nil, err
	}
	err = newState.NextRandaoMix.SetBytes(s.NextRandaoMix)
	if err != nil {
		return nil, err
	}
	newState.ValidatorBalances = append([]uint64{}, s.ValidatorBalances...)
	return newState, nil
}
func (s *State) GetEffectiveBalance(index uint32, c *config.Config) uint64 {
	if s.ValidatorBalances[index] <= c.MaxDeposit {
		return s.ValidatorBalances[index]
	}
	return c.MaxDeposit
}
func (s *State) GetTotalBalance(activeValidators []uint32, c *config.Config) uint64 {
	balance := uint64(0)
	for _, i := range activeValidators {
		balance += s.GetEffectiveBalance(i, c)
	}
	return balance
}
func (s *State) GetTotalBalanceMap(activeValidators map[uint32]struct{}, c *config.Config) uint64 {
	balance := uint64(0)
	for i := range activeValidators {
		balance += s.GetEffectiveBalance(i, c)
	}
	return balance
}
func GetNewValidatorRegistryDeltaChainTip(currentValidatorRegistryDeltaChainTip chainhash.Hash, validatorIndex uint32, pubkey [96]byte, flag uint64) (chainhash.Hash, error) {
	return ssz.HashTreeRoot(ValidatorRegistryDeltaBlock{
		LatestRegistryDeltaRoot: currentValidatorRegistryDeltaChainTip,
		ValidatorIndex:          validatorIndex,
		Pubkey:                  pubkey,
		Flag:                    flag,
	})
}
const (
	ActivationFlag = iota
	ExitFlag
)
func (s *State) ActivateValidator(index uint32) error {
	validator := &s.ValidatorRegistry[index]
	if validator.Status != PendingActivation {
		return errors.New("validator is not pending activation")
	}
	validator.Status = Active
	validator.LatestStatusChangeSlot = s.Slot
	deltaChainTip, err := GetNewValidatorRegistryDeltaChainTip(s.ValidatorRegistryDeltaChainTip, index, validator.Pubkey, ActivationFlag)
	if err != nil {
		return err
	}
	s.ValidatorRegistryDeltaChainTip = deltaChainTip
	return nil
}
func (s *State) InitiateValidatorExit(index uint32) error {
	validator := &s.ValidatorRegistry[index]
	if validator.Status != Active {
		return errors.New("validator is not active")
	}
	validator.Status = ActivePendingExit
	validator.LatestStatusChangeSlot = s.Slot
	return nil
}
func (s *State) GetBeaconProposerIndex(slot uint64, c *config.Config) (uint32, error) {
	committees, err := s.GetShardCommitteesAtSlot(slot, c)
	if err != nil {
		return 0, err
	}
	firstCommittee := committees[0].Committee
	return firstCommittee[int(slot)%len(firstCommittee)], nil
}
func (s *State) GetAssignmentsAssumingShuffle(c *config.Config) [][]ShardAndCommittee {
	startShard := s.ShardAndCommitteeForSlots[0][0].Shard
	return getNewShuffling(s.RandaoMix, s.ValidatorRegistry, int(startShard), c)
}
func (s *State) GetShardProposerIndex(slot uint64, shard uint64, c *config.Config) (uint32, error) {
	var shardCommittee []uint32
	for _, slotCommittees := range s.ShardAndCommitteeForSlots {
		for _, committee := range slotCommittees {
			if committee.Shard == shard {
				shardCommittee = committee.Committee
			}
		}
	}
	if shardCommittee == nil {
		return 0, fmt.Errorf("no shard committee for shard %d", shard)
	}
	return shardCommittee[int(slot+1)%len(shardCommittee)], nil
}