mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 19:40:37 +00:00
70152bf476
* add new thing * only have it for late blocks * comments * change to lock * add test * Update beacon-chain/state/state-native/state_test.go --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
940 lines
34 KiB
Go
940 lines
34 KiB
Go
package state_native
|
|
|
|
import (
|
|
"context"
|
|
"runtime"
|
|
"sort"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/fieldtrie"
|
|
customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
"github.com/prysmaticlabs/prysm/v4/container/slice"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/runtime/version"
|
|
"go.opencensus.io/trace"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var phase0Fields = []types.FieldIndex{
|
|
types.GenesisTime,
|
|
types.GenesisValidatorsRoot,
|
|
types.Slot,
|
|
types.Fork,
|
|
types.LatestBlockHeader,
|
|
types.BlockRoots,
|
|
types.StateRoots,
|
|
types.HistoricalRoots,
|
|
types.Eth1Data,
|
|
types.Eth1DataVotes,
|
|
types.Eth1DepositIndex,
|
|
types.Validators,
|
|
types.Balances,
|
|
types.RandaoMixes,
|
|
types.Slashings,
|
|
types.PreviousEpochAttestations,
|
|
types.CurrentEpochAttestations,
|
|
types.JustificationBits,
|
|
types.PreviousJustifiedCheckpoint,
|
|
types.CurrentJustifiedCheckpoint,
|
|
types.FinalizedCheckpoint,
|
|
}
|
|
|
|
var altairFields = []types.FieldIndex{
|
|
types.GenesisTime,
|
|
types.GenesisValidatorsRoot,
|
|
types.Slot,
|
|
types.Fork,
|
|
types.LatestBlockHeader,
|
|
types.BlockRoots,
|
|
types.StateRoots,
|
|
types.HistoricalRoots,
|
|
types.Eth1Data,
|
|
types.Eth1DataVotes,
|
|
types.Eth1DepositIndex,
|
|
types.Validators,
|
|
types.Balances,
|
|
types.RandaoMixes,
|
|
types.Slashings,
|
|
types.PreviousEpochParticipationBits,
|
|
types.CurrentEpochParticipationBits,
|
|
types.JustificationBits,
|
|
types.PreviousJustifiedCheckpoint,
|
|
types.CurrentJustifiedCheckpoint,
|
|
types.FinalizedCheckpoint,
|
|
types.InactivityScores,
|
|
types.CurrentSyncCommittee,
|
|
types.NextSyncCommittee,
|
|
}
|
|
|
|
var bellatrixFields = append(altairFields, types.LatestExecutionPayloadHeader)
|
|
|
|
var capellaFields = append(
|
|
altairFields,
|
|
types.LatestExecutionPayloadHeaderCapella,
|
|
types.NextWithdrawalIndex,
|
|
types.NextWithdrawalValidatorIndex,
|
|
types.HistoricalSummaries,
|
|
)
|
|
|
|
const (
|
|
phase0SharedFieldRefCount = 10
|
|
altairSharedFieldRefCount = 11
|
|
bellatrixSharedFieldRefCount = 12
|
|
capellaSharedFieldRefCount = 14
|
|
)
|
|
|
|
// InitializeFromProtoPhase0 the beacon state from a protobuf representation.
|
|
func InitializeFromProtoPhase0(st *ethpb.BeaconState) (state.BeaconState, error) {
|
|
return InitializeFromProtoUnsafePhase0(proto.Clone(st).(*ethpb.BeaconState))
|
|
}
|
|
|
|
// InitializeFromProtoAltair the beacon state from a protobuf representation.
|
|
func InitializeFromProtoAltair(st *ethpb.BeaconStateAltair) (state.BeaconState, error) {
|
|
return InitializeFromProtoUnsafeAltair(proto.Clone(st).(*ethpb.BeaconStateAltair))
|
|
}
|
|
|
|
// InitializeFromProtoBellatrix the beacon state from a protobuf representation.
|
|
func InitializeFromProtoBellatrix(st *ethpb.BeaconStateBellatrix) (state.BeaconState, error) {
|
|
return InitializeFromProtoUnsafeBellatrix(proto.Clone(st).(*ethpb.BeaconStateBellatrix))
|
|
}
|
|
|
|
// InitializeFromProtoCapella the beacon state from a protobuf representation.
|
|
func InitializeFromProtoCapella(st *ethpb.BeaconStateCapella) (state.BeaconState, error) {
|
|
return InitializeFromProtoUnsafeCapella(proto.Clone(st).(*ethpb.BeaconStateCapella))
|
|
}
|
|
|
|
// InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields
|
|
// and sets them as fields of the BeaconState type.
|
|
func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, error) {
|
|
if st == nil {
|
|
return nil, errors.New("received nil state")
|
|
}
|
|
|
|
var bRoots customtypes.BlockRoots
|
|
for i, r := range st.BlockRoots {
|
|
copy(bRoots[i][:], r)
|
|
}
|
|
var sRoots customtypes.StateRoots
|
|
for i, r := range st.StateRoots {
|
|
copy(sRoots[i][:], r)
|
|
}
|
|
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
|
|
for i, r := range st.HistoricalRoots {
|
|
copy(hRoots[i][:], r)
|
|
}
|
|
var mixes customtypes.RandaoMixes
|
|
for i, m := range st.RandaoMixes {
|
|
copy(mixes[i][:], m)
|
|
}
|
|
|
|
fieldCount := params.BeaconConfig().BeaconStateFieldCount
|
|
b := &BeaconState{
|
|
version: version.Phase0,
|
|
genesisTime: st.GenesisTime,
|
|
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
|
|
slot: st.Slot,
|
|
fork: st.Fork,
|
|
latestBlockHeader: st.LatestBlockHeader,
|
|
blockRoots: &bRoots,
|
|
stateRoots: &sRoots,
|
|
historicalRoots: hRoots,
|
|
eth1Data: st.Eth1Data,
|
|
eth1DataVotes: st.Eth1DataVotes,
|
|
eth1DepositIndex: st.Eth1DepositIndex,
|
|
validators: st.Validators,
|
|
balances: st.Balances,
|
|
randaoMixes: &mixes,
|
|
slashings: st.Slashings,
|
|
previousEpochAttestations: st.PreviousEpochAttestations,
|
|
currentEpochAttestations: st.CurrentEpochAttestations,
|
|
justificationBits: st.JustificationBits,
|
|
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
|
|
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
|
|
finalizedCheckpoint: st.FinalizedCheckpoint,
|
|
|
|
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
|
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
|
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
|
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount),
|
|
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
|
valMapHandler: stateutil.NewValMapHandler(st.Validators),
|
|
}
|
|
|
|
for _, f := range phase0Fields {
|
|
b.dirtyFields[f] = true
|
|
b.rebuildTrie[f] = true
|
|
b.dirtyIndices[f] = []uint64{}
|
|
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.stateFieldLeaves[f] = trie
|
|
}
|
|
|
|
// Initialize field reference tracking for shared data.
|
|
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.PreviousEpochAttestations] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.CurrentEpochAttestations] = stateutil.NewRef(1)
|
|
|
|
state.StateCount.Inc()
|
|
// Finalizer runs when dst is being destroyed in garbage collection.
|
|
runtime.SetFinalizer(b, finalizerCleanup)
|
|
return b, nil
|
|
}
|
|
|
|
// InitializeFromProtoUnsafeAltair directly uses the beacon state protobuf fields
|
|
// and sets them as fields of the BeaconState type.
|
|
func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconState, error) {
|
|
if st == nil {
|
|
return nil, errors.New("received nil state")
|
|
}
|
|
|
|
var bRoots customtypes.BlockRoots
|
|
for i, r := range st.BlockRoots {
|
|
bRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var sRoots customtypes.StateRoots
|
|
for i, r := range st.StateRoots {
|
|
sRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
|
|
for i, r := range st.HistoricalRoots {
|
|
hRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var mixes customtypes.RandaoMixes
|
|
for i, m := range st.RandaoMixes {
|
|
mixes[i] = bytesutil.ToBytes32(m)
|
|
}
|
|
|
|
fieldCount := params.BeaconConfig().BeaconStateAltairFieldCount
|
|
b := &BeaconState{
|
|
version: version.Altair,
|
|
genesisTime: st.GenesisTime,
|
|
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
|
|
slot: st.Slot,
|
|
fork: st.Fork,
|
|
latestBlockHeader: st.LatestBlockHeader,
|
|
blockRoots: &bRoots,
|
|
stateRoots: &sRoots,
|
|
historicalRoots: hRoots,
|
|
eth1Data: st.Eth1Data,
|
|
eth1DataVotes: st.Eth1DataVotes,
|
|
eth1DepositIndex: st.Eth1DepositIndex,
|
|
validators: st.Validators,
|
|
balances: st.Balances,
|
|
randaoMixes: &mixes,
|
|
slashings: st.Slashings,
|
|
previousEpochParticipation: st.PreviousEpochParticipation,
|
|
currentEpochParticipation: st.CurrentEpochParticipation,
|
|
justificationBits: st.JustificationBits,
|
|
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
|
|
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
|
|
finalizedCheckpoint: st.FinalizedCheckpoint,
|
|
inactivityScores: st.InactivityScores,
|
|
currentSyncCommittee: st.CurrentSyncCommittee,
|
|
nextSyncCommittee: st.NextSyncCommittee,
|
|
|
|
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
|
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
|
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
|
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, altairSharedFieldRefCount),
|
|
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
|
valMapHandler: stateutil.NewValMapHandler(st.Validators),
|
|
}
|
|
|
|
for _, f := range altairFields {
|
|
b.dirtyFields[f] = true
|
|
b.rebuildTrie[f] = true
|
|
b.dirtyIndices[f] = []uint64{}
|
|
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.stateFieldLeaves[f] = trie
|
|
}
|
|
|
|
// Initialize field reference tracking for shared data.
|
|
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) // New in Altair.
|
|
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) // New in Altair.
|
|
b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) // New in Altair.
|
|
|
|
state.StateCount.Inc()
|
|
// Finalizer runs when dst is being destroyed in garbage collection.
|
|
runtime.SetFinalizer(b, finalizerCleanup)
|
|
return b, nil
|
|
}
|
|
|
|
// InitializeFromProtoUnsafeBellatrix directly uses the beacon state protobuf fields
|
|
// and sets them as fields of the BeaconState type.
|
|
func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.BeaconState, error) {
|
|
if st == nil {
|
|
return nil, errors.New("received nil state")
|
|
}
|
|
|
|
var bRoots customtypes.BlockRoots
|
|
for i, r := range st.BlockRoots {
|
|
bRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var sRoots customtypes.StateRoots
|
|
for i, r := range st.StateRoots {
|
|
sRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
|
|
for i, r := range st.HistoricalRoots {
|
|
hRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var mixes customtypes.RandaoMixes
|
|
for i, m := range st.RandaoMixes {
|
|
mixes[i] = bytesutil.ToBytes32(m)
|
|
}
|
|
|
|
fieldCount := params.BeaconConfig().BeaconStateBellatrixFieldCount
|
|
b := &BeaconState{
|
|
version: version.Bellatrix,
|
|
genesisTime: st.GenesisTime,
|
|
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
|
|
slot: st.Slot,
|
|
fork: st.Fork,
|
|
latestBlockHeader: st.LatestBlockHeader,
|
|
blockRoots: &bRoots,
|
|
stateRoots: &sRoots,
|
|
historicalRoots: hRoots,
|
|
eth1Data: st.Eth1Data,
|
|
eth1DataVotes: st.Eth1DataVotes,
|
|
eth1DepositIndex: st.Eth1DepositIndex,
|
|
validators: st.Validators,
|
|
balances: st.Balances,
|
|
randaoMixes: &mixes,
|
|
slashings: st.Slashings,
|
|
previousEpochParticipation: st.PreviousEpochParticipation,
|
|
currentEpochParticipation: st.CurrentEpochParticipation,
|
|
justificationBits: st.JustificationBits,
|
|
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
|
|
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
|
|
finalizedCheckpoint: st.FinalizedCheckpoint,
|
|
inactivityScores: st.InactivityScores,
|
|
currentSyncCommittee: st.CurrentSyncCommittee,
|
|
nextSyncCommittee: st.NextSyncCommittee,
|
|
latestExecutionPayloadHeader: st.LatestExecutionPayloadHeader,
|
|
|
|
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
|
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
|
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
|
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount),
|
|
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
|
valMapHandler: stateutil.NewValMapHandler(st.Validators),
|
|
}
|
|
|
|
for _, f := range bellatrixFields {
|
|
b.dirtyFields[f] = true
|
|
b.rebuildTrie[f] = true
|
|
b.dirtyIndices[f] = []uint64{}
|
|
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.stateFieldLeaves[f] = trie
|
|
}
|
|
|
|
// Initialize field reference tracking for shared data.
|
|
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.LatestExecutionPayloadHeader] = stateutil.NewRef(1) // New in Bellatrix.
|
|
|
|
state.StateCount.Inc()
|
|
// Finalizer runs when dst is being destroyed in garbage collection.
|
|
runtime.SetFinalizer(b, finalizerCleanup)
|
|
return b, nil
|
|
}
|
|
|
|
// InitializeFromProtoUnsafeCapella directly uses the beacon state protobuf fields
|
|
// and sets them as fields of the BeaconState type.
|
|
func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.BeaconState, error) {
|
|
if st == nil {
|
|
return nil, errors.New("received nil state")
|
|
}
|
|
|
|
var bRoots customtypes.BlockRoots
|
|
for i, r := range st.BlockRoots {
|
|
bRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var sRoots customtypes.StateRoots
|
|
for i, r := range st.StateRoots {
|
|
sRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
|
|
for i, r := range st.HistoricalRoots {
|
|
hRoots[i] = bytesutil.ToBytes32(r)
|
|
}
|
|
var mixes customtypes.RandaoMixes
|
|
for i, m := range st.RandaoMixes {
|
|
mixes[i] = bytesutil.ToBytes32(m)
|
|
}
|
|
|
|
fieldCount := params.BeaconConfig().BeaconStateCapellaFieldCount
|
|
b := &BeaconState{
|
|
version: version.Capella,
|
|
genesisTime: st.GenesisTime,
|
|
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
|
|
slot: st.Slot,
|
|
fork: st.Fork,
|
|
latestBlockHeader: st.LatestBlockHeader,
|
|
blockRoots: &bRoots,
|
|
stateRoots: &sRoots,
|
|
historicalRoots: hRoots,
|
|
eth1Data: st.Eth1Data,
|
|
eth1DataVotes: st.Eth1DataVotes,
|
|
eth1DepositIndex: st.Eth1DepositIndex,
|
|
validators: st.Validators,
|
|
balances: st.Balances,
|
|
randaoMixes: &mixes,
|
|
slashings: st.Slashings,
|
|
previousEpochParticipation: st.PreviousEpochParticipation,
|
|
currentEpochParticipation: st.CurrentEpochParticipation,
|
|
justificationBits: st.JustificationBits,
|
|
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
|
|
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
|
|
finalizedCheckpoint: st.FinalizedCheckpoint,
|
|
inactivityScores: st.InactivityScores,
|
|
currentSyncCommittee: st.CurrentSyncCommittee,
|
|
nextSyncCommittee: st.NextSyncCommittee,
|
|
latestExecutionPayloadHeaderCapella: st.LatestExecutionPayloadHeader,
|
|
nextWithdrawalIndex: st.NextWithdrawalIndex,
|
|
nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex,
|
|
historicalSummaries: st.HistoricalSummaries,
|
|
|
|
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
|
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
|
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
|
sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount),
|
|
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
|
valMapHandler: stateutil.NewValMapHandler(st.Validators),
|
|
}
|
|
|
|
for _, f := range capellaFields {
|
|
b.dirtyFields[f] = true
|
|
b.rebuildTrie[f] = true
|
|
b.dirtyIndices[f] = []uint64{}
|
|
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.stateFieldLeaves[f] = trie
|
|
}
|
|
|
|
// Initialize field reference tracking for shared data.
|
|
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1)
|
|
b.sharedFieldReferences[types.LatestExecutionPayloadHeaderCapella] = stateutil.NewRef(1) // New in Capella.
|
|
b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella.
|
|
|
|
state.StateCount.Inc()
|
|
// Finalizer runs when dst is being destroyed in garbage collection.
|
|
runtime.SetFinalizer(b, finalizerCleanup)
|
|
return b, nil
|
|
}
|
|
|
|
// Copy returns a deep copy of the beacon state.
|
|
func (b *BeaconState) Copy() state.BeaconState {
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
|
|
var fieldCount int
|
|
switch b.version {
|
|
case version.Phase0:
|
|
fieldCount = params.BeaconConfig().BeaconStateFieldCount
|
|
case version.Altair:
|
|
fieldCount = params.BeaconConfig().BeaconStateAltairFieldCount
|
|
case version.Bellatrix:
|
|
fieldCount = params.BeaconConfig().BeaconStateBellatrixFieldCount
|
|
case version.Capella:
|
|
fieldCount = params.BeaconConfig().BeaconStateCapellaFieldCount
|
|
}
|
|
|
|
dst := &BeaconState{
|
|
version: b.version,
|
|
|
|
// Primitive types, safe to copy.
|
|
genesisTime: b.genesisTime,
|
|
slot: b.slot,
|
|
eth1DepositIndex: b.eth1DepositIndex,
|
|
nextWithdrawalIndex: b.nextWithdrawalIndex,
|
|
nextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
|
|
|
// Large arrays, infrequently changed, constant size.
|
|
blockRoots: b.blockRoots,
|
|
stateRoots: b.stateRoots,
|
|
randaoMixes: b.randaoMixes,
|
|
previousEpochAttestations: b.previousEpochAttestations,
|
|
currentEpochAttestations: b.currentEpochAttestations,
|
|
eth1DataVotes: b.eth1DataVotes,
|
|
slashings: b.slashings,
|
|
|
|
// Large arrays, increases over time.
|
|
balances: b.balances,
|
|
historicalRoots: b.historicalRoots,
|
|
historicalSummaries: b.historicalSummaries,
|
|
validators: b.validators,
|
|
previousEpochParticipation: b.previousEpochParticipation,
|
|
currentEpochParticipation: b.currentEpochParticipation,
|
|
inactivityScores: b.inactivityScores,
|
|
|
|
// Everything else, too small to be concerned about, constant size.
|
|
genesisValidatorsRoot: b.genesisValidatorsRoot,
|
|
justificationBits: b.justificationBitsVal(),
|
|
fork: b.forkVal(),
|
|
latestBlockHeader: b.latestBlockHeaderVal(),
|
|
eth1Data: b.eth1DataVal(),
|
|
previousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
|
|
currentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
|
|
finalizedCheckpoint: b.finalizedCheckpointVal(),
|
|
currentSyncCommittee: b.currentSyncCommitteeVal(),
|
|
nextSyncCommittee: b.nextSyncCommitteeVal(),
|
|
latestExecutionPayloadHeader: b.latestExecutionPayloadHeaderVal(),
|
|
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapellaVal(),
|
|
|
|
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
|
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
|
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
|
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
|
|
|
// Share the reference to validator index map.
|
|
valMapHandler: b.valMapHandler,
|
|
}
|
|
|
|
switch b.version {
|
|
case version.Phase0:
|
|
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount)
|
|
case version.Altair:
|
|
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, altairSharedFieldRefCount)
|
|
case version.Bellatrix:
|
|
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount)
|
|
case version.Capella:
|
|
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount)
|
|
}
|
|
|
|
for field, ref := range b.sharedFieldReferences {
|
|
ref.AddRef()
|
|
dst.sharedFieldReferences[field] = ref
|
|
}
|
|
|
|
// Increment ref for validator map
|
|
b.valMapHandler.AddRef()
|
|
|
|
for i := range b.dirtyFields {
|
|
dst.dirtyFields[i] = true
|
|
}
|
|
|
|
for i := range b.dirtyIndices {
|
|
indices := make([]uint64, len(b.dirtyIndices[i]))
|
|
copy(indices, b.dirtyIndices[i])
|
|
dst.dirtyIndices[i] = indices
|
|
}
|
|
|
|
for i := range b.rebuildTrie {
|
|
dst.rebuildTrie[i] = true
|
|
}
|
|
|
|
for fldIdx, fieldTrie := range b.stateFieldLeaves {
|
|
dst.stateFieldLeaves[fldIdx] = fieldTrie
|
|
if fieldTrie.FieldReference() != nil {
|
|
fieldTrie.Lock()
|
|
fieldTrie.FieldReference().AddRef()
|
|
fieldTrie.Unlock()
|
|
}
|
|
}
|
|
|
|
if b.merkleLayers != nil {
|
|
dst.merkleLayers = make([][][]byte, len(b.merkleLayers))
|
|
for i, layer := range b.merkleLayers {
|
|
dst.merkleLayers[i] = make([][]byte, len(layer))
|
|
for j, content := range layer {
|
|
dst.merkleLayers[i][j] = make([]byte, len(content))
|
|
copy(dst.merkleLayers[i][j], content)
|
|
}
|
|
}
|
|
}
|
|
|
|
state.StateCount.Inc()
|
|
// Finalizer runs when dst is being destroyed in garbage collection.
|
|
runtime.SetFinalizer(dst, finalizerCleanup)
|
|
return dst
|
|
}
|
|
|
|
// HashTreeRoot of the beacon state retrieves the Merkle root of the trie
|
|
// representation of the beacon state based on the Ethereum Simple Serialize specification.
|
|
func (b *BeaconState) HashTreeRoot(ctx context.Context) ([32]byte, error) {
|
|
ctx, span := trace.StartSpan(ctx, "beaconState.HashTreeRoot")
|
|
defer span.End()
|
|
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
if err := b.initializeMerkleLayers(ctx); err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
if err := b.recomputeDirtyFields(ctx); err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
return bytesutil.ToBytes32(b.merkleLayers[len(b.merkleLayers)-1][0]), nil
|
|
}
|
|
|
|
// Initializes the Merkle layers for the beacon state if they are empty.
|
|
//
|
|
// WARNING: Caller must acquire the mutex before using.
|
|
func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
|
|
if len(b.merkleLayers) > 0 {
|
|
return nil
|
|
}
|
|
fieldRoots, err := ComputeFieldRootsWithHasher(ctx, b)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
layers := stateutil.Merkleize(fieldRoots)
|
|
b.merkleLayers = layers
|
|
switch b.version {
|
|
case version.Phase0:
|
|
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFieldCount)
|
|
case version.Altair:
|
|
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateAltairFieldCount)
|
|
case version.Bellatrix:
|
|
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateBellatrixFieldCount)
|
|
case version.Capella:
|
|
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateCapellaFieldCount)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Recomputes the Merkle layers for the dirty fields in the state.
|
|
//
|
|
// WARNING: Caller must acquire the mutex before using.
|
|
func (b *BeaconState) recomputeDirtyFields(ctx context.Context) error {
|
|
for field := range b.dirtyFields {
|
|
root, err := b.rootSelector(ctx, field)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
idx := field.RealPosition()
|
|
b.merkleLayers[0][idx] = root[:]
|
|
b.recomputeRoot(idx)
|
|
delete(b.dirtyFields, field)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FieldReferencesCount returns the reference count held by each field. This
|
|
// also includes the field trie held by each field.
|
|
func (b *BeaconState) FieldReferencesCount() map[string]uint64 {
|
|
refMap := make(map[string]uint64)
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
for i, f := range b.sharedFieldReferences {
|
|
refMap[i.String()] = uint64(f.Refs())
|
|
}
|
|
for i, f := range b.stateFieldLeaves {
|
|
numOfRefs := uint64(f.FieldReference().Refs())
|
|
f.RLock()
|
|
if !f.Empty() {
|
|
refMap[i.String()+"_trie"] = numOfRefs
|
|
}
|
|
f.RUnlock()
|
|
}
|
|
return refMap
|
|
}
|
|
|
|
// IsNil checks if the state and the underlying proto
|
|
// object are nil.
|
|
func (b *BeaconState) IsNil() bool {
|
|
return b == nil
|
|
}
|
|
|
|
func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) ([32]byte, error) {
|
|
ctx, span := trace.StartSpan(ctx, "beaconState.rootSelector")
|
|
defer span.End()
|
|
span.AddAttributes(trace.StringAttribute("field", field.String()))
|
|
|
|
switch field {
|
|
case types.GenesisTime:
|
|
return ssz.Uint64Root(b.genesisTime), nil
|
|
case types.GenesisValidatorsRoot:
|
|
return b.genesisValidatorsRoot, nil
|
|
case types.Slot:
|
|
return ssz.Uint64Root(uint64(b.slot)), nil
|
|
case types.Eth1DepositIndex:
|
|
return ssz.Uint64Root(b.eth1DepositIndex), nil
|
|
case types.Fork:
|
|
return ssz.ForkRoot(b.fork)
|
|
case types.LatestBlockHeader:
|
|
return stateutil.BlockHeaderRoot(b.latestBlockHeader)
|
|
case types.BlockRoots:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(field, b.blockRoots, fieldparams.BlockRootsLength)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(field, b.blockRoots)
|
|
case types.StateRoots:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(field, b.stateRoots, fieldparams.StateRootsLength)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(field, b.stateRoots)
|
|
case types.HistoricalRoots:
|
|
hRoots := make([][]byte, len(b.historicalRoots))
|
|
for i := range hRoots {
|
|
hRoots[i] = b.historicalRoots[i][:]
|
|
}
|
|
return ssz.ByteArrayRootWithLimit(hRoots, fieldparams.HistoricalRootsLength)
|
|
case types.Eth1Data:
|
|
return stateutil.Eth1Root(b.eth1Data)
|
|
case types.Eth1DataVotes:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(
|
|
field,
|
|
b.eth1DataVotes,
|
|
params.BeaconConfig().Eth1DataVotesLength(),
|
|
)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(field, b.eth1DataVotes)
|
|
case types.Validators:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(field, b.validators, fieldparams.ValidatorRegistryLimit)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(11, b.validators)
|
|
case types.Balances:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(field, b.balances, stateutil.ValidatorLimitForBalancesChunks())
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(12, b.balances)
|
|
case types.RandaoMixes:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(field, b.randaoMixes, fieldparams.RandaoMixesLength)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(13, b.randaoMixes)
|
|
case types.Slashings:
|
|
return ssz.SlashingsRoot(b.slashings)
|
|
case types.PreviousEpochAttestations:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(
|
|
field,
|
|
b.previousEpochAttestations,
|
|
params.BeaconConfig().PreviousEpochAttestationsLength(),
|
|
)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(field, b.previousEpochAttestations)
|
|
case types.CurrentEpochAttestations:
|
|
if b.rebuildTrie[field] {
|
|
err := b.resetFieldTrie(
|
|
field,
|
|
b.currentEpochAttestations,
|
|
params.BeaconConfig().CurrentEpochAttestationsLength(),
|
|
)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
delete(b.rebuildTrie, field)
|
|
return b.stateFieldLeaves[field].TrieRoot()
|
|
}
|
|
return b.recomputeFieldTrie(field, b.currentEpochAttestations)
|
|
case types.PreviousEpochParticipationBits:
|
|
return stateutil.ParticipationBitsRoot(b.previousEpochParticipation)
|
|
case types.CurrentEpochParticipationBits:
|
|
return stateutil.ParticipationBitsRoot(b.currentEpochParticipation)
|
|
case types.JustificationBits:
|
|
return bytesutil.ToBytes32(b.justificationBits), nil
|
|
case types.PreviousJustifiedCheckpoint:
|
|
return ssz.CheckpointRoot(b.previousJustifiedCheckpoint)
|
|
case types.CurrentJustifiedCheckpoint:
|
|
return ssz.CheckpointRoot(b.currentJustifiedCheckpoint)
|
|
case types.FinalizedCheckpoint:
|
|
return ssz.CheckpointRoot(b.finalizedCheckpoint)
|
|
case types.InactivityScores:
|
|
return stateutil.Uint64ListRootWithRegistryLimit(b.inactivityScores)
|
|
case types.CurrentSyncCommittee:
|
|
return stateutil.SyncCommitteeRoot(b.currentSyncCommittee)
|
|
case types.NextSyncCommittee:
|
|
return stateutil.SyncCommitteeRoot(b.nextSyncCommittee)
|
|
case types.LatestExecutionPayloadHeader:
|
|
return b.latestExecutionPayloadHeader.HashTreeRoot()
|
|
case types.LatestExecutionPayloadHeaderCapella:
|
|
return b.latestExecutionPayloadHeaderCapella.HashTreeRoot()
|
|
case types.NextWithdrawalIndex:
|
|
return ssz.Uint64Root(b.nextWithdrawalIndex), nil
|
|
case types.NextWithdrawalValidatorIndex:
|
|
return ssz.Uint64Root(uint64(b.nextWithdrawalValidatorIndex)), nil
|
|
case types.HistoricalSummaries:
|
|
return stateutil.HistoricalSummariesRoot(b.historicalSummaries)
|
|
}
|
|
return [32]byte{}, errors.New("invalid field index provided")
|
|
}
|
|
|
|
// CopyAllTries copies our field tries from the state. This is used to
|
|
// remove shared field tries which have references to other states and
|
|
// only have this copied set referencing to the current state.
|
|
func (b *BeaconState) CopyAllTries() {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
|
|
for fldIdx, fieldTrie := range b.stateFieldLeaves {
|
|
if fieldTrie.FieldReference() != nil {
|
|
fieldTrie.Lock()
|
|
if fieldTrie.FieldReference().Refs() > 1 {
|
|
fieldTrie.FieldReference().MinusRef()
|
|
newTrie := fieldTrie.CopyTrie()
|
|
b.stateFieldLeaves[fldIdx] = newTrie
|
|
}
|
|
fieldTrie.Unlock()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *BeaconState) recomputeFieldTrie(index types.FieldIndex, elements interface{}) ([32]byte, error) {
|
|
fTrie := b.stateFieldLeaves[index]
|
|
fTrieMutex := fTrie.RWMutex
|
|
// We can't lock the trie directly because the trie's variable gets reassigned,
|
|
// and therefore we would call Unlock() on a different object.
|
|
fTrieMutex.Lock()
|
|
|
|
if fTrie.Empty() {
|
|
err := b.resetFieldTrie(index, elements, fTrie.Length())
|
|
if err != nil {
|
|
fTrieMutex.Unlock()
|
|
return [32]byte{}, err
|
|
}
|
|
// Reduce reference count as we are instantiating a new trie.
|
|
fTrie.FieldReference().MinusRef()
|
|
fTrieMutex.Unlock()
|
|
return b.stateFieldLeaves[index].TrieRoot()
|
|
}
|
|
|
|
if fTrie.FieldReference().Refs() > 1 {
|
|
fTrie.FieldReference().MinusRef()
|
|
newTrie := fTrie.TransferTrie()
|
|
b.stateFieldLeaves[index] = newTrie
|
|
fTrie = newTrie
|
|
}
|
|
fTrieMutex.Unlock()
|
|
|
|
// remove duplicate indexes
|
|
b.dirtyIndices[index] = slice.SetUint64(b.dirtyIndices[index])
|
|
// sort indexes again
|
|
sort.Slice(b.dirtyIndices[index], func(i int, j int) bool {
|
|
return b.dirtyIndices[index][i] < b.dirtyIndices[index][j]
|
|
})
|
|
root, err := fTrie.RecomputeTrie(b.dirtyIndices[index], elements)
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
b.dirtyIndices[index] = []uint64{}
|
|
return root, nil
|
|
}
|
|
|
|
func (b *BeaconState) resetFieldTrie(index types.FieldIndex, elements interface{}, length uint64) error {
|
|
fTrie, err := fieldtrie.NewFieldTrie(index, fieldMap[index], elements, length)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b.stateFieldLeaves[index] = fTrie
|
|
b.dirtyIndices[index] = []uint64{}
|
|
return nil
|
|
}
|
|
|
|
func finalizerCleanup(b *BeaconState) {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
for field, v := range b.sharedFieldReferences {
|
|
v.MinusRef()
|
|
if b.stateFieldLeaves[field].FieldReference() != nil {
|
|
b.stateFieldLeaves[field].FieldReference().MinusRef()
|
|
}
|
|
}
|
|
for i := range b.dirtyFields {
|
|
delete(b.dirtyFields, i)
|
|
}
|
|
for i := range b.rebuildTrie {
|
|
delete(b.rebuildTrie, i)
|
|
}
|
|
for i := range b.dirtyIndices {
|
|
delete(b.dirtyIndices, i)
|
|
}
|
|
for i := range b.sharedFieldReferences {
|
|
delete(b.sharedFieldReferences, i)
|
|
}
|
|
for i := range b.stateFieldLeaves {
|
|
delete(b.stateFieldLeaves, i)
|
|
}
|
|
state.StateCount.Sub(1)
|
|
}
|