erigon-pulse/cmd/erigon-cl/core/state/state.go
Giulio rebuffo 628f52dd6c
Added reverse beacon changeset for beacon state rewind (#7185)
Added changesets for beacon chain to implement memory efficient fork
choice
2023-03-26 22:25:08 +00:00

256 lines
8.0 KiB
Go

package state
import (
"encoding/binary"
"github.com/minio/sha256-simd"
lru2 "github.com/hashicorp/golang-lru/v2"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/utils"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/beacon_changeset"
)
type HashFunc func([]byte) ([32]byte, error)
const (
blockRootsLength = 8192
stateRootsLength = 8192
randoMixesLength = 65536
slashingsLength = 8192
)
type BeaconState struct {
// State fields
genesisTime uint64
genesisValidatorsRoot libcommon.Hash
slot uint64
fork *cltypes.Fork
latestBlockHeader *cltypes.BeaconBlockHeader
blockRoots [blockRootsLength]libcommon.Hash
stateRoots [stateRootsLength]libcommon.Hash
historicalRoots []libcommon.Hash
eth1Data *cltypes.Eth1Data
eth1DataVotes []*cltypes.Eth1Data
eth1DepositIndex uint64
validators []*cltypes.Validator
balances []uint64
randaoMixes [randoMixesLength]libcommon.Hash
slashings [slashingsLength]uint64
previousEpochParticipation cltypes.ParticipationFlagsList
currentEpochParticipation cltypes.ParticipationFlagsList
justificationBits cltypes.JustificationBits
// Altair
previousJustifiedCheckpoint *cltypes.Checkpoint
currentJustifiedCheckpoint *cltypes.Checkpoint
finalizedCheckpoint *cltypes.Checkpoint
inactivityScores []uint64
currentSyncCommittee *cltypes.SyncCommittee
nextSyncCommittee *cltypes.SyncCommittee
// Bellatrix
latestExecutionPayloadHeader *cltypes.Eth1Header
// Capella
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex uint64
historicalSummaries []*cltypes.HistoricalSummary
// Phase0: genesis fork. these 2 fields replace participation bits.
previousEpochAttestations []*cltypes.PendingAttestation
currentEpochAttestations []*cltypes.PendingAttestation
// Internals
version clparams.StateVersion // State version
leaves [32][32]byte // Pre-computed leaves.
touchedLeaves map[StateLeafIndex]bool // Maps each leaf to whether they were touched or not.
publicKeyIndicies map[[48]byte]uint64
// Caches
activeValidatorsCache *lru2.Cache[uint64, []uint64]
committeeCache *lru2.Cache[[16]byte, []uint64]
shuffledSetsCache *lru2.Cache[libcommon.Hash, []uint64]
totalActiveBalanceCache *uint64
totalActiveBalanceRootCache uint64
proposerIndex *uint64
previousStateRoot libcommon.Hash
// Configs
beaconConfig *clparams.BeaconChainConfig
// Changesets
reverseChangeset *beacon_changeset.ReverseBeaconStateChangeSet
}
func New(cfg *clparams.BeaconChainConfig) *BeaconState {
state := &BeaconState{
beaconConfig: cfg,
}
state.initBeaconState()
return state
}
func preparateRootsForHashing(roots []libcommon.Hash) [][32]byte {
ret := make([][32]byte, len(roots))
for i := range roots {
copy(ret[i][:], roots[i][:])
}
return ret
}
// MarshallSSZTo retrieve the SSZ encoded length of the state.
func (b *BeaconState) DecodeSSZ(buf []byte) error {
panic("not implemented")
}
// BlockRoot computes the block root for the state.
func (b *BeaconState) BlockRoot() ([32]byte, error) {
stateRoot, err := b.HashSSZ()
if err != nil {
return [32]byte{}, err
}
return (&cltypes.BeaconBlockHeader{
Slot: b.latestBlockHeader.Slot,
ProposerIndex: b.latestBlockHeader.ProposerIndex,
BodyRoot: b.latestBlockHeader.BodyRoot,
ParentRoot: b.latestBlockHeader.ParentRoot,
Root: stateRoot,
}).HashSSZ()
}
func (b *BeaconState) _refreshActiveBalances() {
epoch := b.Epoch()
b.totalActiveBalanceCache = new(uint64)
*b.totalActiveBalanceCache = 0
for _, validator := range b.validators {
if validator.Active(epoch) {
*b.totalActiveBalanceCache += validator.EffectiveBalance
}
}
*b.totalActiveBalanceCache = utils.Max64(b.beaconConfig.EffectiveBalanceIncrement, *b.totalActiveBalanceCache)
b.totalActiveBalanceRootCache = utils.IntegerSquareRoot(*b.totalActiveBalanceCache)
}
func (b *BeaconState) _updateProposerIndex() (err error) {
epoch := b.Epoch()
hash := sha256.New()
// Input for the seed hash.
input := b.GetSeed(epoch, clparams.MainnetBeaconConfig.DomainBeaconProposer)
slotByteArray := make([]byte, 8)
binary.LittleEndian.PutUint64(slotByteArray, b.slot)
// Add slot to the end of the input.
inputWithSlot := append(input[:], slotByteArray...)
// Calculate the hash.
hash.Write(inputWithSlot)
seed := hash.Sum(nil)
indices := b.GetActiveValidatorsIndices(epoch)
// Write the seed to an array.
seedArray := [32]byte{}
copy(seedArray[:], seed)
b.proposerIndex = new(uint64)
*b.proposerIndex, err = b.ComputeProposerIndex(indices, seedArray)
return
}
// _initializeValidatorsPhase0 initializes the validators matching flags based on previous/current attestations
func (b *BeaconState) _initializeValidatorsPhase0() error {
// Previous Pending attestations
if b.slot == 0 {
return nil
}
previousEpochRoot, err := b.GetBlockRoot(b.PreviousEpoch())
if err != nil {
return err
}
for _, attestation := range b.previousEpochAttestations {
slotRoot, err := b.GetBlockRootAtSlot(attestation.Data.Slot)
if err != nil {
return err
}
indicies, err := b.GetAttestingIndicies(attestation.Data, attestation.AggregationBits, false)
if err != nil {
return err
}
for _, index := range indicies {
if b.validators[index].MinPreviousInclusionDelayAttestation == nil || b.validators[index].MinPreviousInclusionDelayAttestation.InclusionDelay > attestation.InclusionDelay {
b.validators[index].MinPreviousInclusionDelayAttestation = attestation
}
b.validators[index].IsPreviousMatchingSourceAttester = true
if attestation.Data.Target.Root != previousEpochRoot {
continue
}
b.validators[index].IsPreviousMatchingTargetAttester = true
if attestation.Data.BeaconBlockHash == slotRoot {
b.validators[index].IsPreviousMatchingHeadAttester = true
}
}
}
// Current Pending attestations
if len(b.currentEpochAttestations) == 0 {
return nil
}
currentEpochRoot, err := b.GetBlockRoot(b.Epoch())
if err != nil {
return err
}
for _, attestation := range b.currentEpochAttestations {
slotRoot, err := b.GetBlockRootAtSlot(attestation.Data.Slot)
if err != nil {
return err
}
if err != nil {
return err
}
indicies, err := b.GetAttestingIndicies(attestation.Data, attestation.AggregationBits, false)
if err != nil {
return err
}
for _, index := range indicies {
if b.validators[index].MinCurrentInclusionDelayAttestation == nil || b.validators[index].MinCurrentInclusionDelayAttestation.InclusionDelay > attestation.InclusionDelay {
b.validators[index].MinCurrentInclusionDelayAttestation = attestation
}
b.validators[index].IsCurrentMatchingSourceAttester = true
if attestation.Data.Target.Root == currentEpochRoot {
b.validators[index].IsCurrentMatchingTargetAttester = true
}
if attestation.Data.BeaconBlockHash == slotRoot {
b.validators[index].IsCurrentMatchingHeadAttester = true
}
}
}
return nil
}
func (b *BeaconState) initBeaconState() error {
if b.touchedLeaves == nil {
b.touchedLeaves = make(map[StateLeafIndex]bool)
}
b.publicKeyIndicies = make(map[[48]byte]uint64)
b._refreshActiveBalances()
for i, validator := range b.validators {
b.publicKeyIndicies[validator.PublicKey] = uint64(i)
}
var err error
if b.activeValidatorsCache, err = lru2.New[uint64, []uint64](5); err != nil {
return err
}
if b.shuffledSetsCache, err = lru2.New[libcommon.Hash, []uint64](25); err != nil {
return err
}
if b.committeeCache, err = lru2.New[[16]byte, []uint64](256); err != nil {
return err
}
if err := b._updateProposerIndex(); err != nil {
return err
}
if b.version >= clparams.Phase0Version {
return b._initializeValidatorsPhase0()
}
return nil
}