Electra: BeaconState implementation (#13919)

* Electra: Beacon State

* Electra: Beacon state fixes from PR 13919

* Add missing tests - part 1

* Split eip_7251_root.go into different files and reuse/share code with historical state summaries root. It's identical!

* Add missing tests - part 2

* deposit receipts start index getters and setters (#13947)

* adding in getters and setters for deposit receipts start index

* adding tests

* gaz

* Add missing tests - part 3 of 3

Update the electra withdrawal example with a ssz state containing pending partial withdrawals

* add tests for beacon-chain/state/state-native/getters_balance_deposits.go

* Add electra field to testing/util/block.go execution payload

* godoc commentary on public methods

* Fix failing test

* Add balances index out of bounds check and relevant tests.

* Revert switch case electra

* Instead of copying spectest data into testdata, use the spectest dependency

* Deepsource fixes

* Address @rkapka PR feedback

* s/MaxPendingPartialsPerWithdrawalSweep/MaxPendingPartialsPerWithdrawalsSweep/

* Use multivalue slice compatible accessors for validator and balance in ActiveBalanceAtIndex

* More @rkapka feedback. What a great reviewer!

* More tests for branching logic in ExitEpochAndUpdateChurn

* fix build

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
This commit is contained in:
Preston Van Loon
2024-05-06 13:04:33 -05:00
committed by GitHub
parent 26355768a0
commit 9b2934f1f6
78 changed files with 3100 additions and 838 deletions

View File

@@ -325,7 +325,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return emptyAttri
@@ -342,7 +342,7 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
return emptyAttri
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return emptyAttri

View File

@@ -135,8 +135,6 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) {
}
func TestVerifyExitAndSignature(t *testing.T) {
undo := util.HackDenebMaxuint(t)
defer undo()
denebSlot, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
tests := []struct {

View File

@@ -145,7 +145,7 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) {
expectedWithdrawals, err := st.ExpectedWithdrawals()
expectedWithdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
return nil, errors.Wrap(err, "could not get expected withdrawals")
}

View File

@@ -78,6 +78,7 @@ go_test(
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",

View File

@@ -2,6 +2,7 @@ package helpers
import (
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/math"
)
// BalanceChurnLimit for the current active balance, in gwei.
@@ -18,12 +19,12 @@ import (
// get_total_active_balance(state) // CHURN_LIMIT_QUOTIENT
// )
// return churn - churn % EFFECTIVE_BALANCE_INCREMENT
func BalanceChurnLimit(activeBalanceGwei uint64) uint64 {
func BalanceChurnLimit(activeBalance math.Gwei) math.Gwei {
churn := max(
params.BeaconConfig().MinPerEpochChurnLimitElectra,
(activeBalanceGwei / params.BeaconConfig().ChurnLimitQuotient),
(uint64(activeBalance) / params.BeaconConfig().ChurnLimitQuotient),
)
return churn - churn%params.BeaconConfig().EffectiveBalanceIncrement
return math.Gwei(churn - churn%params.BeaconConfig().EffectiveBalanceIncrement)
}
// ActivationExitChurnLimit for the current active balance, in gwei.
@@ -36,8 +37,8 @@ func BalanceChurnLimit(activeBalanceGwei uint64) uint64 {
// Return the churn limit for the current epoch dedicated to activations and exits.
// """
// return min(MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT, get_balance_churn_limit(state))
func ActivationExitChurnLimit(activeBalanceGwei uint64) uint64 {
return min(params.BeaconConfig().MaxPerEpochActivationExitChurnLimit, BalanceChurnLimit(activeBalanceGwei))
func ActivationExitChurnLimit(activeBalance math.Gwei) math.Gwei {
return min(math.Gwei(params.BeaconConfig().MaxPerEpochActivationExitChurnLimit), BalanceChurnLimit(activeBalance))
}
// ConsolidationChurnLimit for the current active balance, in gwei.
@@ -47,6 +48,6 @@ func ActivationExitChurnLimit(activeBalanceGwei uint64) uint64 {
//
// def get_consolidation_churn_limit(state: BeaconState) -> Gwei:
// return get_balance_churn_limit(state) - get_activation_exit_churn_limit(state)
func ConsolidationChurnLimit(activeBalanceGwei uint64) uint64 {
return BalanceChurnLimit(activeBalanceGwei) - ActivationExitChurnLimit(activeBalanceGwei)
func ConsolidationChurnLimit(activeBalance math.Gwei) math.Gwei {
return BalanceChurnLimit(activeBalance) - ActivationExitChurnLimit(activeBalance)
}

View File

@@ -5,29 +5,30 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
)
func TestBalanceChurnLimit(t *testing.T) {
tests := []struct {
name string
activeBalance uint64
expected uint64
activeBalance math.Gwei
expected math.Gwei
}{
{
name: "less than MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA",
activeBalance: 111,
expected: params.BeaconConfig().MinPerEpochChurnLimitElectra,
expected: math.Gwei(params.BeaconConfig().MinPerEpochChurnLimitElectra),
},
{
name: "modulo EFFECTIVE_BALANCE_INCREMENT",
activeBalance: 111 + params.BeaconConfig().MinPerEpochChurnLimitElectra*params.BeaconConfig().ChurnLimitQuotient,
expected: params.BeaconConfig().MinPerEpochChurnLimitElectra,
activeBalance: math.Gwei(111 + params.BeaconConfig().MinPerEpochChurnLimitElectra*params.BeaconConfig().ChurnLimitQuotient),
expected: math.Gwei(params.BeaconConfig().MinPerEpochChurnLimitElectra),
},
{
name: "more than MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA",
activeBalance: 2000 * params.BeaconConfig().EffectiveBalanceIncrement * params.BeaconConfig().ChurnLimitQuotient,
expected: 2000 * params.BeaconConfig().EffectiveBalanceIncrement,
activeBalance: math.Gwei(2000 * params.BeaconConfig().EffectiveBalanceIncrement * params.BeaconConfig().ChurnLimitQuotient),
expected: math.Gwei(2000 * params.BeaconConfig().EffectiveBalanceIncrement),
},
}
@@ -41,18 +42,18 @@ func TestBalanceChurnLimit(t *testing.T) {
func TestActivationExitChurnLimit(t *testing.T) {
tests := []struct {
name string
activeBalance uint64
expected uint64
activeBalance math.Gwei
expected math.Gwei
}{
{
name: "less than MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT",
activeBalance: 1,
expected: params.BeaconConfig().MinPerEpochChurnLimitElectra,
expected: math.Gwei(params.BeaconConfig().MinPerEpochChurnLimitElectra),
},
{
name: "more than MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT",
activeBalance: 2000 * params.BeaconConfig().EffectiveBalanceIncrement * params.BeaconConfig().ChurnLimitQuotient,
expected: params.BeaconConfig().MaxPerEpochActivationExitChurnLimit,
activeBalance: math.Gwei(2000 * params.BeaconConfig().EffectiveBalanceIncrement * params.BeaconConfig().ChurnLimitQuotient),
expected: math.Gwei(params.BeaconConfig().MaxPerEpochActivationExitChurnLimit),
},
}
@@ -66,6 +67,6 @@ func TestActivationExitChurnLimit(t *testing.T) {
// FuzzConsolidationChurnLimit exercises BalanceChurnLimit and ActivationExitChurnLimit
func FuzzConsolidationChurnLimit(f *testing.F) {
f.Fuzz(func(t *testing.T, activeBalance uint64) {
helpers.ConsolidationChurnLimit(activeBalance)
helpers.ConsolidationChurnLimit(math.Gwei(activeBalance))
})
}

View File

@@ -96,7 +96,7 @@ func (s *Server) ExpectedWithdrawals(w http.ResponseWriter, r *http.Request) {
})
return
}
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "could not get expected withdrawals",

View File

@@ -140,7 +140,7 @@ func TestGetSpec(t *testing.T) {
config.PendingPartialWithdrawalsLimit = 80
config.MinActivationBalance = 81
config.PendingBalanceDepositLimit = 82
config.MaxPendingPartialsPerWithdrawalSweep = 83
config.MaxPendingPartialsPerWithdrawalsSweep = 83
config.PendingConsolidationsLimit = 84
config.MaxPartialWithdrawalsPerPayload = 85
config.FullExitRequestAmount = 86

View File

@@ -449,7 +449,7 @@ func (s *Server) sendPayloadAttributes(ctx context.Context, w http.ResponseWrite
SuggestedFeeRecipient: hexutil.Encode(headPayload.FeeRecipient()),
}
case version.Capella:
withdrawals, err := headState.ExpectedWithdrawals()
withdrawals, _, err := headState.ExpectedWithdrawals()
if err != nil {
return write(w, flusher, "Could not get head state expected withdrawals: "+err.Error())
}
@@ -460,7 +460,7 @@ func (s *Server) sendPayloadAttributes(ctx context.Context, w http.ResponseWrite
Withdrawals: structs.WithdrawalsFromConsensus(withdrawals),
}
case version.Deneb:
withdrawals, err := headState.ExpectedWithdrawals()
withdrawals, _, err := headState.ExpectedWithdrawals()
if err != nil {
return write(w, flusher, "Could not get head state expected withdrawals: "+err.Error())
}

View File

@@ -128,7 +128,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
var attr payloadattribute.Attributer
switch st.Version() {
case version.Deneb:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
}
@@ -143,7 +143,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
return nil, false, err
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
return nil, false, err
}

View File

@@ -13,6 +13,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",

View File

@@ -11,6 +11,7 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
@@ -56,6 +57,8 @@ type ReadOnlyBeaconState interface {
ReadOnlyParticipation
ReadOnlyInactivity
ReadOnlySyncCommittee
ReadOnlyDeposits
ReadOnlyConsolidations
ToProtoUnsafe() interface{}
ToProto() interface{}
GenesisTime() uint64
@@ -87,6 +90,9 @@ type WriteOnlyBeaconState interface {
WriteOnlyParticipation
WriteOnlyInactivity
WriteOnlySyncCommittee
WriteOnlyConsolidations
WriteOnlyWithdrawals
WriteOnlyDeposits
SetGenesisTime(val uint64) error
SetGenesisValidatorsRoot(val []byte) error
SetSlot(val primitives.Slot) error
@@ -98,8 +104,6 @@ type WriteOnlyBeaconState interface {
AppendHistoricalRoots(root [32]byte) error
AppendHistoricalSummaries(*ethpb.HistoricalSummary) error
SetLatestExecutionPayloadHeader(payload interfaces.ExecutionData) error
SetNextWithdrawalIndex(i uint64) error
SetNextWithdrawalValidatorIndex(i primitives.ValidatorIndex) error
SaveValidatorIndices()
}
@@ -134,6 +138,7 @@ type ReadOnlyBalances interface {
Balances() []uint64
BalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error)
BalancesLength() int
ActiveBalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error)
}
// ReadOnlyCheckpoint defines a struct which only has read access to checkpoint methods.
@@ -182,9 +187,11 @@ type ReadOnlyAttestations interface {
// ReadOnlyWithdrawals defines a struct which only has read access to withdrawal methods.
type ReadOnlyWithdrawals interface {
ExpectedWithdrawals() ([]*enginev1.Withdrawal, error)
ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, error)
NextWithdrawalValidatorIndex() (primitives.ValidatorIndex, error)
NextWithdrawalIndex() (uint64, error)
PendingBalanceToWithdraw(idx primitives.ValidatorIndex) (uint64, error)
NumPendingPartialWithdrawals() (uint64, error)
}
// ReadOnlyParticipation defines a struct which only has read access to participation methods.
@@ -204,6 +211,19 @@ type ReadOnlySyncCommittee interface {
NextSyncCommittee() (*ethpb.SyncCommittee, error)
}
type ReadOnlyDeposits interface {
DepositBalanceToConsume() (math.Gwei, error)
DepositReceiptsStartIndex() (uint64, error)
PendingBalanceDeposits() ([]*ethpb.PendingBalanceDeposit, error)
}
type ReadOnlyConsolidations interface {
ConsolidationBalanceToConsume() (math.Gwei, error)
EarliestConsolidationEpoch() (primitives.Epoch, error)
PendingConsolidations() ([]*ethpb.PendingConsolidation, error)
NumPendingConsolidations() (uint64, error)
}
// WriteOnlyBlockRoots defines a struct which only has write access to block roots methods.
type WriteOnlyBlockRoots interface {
SetBlockRoots(val [][]byte) error
@@ -222,6 +242,7 @@ type WriteOnlyEth1Data interface {
SetEth1DataVotes(val []*ethpb.Eth1Data) error
AppendEth1DataVotes(val *ethpb.Eth1Data) error
SetEth1DepositIndex(val uint64) error
ExitEpochAndUpdateChurn(exitBalance math.Gwei) (primitives.Epoch, error)
}
// WriteOnlyValidators defines a struct which only has write access to validators methods.
@@ -283,3 +304,24 @@ type WriteOnlySyncCommittee interface {
SetCurrentSyncCommittee(val *ethpb.SyncCommittee) error
SetNextSyncCommittee(val *ethpb.SyncCommittee) error
}
type WriteOnlyWithdrawals interface {
AppendPendingPartialWithdrawal(ppw *ethpb.PendingPartialWithdrawal) error
DequeuePartialWithdrawals(num uint64) error
SetNextWithdrawalIndex(i uint64) error
SetNextWithdrawalValidatorIndex(i primitives.ValidatorIndex) error
}
type WriteOnlyConsolidations interface {
AppendPendingConsolidation(val *ethpb.PendingConsolidation) error
SetConsolidationBalanceToConsume(math.Gwei) error
SetEarliestConsolidationEpoch(epoch primitives.Epoch) error
SetPendingConsolidations(val []*ethpb.PendingConsolidation) error
}
type WriteOnlyDeposits interface {
AppendPendingBalanceDeposit(index primitives.ValidatorIndex, amount uint64) error
SetDepositReceiptsStartIndex(index uint64) error
SetPendingBalanceDeposits(val []*ethpb.PendingBalanceDeposit) error
SetDepositBalanceToConsume(math.Gwei) error
}

View File

@@ -3,11 +3,16 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"beacon_state_mainnet.go",
"beacon_state_minimal.go", # keep
"doc.go",
"error.go",
"getters_attestation.go",
"getters_balance_deposits.go",
"getters_block.go",
"getters_checkpoint.go",
"getters_consolidation.go",
"getters_deposit_receipts.go",
"getters_eth1.go",
"getters_misc.go",
"getters_participation.go",
@@ -22,8 +27,12 @@ go_library(
"proofs.go",
"readonly_validator.go",
"setters_attestation.go",
"setters_balance_deposits.go",
"setters_block.go",
"setters_checkpoint.go",
"setters_churn.go",
"setters_consolidation.go",
"setters_deposit_receipts.go",
"setters_eth1.go",
"setters_misc.go",
"setters_participation.go",
@@ -38,13 +47,11 @@ go_library(
"state_trie.go",
"types.go",
"validator_index_cache.go",
] + select({
"//config:mainnet": ["beacon_state_mainnet.go"],
"//config:minimal": ["beacon_state_minimal.go"],
}),
],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/fieldtrie:go_default_library",
@@ -82,8 +89,11 @@ go_test(
name = "go_default_test",
srcs = [
"getters_attestation_test.go",
"getters_balance_deposits_test.go",
"getters_block_test.go",
"getters_checkpoint_test.go",
"getters_consolidation_test.go",
"getters_deposit_receipts_test.go",
"getters_participation_test.go",
"getters_test.go",
"getters_validator_test.go",
@@ -94,9 +104,14 @@ go_test(
"readonly_validator_test.go",
"references_test.go",
"setters_attestation_test.go",
"setters_balance_deposits_test.go",
"setters_churn_test.go",
"setters_consolidation_test.go",
"setters_deposit_receipts_test.go",
"setters_eth1_test.go",
"setters_misc_test.go",
"setters_participation_test.go",
"setters_payload_header_test.go",
"setters_validator_test.go",
"setters_withdrawal_test.go",
"state_fuzz_test.go",
@@ -105,9 +120,12 @@ go_test(
"types_test.go",
"validator_index_cache_test.go",
],
data = glob(["testdata/**"]),
data = glob(["testdata/**"]) + [
"@consensus_spec_tests_mainnet//:test_data",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native/types:go_default_library",
@@ -116,11 +134,14 @@ go_test(
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/trie:go_default_library",
"//crypto/rand:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/interop:go_default_library",
@@ -128,9 +149,13 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_google_go_cmp//cmp:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//testing/protocmp:go_default_library",
],
)

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
@@ -57,9 +58,21 @@ type BeaconState struct {
latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader
latestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella
latestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb
latestExecutionPayloadHeaderElectra *enginev1.ExecutionPayloadHeaderElectra
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex primitives.ValidatorIndex
// Electra fields
depositReceiptsStartIndex uint64
depositBalanceToConsume math.Gwei
exitBalanceToConsume math.Gwei
earliestExitEpoch primitives.Epoch
consolidationBalanceToConsume math.Gwei
earliestConsolidationEpoch primitives.Epoch
pendingBalanceDeposits []*ethpb.PendingBalanceDeposit // pending_balance_deposits: List[PendingBalanceDeposit, PENDING_BALANCE_DEPOSITS_LIMIT]
pendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal // pending_partial_withdrawals: List[PartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
id uint64
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
@@ -103,8 +116,19 @@ type beaconStateMarshalable struct {
NextSyncCommittee *ethpb.SyncCommittee `json:"next_sync_committee" yaml:"next_sync_committee"`
LatestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader `json:"latest_execution_payload_header" yaml:"latest_execution_payload_header"`
LatestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella `json:"latest_execution_payload_header_capella" yaml:"latest_execution_payload_header_capella"`
LatestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb `json:"latest_execution_payload_header_deneb" yaml:"latest_execution_payload_header_deneb"`
LatestExecutionPayloadHeaderElectra *enginev1.ExecutionPayloadHeaderElectra `json:"latest_execution_payload_header_electra" yaml:"latest_execution_payload_header_electra"`
NextWithdrawalIndex uint64 `json:"next_withdrawal_index" yaml:"next_withdrawal_index"`
NextWithdrawalValidatorIndex primitives.ValidatorIndex `json:"next_withdrawal_validator_index" yaml:"next_withdrawal_validator_index"`
DepositReceiptsStartIndex uint64 `json:"deposit_receipts_start_index" yaml:"deposit_receipts_start_index"`
DepositBalanceToConsume math.Gwei `json:"deposit_balance_to_consume" yaml:"deposit_balance_to_consume"`
ExitBalanceToConsume math.Gwei `json:"exit_balance_to_consume" yaml:"exit_balance_to_consume"`
EarliestExitEpoch primitives.Epoch `json:"earliest_exit_epoch" yaml:"earliest_exit_epoch"`
ConsolidationBalanceToConsume math.Gwei `json:"consolidation_balance_to_consume" yaml:"consolidation_balance_to_consume"`
EarliestConsolidationEpoch primitives.Epoch `json:"earliest_consolidation_epoch" yaml:"earliest_consolidation_epoch"`
PendingBalanceDeposits []*ethpb.PendingBalanceDeposit `json:"pending_balance_deposits" yaml:"pending_balance_deposits"`
PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"`
PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"`
}
func (b *BeaconState) MarshalJSON() ([]byte, error) {
@@ -162,8 +186,19 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) {
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader,
LatestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella,
LatestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb,
LatestExecutionPayloadHeaderElectra: b.latestExecutionPayloadHeaderElectra,
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
DepositReceiptsStartIndex: b.depositReceiptsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingBalanceDeposits: b.pendingBalanceDeposits,
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
PendingConsolidations: b.pendingConsolidations,
}
return json.Marshal(marshalable)
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
@@ -57,9 +58,21 @@ type BeaconState struct {
latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader
latestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella
latestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb
latestExecutionPayloadHeaderElectra *enginev1.ExecutionPayloadHeaderElectra
nextWithdrawalIndex uint64
nextWithdrawalValidatorIndex primitives.ValidatorIndex
// Electra fields
depositReceiptsStartIndex uint64
depositBalanceToConsume math.Gwei
exitBalanceToConsume math.Gwei
earliestExitEpoch primitives.Epoch
consolidationBalanceToConsume math.Gwei
earliestConsolidationEpoch primitives.Epoch
pendingBalanceDeposits []*ethpb.PendingBalanceDeposit // pending_balance_deposits: List[PendingBalanceDeposit, PENDING_BALANCE_DEPOSITS_LIMIT]
pendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal // pending_partial_withdrawals: List[PartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
id uint64
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
@@ -103,8 +116,19 @@ type beaconStateMarshalable struct {
NextSyncCommittee *ethpb.SyncCommittee `json:"next_sync_committee" yaml:"next_sync_committee"`
LatestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader `json:"latest_execution_payload_header" yaml:"latest_execution_payload_header"`
LatestExecutionPayloadHeaderCapella *enginev1.ExecutionPayloadHeaderCapella `json:"latest_execution_payload_header_capella" yaml:"latest_execution_payload_header_capella"`
LatestExecutionPayloadHeaderDeneb *enginev1.ExecutionPayloadHeaderDeneb `json:"latest_execution_payload_header_deneb" yaml:"latest_execution_payload_header_deneb"`
LatestExecutionPayloadHeaderElectra *enginev1.ExecutionPayloadHeaderElectra `json:"latest_execution_payload_header_electra" yaml:"latest_execution_payload_header_electra"`
NextWithdrawalIndex uint64 `json:"next_withdrawal_index" yaml:"next_withdrawal_index"`
NextWithdrawalValidatorIndex primitives.ValidatorIndex `json:"next_withdrawal_validator_index" yaml:"next_withdrawal_validator_index"`
DepositReceiptsStartIndex uint64 `json:"deposit_receipts_start_index" yaml:"deposit_receipts_start_index"`
DepositBalanceToConsume math.Gwei `json:"deposit_balance_to_consume" yaml:"deposit_balance_to_consume"`
ExitBalanceToConsume math.Gwei `json:"exit_balance_to_consume" yaml:"exit_balance_to_consume"`
EarliestExitEpoch primitives.Epoch `json:"earliest_exit_epoch" yaml:"earliest_exit_epoch"`
ConsolidationBalanceToConsume math.Gwei `json:"consolidation_balance_to_consume" yaml:"consolidation_balance_to_consume"`
EarliestConsolidationEpoch primitives.Epoch `json:"earliest_consolidation_epoch" yaml:"earliest_consolidation_epoch"`
PendingBalanceDeposits []*ethpb.PendingBalanceDeposit `json:"pending_balance_deposits" yaml:"pending_balance_deposits"`
PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"`
PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"`
}
func (b *BeaconState) MarshalJSON() ([]byte, error) {
@@ -162,8 +186,19 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) {
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader,
LatestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella,
LatestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb,
LatestExecutionPayloadHeaderElectra: b.latestExecutionPayloadHeaderElectra,
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
DepositReceiptsStartIndex: b.depositReceiptsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingBalanceDeposits: b.pendingBalanceDeposits,
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
PendingConsolidations: b.pendingConsolidations,
}
return json.Marshal(marshalable)
}

View File

@@ -0,0 +1,39 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// DepositBalanceToConsume is a non-mutating call to the beacon state which returns the value of the
// deposit balance to consume field. This method requires access to the RLock on the state and only
// applies in electra or later.
func (b *BeaconState) DepositBalanceToConsume() (math.Gwei, error) {
if b.version < version.Electra {
return 0, errNotSupported("DepositBalanceToConsume", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.depositBalanceToConsume, nil
}
// PendingBalanceDeposits is a non-mutating call to the beacon state which returns a deep copy of
// the pending balance deposit slice. This method requires access to the RLock on the state and
// only applies in electra or later.
func (b *BeaconState) PendingBalanceDeposits() ([]*ethpb.PendingBalanceDeposit, error) {
if b.version < version.Electra {
return nil, errNotSupported("PendingBalanceDeposits", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.pendingBalanceDepositsVal(), nil
}
func (b *BeaconState) pendingBalanceDepositsVal() []*ethpb.PendingBalanceDeposit {
if b.pendingBalanceDeposits == nil {
return nil
}
return ethpb.CopyPendingBalanceDeposits(b.pendingBalanceDeposits)
}

View File

@@ -0,0 +1,50 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestDepositBalanceToConsume(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
DepositBalanceToConsume: 44,
})
require.NoError(t, err)
dbtc, err := s.DepositBalanceToConsume()
require.NoError(t, err)
require.Equal(t, math.Gwei(44), dbtc)
// Fails for older than electra state
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
_, err = s.DepositBalanceToConsume()
require.ErrorContains(t, "not supported", err)
}
func TestPendingBalanceDeposits(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
PendingBalanceDeposits: []*eth.PendingBalanceDeposit{
{Index: 1, Amount: 2},
{Index: 3, Amount: 4},
},
})
require.NoError(t, err)
pbd, err := s.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 2, len(pbd))
require.Equal(t, primitives.ValidatorIndex(1), pbd[0].Index)
require.Equal(t, uint64(2), pbd[0].Amount)
require.Equal(t, primitives.ValidatorIndex(3), pbd[1].Index)
require.Equal(t, uint64(4), pbd[1].Amount)
// Fails for older than electra state
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
_, err = s.DepositBalanceToConsume()
require.ErrorContains(t, "not supported", err)
}

View File

@@ -0,0 +1,64 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// EarliestConsolidationEpoch is a non-mutating call to the beacon state which returns the value of
// the earliest consolidation epoch field. This method requires access to the RLock on the state and
// only applies in electra or later.
func (b *BeaconState) EarliestConsolidationEpoch() (primitives.Epoch, error) {
if b.version < version.Electra {
return 0, errNotSupported("EarliestConsolidationEpoch", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.earliestConsolidationEpoch, nil
}
// ConsolidationBalanceToConsume is a non-mutating call to the beacon state which returns the value
// of the consolidation balance to consume field. This method requires access to the RLock on the
// state and only applies in electra or later.
func (b *BeaconState) ConsolidationBalanceToConsume() (math.Gwei, error) {
if b.version < version.Electra {
return 0, errNotSupported("ConsolidationBalanceToConsume", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.consolidationBalanceToConsume, nil
}
// PendingConsolidations is a non-mutating call to the beacon state which returns a deep copy of the
// pending consolidations slice. This method requires access to the RLock on the state and only
// applies in electra or later.
func (b *BeaconState) PendingConsolidations() ([]*ethpb.PendingConsolidation, error) {
if b.version < version.Electra {
return nil, errNotSupported("PendingConsolidations", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.pendingConsolidationsVal(), nil
}
// NumPendingConsolidations is a non-mutating call to the beacon state which returns the number of
// pending consolidations in the beacon state. This method requires access to the RLock on the state
// and only applies in electra or later.
func (b *BeaconState) NumPendingConsolidations() (uint64, error) {
if b.version < version.Electra {
return 0, errNotSupported("NumPendingConsolidations", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return uint64(len(b.pendingConsolidations)), nil
}
func (b *BeaconState) pendingConsolidationsVal() []*ethpb.PendingConsolidation {
if b.pendingConsolidations == nil {
return nil
}
return ethpb.CopyPendingConsolidations(b.pendingConsolidations)
}

View File

@@ -0,0 +1,118 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestEarliestConsolidationEpoch(t *testing.T) {
t.Run("electra returns expected value", func(t *testing.T) {
want := primitives.Epoch(10)
st, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{
EarliestConsolidationEpoch: want,
})
require.NoError(t, err)
got, err := st.EarliestConsolidationEpoch()
require.NoError(t, err)
require.Equal(t, want, got)
})
t.Run("earlier than electra returns error", func(t *testing.T) {
st, err := state_native.InitializeFromProtoDeneb(&ethpb.BeaconStateDeneb{})
require.NoError(t, err)
_, err = st.EarliestConsolidationEpoch()
require.ErrorContains(t, "is not supported", err)
})
}
func TestConsolidationBalanceToConsume(t *testing.T) {
t.Run("electra returns expected value", func(t *testing.T) {
want := math.Gwei(10)
st, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{
ConsolidationBalanceToConsume: want,
})
require.NoError(t, err)
got, err := st.ConsolidationBalanceToConsume()
require.NoError(t, err)
require.Equal(t, want, got)
})
t.Run("earlier than electra returns error", func(t *testing.T) {
st, err := state_native.InitializeFromProtoDeneb(&ethpb.BeaconStateDeneb{})
require.NoError(t, err)
_, err = st.ConsolidationBalanceToConsume()
require.ErrorContains(t, "is not supported", err)
})
}
func TestPendingConsolidations(t *testing.T) {
t.Run("electra returns expected value", func(t *testing.T) {
want := []*ethpb.PendingConsolidation{
{
SourceIndex: 1,
TargetIndex: 2,
},
{
SourceIndex: 3,
TargetIndex: 4,
},
{
SourceIndex: 5,
TargetIndex: 6,
},
{
SourceIndex: 7,
TargetIndex: 8,
},
}
st, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{
PendingConsolidations: want,
})
require.NoError(t, err)
got, err := st.PendingConsolidations()
require.NoError(t, err)
require.DeepEqual(t, want, got)
})
t.Run("earlier than electra returns error", func(t *testing.T) {
st, err := state_native.InitializeFromProtoDeneb(&ethpb.BeaconStateDeneb{})
require.NoError(t, err)
_, err = st.PendingConsolidations()
require.ErrorContains(t, "is not supported", err)
})
}
func TestNumPendingConsolidations(t *testing.T) {
t.Run("electra returns expected value", func(t *testing.T) {
want := uint64(4)
st, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{
PendingConsolidations: []*ethpb.PendingConsolidation{
{
SourceIndex: 1,
TargetIndex: 2,
},
{
SourceIndex: 3,
TargetIndex: 4,
},
{
SourceIndex: 5,
TargetIndex: 6,
},
{
SourceIndex: 7,
TargetIndex: 8,
},
},
})
require.NoError(t, err)
got, err := st.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, want, got)
})
}

View File

@@ -0,0 +1,16 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// DepositReceiptsStartIndex is used for returning the deposit receipts start index which is used for eip6110
func (b *BeaconState) DepositReceiptsStartIndex() (uint64, error) {
if b.version < version.Electra {
return 0, errNotSupported("DepositReceiptsStartIndex", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.depositReceiptsStartIndex, nil
}

View File

@@ -0,0 +1,26 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestDepositReceiptsStartIndex(t *testing.T) {
t.Run("previous fork returns expected error", func(t *testing.T) {
dState, _ := util.DeterministicGenesisState(t, 1)
_, err := dState.DepositReceiptsStartIndex()
require.ErrorContains(t, "is not supported", err)
})
t.Run("electra returns expected value", func(t *testing.T) {
want := uint64(2)
dState, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{DepositReceiptsStartIndex: want})
require.NoError(t, err)
got, err := dState.DepositReceiptsStartIndex()
require.NoError(t, err)
require.Equal(t, want, got)
})
}

View File

@@ -1,6 +1,7 @@
package state_native
import (
"fmt"
"math/big"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
@@ -19,15 +20,18 @@ func (b *BeaconState) LatestExecutionPayloadHeader() (interfaces.ExecutionData,
b.lock.RLock()
defer b.lock.RUnlock()
if b.version == version.Bellatrix {
switch b.version {
case version.Bellatrix:
return blocks.WrappedExecutionPayloadHeader(b.latestExecutionPayloadHeaderVal())
}
if b.version == version.Capella {
case version.Capella:
return blocks.WrappedExecutionPayloadHeaderCapella(b.latestExecutionPayloadHeaderCapellaVal(), big.NewInt(0))
case version.Deneb:
return blocks.WrappedExecutionPayloadHeaderDeneb(b.latestExecutionPayloadHeaderDenebVal(), big.NewInt(0))
case version.Electra:
return blocks.WrappedExecutionPayloadHeaderElectra(b.latestExecutionPayloadHeaderElectraVal(), big.NewInt(0))
default:
return nil, fmt.Errorf("unsupported version (%s) for latest execution payload header", version.String(b.version))
}
return blocks.WrappedExecutionPayloadHeaderDeneb(b.latestExecutionPayloadHeaderDenebVal(), big.NewInt(0))
}
// latestExecutionPayloadHeaderVal of the beacon state.
@@ -45,3 +49,7 @@ func (b *BeaconState) latestExecutionPayloadHeaderCapellaVal() *enginev1.Executi
func (b *BeaconState) latestExecutionPayloadHeaderDenebVal() *enginev1.ExecutionPayloadHeaderDeneb {
return ethpb.CopyExecutionPayloadHeaderDeneb(b.latestExecutionPayloadHeaderDeneb)
}
func (b *BeaconState) latestExecutionPayloadHeaderElectraVal() *enginev1.ExecutionPayloadHeaderElectra {
return ethpb.CopyExecutionPayloadHeaderElectra(b.latestExecutionPayloadHeaderElectra)
}

View File

@@ -172,6 +172,46 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummaries,
}
case version.Electra:
return &ethpb.BeaconStateElectra{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.fork,
LatestBlockHeader: b.latestBlockHeader,
BlockRoots: br,
StateRoots: sr,
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1Data,
Eth1DataVotes: b.eth1DataVotes,
Eth1DepositIndex: b.eth1DepositIndex,
Validators: vals,
Balances: bals,
RandaoMixes: rm,
Slashings: b.slashings,
PreviousEpochParticipation: b.previousEpochParticipation,
CurrentEpochParticipation: b.currentEpochParticipation,
JustificationBits: b.justificationBits,
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
FinalizedCheckpoint: b.finalizedCheckpoint,
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommittee,
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderElectra,
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummaries,
DepositReceiptsStartIndex: b.depositReceiptsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingBalanceDeposits: b.pendingBalanceDeposits,
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
PendingConsolidations: b.pendingConsolidations,
}
default:
return nil
}
@@ -338,6 +378,46 @@ func (b *BeaconState) ToProto() interface{} {
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummariesVal(),
}
case version.Electra:
return &ethpb.BeaconStateElectra{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.forkVal(),
LatestBlockHeader: b.latestBlockHeaderVal(),
BlockRoots: br,
StateRoots: sr,
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1DataVal(),
Eth1DataVotes: b.eth1DataVotesVal(),
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validatorsVal(),
Balances: b.balancesVal(),
RandaoMixes: rm,
Slashings: b.slashingsVal(),
PreviousEpochParticipation: b.previousEpochParticipationVal(),
CurrentEpochParticipation: b.currentEpochParticipationVal(),
JustificationBits: b.justificationBitsVal(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
FinalizedCheckpoint: b.finalizedCheckpointVal(),
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
NextSyncCommittee: b.nextSyncCommitteeVal(),
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderElectraVal(),
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummariesVal(),
DepositReceiptsStartIndex: b.depositReceiptsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingBalanceDeposits: b.pendingBalanceDepositsVal(),
PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(),
PendingConsolidations: b.pendingConsolidationsVal(),
}
default:
return nil
}
@@ -449,7 +529,17 @@ func ProtobufBeaconStateCapella(s interface{}) (*ethpb.BeaconStateCapella, error
func ProtobufBeaconStateDeneb(s interface{}) (*ethpb.BeaconStateDeneb, error) {
pbState, ok := s.(*ethpb.BeaconStateDeneb)
if !ok {
return nil, errors.New("input is not type pb.ProtobufBeaconStateDeneb")
return nil, errors.New("input is not type pb.BeaconStateDeneb")
}
return pbState, nil
}
// ProtobufBeaconStateElectra transforms an input into beacon state Electra in the form of protobuf.
// Error is returned if the input is not type protobuf beacon state.
func ProtobufBeaconStateElectra(s interface{}) (*ethpb.BeaconStateElectra, error) {
pbState, ok := s.(*ethpb.BeaconStateElectra)
if !ok {
return nil, errors.New("input is not type pb.BeaconStateElectra")
}
return pbState, nil
}

View File

@@ -2,6 +2,7 @@ package state_native
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
@@ -413,3 +414,59 @@ func (b *BeaconState) inactivityScoresVal() []uint64 {
copy(res, b.inactivityScores)
return res
}
// ActiveBalanceAtIndex returns the active balance for the given validator.
//
// Spec definition:
//
// def get_active_balance(state: BeaconState, validator_index: ValidatorIndex) -> Gwei:
// max_effective_balance = get_validator_max_effective_balance(state.validators[validator_index])
// return min(state.balances[validator_index], max_effective_balance)
func (b *BeaconState) ActiveBalanceAtIndex(i primitives.ValidatorIndex) (uint64, error) {
if b.version < version.Electra {
return 0, errNotSupported("ActiveBalanceAtIndex", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
v, err := b.validatorAtIndex(i)
if err != nil {
return 0, err
}
bal, err := b.balanceAtIndex(i)
if err != nil {
return 0, err
}
return min(bal, helpers.ValidatorMaxEffectiveBalance(v)), nil
}
// PendingBalanceToWithdraw returns the sum of all pending withdrawals for the given validator.
//
// Spec definition:
//
// def get_pending_balance_to_withdraw(state: BeaconState, validator_index: ValidatorIndex) -> Gwei:
// return sum(
// withdrawal.amount for withdrawal in state.pending_partial_withdrawals if withdrawal.index == validator_index)
func (b *BeaconState) PendingBalanceToWithdraw(idx primitives.ValidatorIndex) (uint64, error) {
if b.version < version.Electra {
return 0, errNotSupported("PendingBalanceToWithdraw", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
// TODO: Consider maintaining this value in the state, if it's a potential bottleneck.
// This is n*m complexity, but this method can only be called
// MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD per slot. A more optimized storage indexing such as a
// lookup map could be used to reduce the complexity marginally.
var sum uint64
for _, w := range b.pendingPartialWithdrawals {
if w.Index == idx {
sum += w.Amount
}
}
return sum, nil
}

View File

@@ -1,12 +1,15 @@
package state_native_test
import (
"math"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
statenative "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
testtmpl "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/testing"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
@@ -66,3 +69,79 @@ func TestValidatorIndexes(t *testing.T) {
require.Equal(t, hexutil.Encode(readOnlyBytes[:]), hexutil.Encode(byteValue[:]))
})
}
func TestActiveBalanceAtIndex(t *testing.T) {
// Test setup with a state with 4 validators.
// Validators 0 & 1 have compounding withdrawal credentials while validators 2 & 3 have BLS withdrawal credentials.
pb := &ethpb.BeaconStateElectra{
Validators: []*ethpb.Validator{
{
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte},
},
{
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte},
},
{
WithdrawalCredentials: []byte{params.BeaconConfig().BLSWithdrawalPrefixByte},
},
{
WithdrawalCredentials: []byte{params.BeaconConfig().BLSWithdrawalPrefixByte},
},
},
Balances: []uint64{
55,
math.MaxUint64,
55,
math.MaxUint64,
},
}
state, err := statenative.InitializeFromProtoUnsafeElectra(pb)
require.NoError(t, err)
ab, err := state.ActiveBalanceAtIndex(0)
require.NoError(t, err)
require.Equal(t, uint64(55), ab)
ab, err = state.ActiveBalanceAtIndex(1)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxEffectiveBalanceElectra, ab)
ab, err = state.ActiveBalanceAtIndex(2)
require.NoError(t, err)
require.Equal(t, uint64(55), ab)
ab, err = state.ActiveBalanceAtIndex(3)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance, ab)
// Accessing a validator index out of bounds should error.
_, err = state.ActiveBalanceAtIndex(4)
require.ErrorIs(t, err, consensus_types.ErrOutOfBounds)
// Accessing a validator wwhere balance slice is out of bounds for some reason.
require.NoError(t, state.SetBalances([]uint64{}))
_, err = state.ActiveBalanceAtIndex(0)
require.ErrorIs(t, err, consensus_types.ErrOutOfBounds)
}
func TestPendingBalanceToWithdraw(t *testing.T) {
pb := &ethpb.BeaconStateElectra{
PendingPartialWithdrawals: []*ethpb.PendingPartialWithdrawal{
{
Amount: 100,
},
{
Amount: 200,
},
{
Amount: 300,
},
},
}
state, err := statenative.InitializeFromProtoUnsafeElectra(pb)
require.NoError(t, err)
ab, err := state.PendingBalanceToWithdraw(0)
require.NoError(t, err)
require.Equal(t, uint64(600), ab)
}

View File

@@ -1,7 +1,10 @@
package state_native
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
@@ -41,10 +44,64 @@ func (b *BeaconState) NextWithdrawalValidatorIndex() (primitives.ValidatorIndex,
// ExpectedWithdrawals returns the withdrawals that a proposer will need to pack in the next block
// applied to the current state. It is also used by validators to check that the execution payload carried
// the right number of withdrawals
func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
// the right number of withdrawals. Note: The number of partial withdrawals will be zero before EIP-7251.
//
// Spec definition:
//
// def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
// epoch = get_current_epoch(state)
// withdrawal_index = state.next_withdrawal_index
// validator_index = state.next_withdrawal_validator_index
// withdrawals: List[Withdrawal] = []
//
// # [New in Electra:EIP7251] Consume pending partial withdrawals
// for withdrawal in state.pending_partial_withdrawals:
// if withdrawal.withdrawable_epoch > epoch or len(withdrawals) == MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP:
// break
//
// validator = state.validators[withdrawal.index]
// has_sufficient_effective_balance = validator.effective_balance >= MIN_ACTIVATION_BALANCE
// has_excess_balance = state.balances[withdrawal.index] > MIN_ACTIVATION_BALANCE
// if validator.exit_epoch == FAR_FUTURE_EPOCH and has_sufficient_effective_balance and has_excess_balance:
// withdrawable_balance = min(state.balances[withdrawal.index] - MIN_ACTIVATION_BALANCE, withdrawal.amount)
// withdrawals.append(Withdrawal(
// index=withdrawal_index,
// validator_index=withdrawal.index,
// address=ExecutionAddress(validator.withdrawal_credentials[12:]),
// amount=withdrawable_balance,
// ))
// withdrawal_index += WithdrawalIndex(1)
//
// partial_withdrawals_count = len(withdrawals)
//
// # Sweep for remaining.
// bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
// for _ in range(bound):
// validator = state.validators[validator_index]
// balance = state.balances[validator_index]
// if is_fully_withdrawable_validator(validator, balance, epoch):
// withdrawals.append(Withdrawal(
// index=withdrawal_index,
// validator_index=validator_index,
// address=ExecutionAddress(validator.withdrawal_credentials[12:]),
// amount=balance,
// ))
// withdrawal_index += WithdrawalIndex(1)
// elif is_partially_withdrawable_validator(validator, balance):
// withdrawals.append(Withdrawal(
// index=withdrawal_index,
// validator_index=validator_index,
// address=ExecutionAddress(validator.withdrawal_credentials[12:]),
// amount=balance - get_validator_max_effective_balance(validator), # [Modified in Electra:EIP7251]
// ))
// withdrawal_index += WithdrawalIndex(1)
// if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
// break
// validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
// return withdrawals, partial_withdrawals_count
func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, uint64, error) {
if b.version < version.Capella {
return nil, errNotSupported("ExpectedWithdrawals", b.version)
return nil, 0, errNotSupported("ExpectedWithdrawals", b.version)
}
b.lock.RLock()
@@ -55,18 +112,49 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
withdrawalIndex := b.nextWithdrawalIndex
epoch := slots.ToEpoch(b.slot)
// Electra partial withdrawals functionality.
if b.version >= version.Electra {
for _, w := range b.pendingPartialWithdrawals {
if w.WithdrawableEpoch > epoch || len(withdrawals) >= int(params.BeaconConfig().MaxPendingPartialsPerWithdrawalsSweep) {
break
}
v, err := b.validatorAtIndex(w.Index)
if err != nil {
return nil, 0, fmt.Errorf("failed to determine withdrawals at index %d: %w", w.Index, err)
}
vBal, err := b.balanceAtIndex(w.Index)
if err != nil {
return nil, 0, fmt.Errorf("could not retrieve balance at index %d: %w", w.Index, err)
}
hasSufficientEffectiveBalance := v.EffectiveBalance >= params.BeaconConfig().MinActivationBalance
hasExcessBalance := vBal > params.BeaconConfig().MinActivationBalance
if v.ExitEpoch == params.BeaconConfig().FarFutureEpoch && hasSufficientEffectiveBalance && hasExcessBalance {
amount := min(vBal-params.BeaconConfig().MinActivationBalance, w.Amount)
withdrawals = append(withdrawals, &enginev1.Withdrawal{
Index: withdrawalIndex,
ValidatorIndex: w.Index,
Address: v.WithdrawalCredentials[12:],
Amount: amount,
})
withdrawalIndex++
}
}
}
partialWithdrawalsCount := uint64(len(withdrawals))
validatorsLen := b.validatorsLen()
bound := mathutil.Min(uint64(validatorsLen), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
for i := uint64(0); i < bound; i++ {
val, err := b.validatorAtIndex(validatorIndex)
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve validator at index %d", validatorIndex)
return nil, 0, errors.Wrapf(err, "could not retrieve validator at index %d", validatorIndex)
}
balance, err := b.balanceAtIndex(validatorIndex)
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve balance at index %d", validatorIndex)
return nil, 0, errors.Wrapf(err, "could not retrieve balance at index %d", validatorIndex)
}
if balance > 0 && isFullyWithdrawableValidator(val, epoch) {
if helpers.IsFullyWithdrawableValidator(val, balance, epoch) {
withdrawals = append(withdrawals, &enginev1.Withdrawal{
Index: withdrawalIndex,
ValidatorIndex: validatorIndex,
@@ -74,12 +162,12 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
Amount: balance,
})
withdrawalIndex++
} else if isPartiallyWithdrawableValidator(val, balance) {
} else if helpers.IsPartiallyWithdrawableValidator(val, balance, epoch) {
withdrawals = append(withdrawals, &enginev1.Withdrawal{
Index: withdrawalIndex,
ValidatorIndex: validatorIndex,
Address: bytesutil.SafeCopyBytes(val.WithdrawalCredentials[ETH1AddressOffset:]),
Amount: balance - params.BeaconConfig().MaxEffectiveBalance,
Amount: balance - helpers.ValidatorMaxEffectiveBalance(val),
})
withdrawalIndex++
}
@@ -91,36 +179,17 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
validatorIndex = 0
}
}
return withdrawals, nil
return withdrawals, partialWithdrawalsCount, nil
}
// hasETH1WithdrawalCredential returns whether the validator has an ETH1
// Withdrawal prefix. It assumes that the caller has a lock on the state
func hasETH1WithdrawalCredential(val *ethpb.Validator) bool {
if val == nil {
return false
}
cred := val.WithdrawalCredentials
return len(cred) > 0 && cred[0] == params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
func (b *BeaconState) pendingPartialWithdrawalsVal() []*ethpb.PendingPartialWithdrawal {
return ethpb.CopyPendingPartialWithdrawals(b.pendingPartialWithdrawals)
}
// isFullyWithdrawableValidator returns whether the validator is able to perform a full
// withdrawal. This differ from the spec helper in that the balance > 0 is not
// checked. This function assumes that the caller holds a lock on the state
func isFullyWithdrawableValidator(val *ethpb.Validator, epoch primitives.Epoch) bool {
if val == nil {
return false
func (b *BeaconState) NumPendingPartialWithdrawals() (uint64, error) {
if b.version < version.Electra {
return 0, errNotSupported("NumPendingPartialWithdrawals", b.version)
}
return hasETH1WithdrawalCredential(val) && val.WithdrawableEpoch <= epoch
}
// isPartiallyWithdrawable returns whether the validator is able to perform a
// partial withdrawal. This function assumes that the caller has a lock on the state
func isPartiallyWithdrawableValidator(val *ethpb.Validator, balance uint64) bool {
if val == nil {
return false
}
hasMaxBalance := val.EffectiveBalance == params.BeaconConfig().MaxEffectiveBalance
hasExcessBalance := balance > params.BeaconConfig().MaxEffectiveBalance
return hasETH1WithdrawalCredential(val) && hasExcessBalance && hasMaxBalance
return uint64(len(b.pendingPartialWithdrawals)), nil
}

View File

@@ -1,8 +1,10 @@
package state_native
package state_native_test
import (
"testing"
"github.com/golang/snappy"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
@@ -10,588 +12,341 @@ import (
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestNextWithdrawalIndex(t *testing.T) {
t.Run("ok for deneb", func(t *testing.T) {
s := BeaconState{version: version.Deneb, nextWithdrawalIndex: 123}
s, err := state_native.InitializeFromProtoDeneb(&ethpb.BeaconStateDeneb{NextWithdrawalIndex: 123})
require.NoError(t, err)
i, err := s.NextWithdrawalIndex()
require.NoError(t, err)
assert.Equal(t, uint64(123), i)
})
t.Run("ok", func(t *testing.T) {
s := BeaconState{version: version.Capella, nextWithdrawalIndex: 123}
s, err := state_native.InitializeFromProtoCapella(&ethpb.BeaconStateCapella{NextWithdrawalIndex: 123})
require.NoError(t, err)
i, err := s.NextWithdrawalIndex()
require.NoError(t, err)
assert.Equal(t, uint64(123), i)
})
t.Run("version before Capella not supported", func(t *testing.T) {
s := BeaconState{version: version.Bellatrix}
_, err := s.NextWithdrawalIndex()
s, err := state_native.InitializeFromProtoBellatrix(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
_, err = s.NextWithdrawalIndex()
assert.ErrorContains(t, "NextWithdrawalIndex is not supported", err)
})
}
func TestNextWithdrawalValidatorIndex(t *testing.T) {
t.Run("ok for deneb", func(t *testing.T) {
s := BeaconState{version: version.Deneb, nextWithdrawalValidatorIndex: 123}
pb := &ethpb.BeaconStateDeneb{NextWithdrawalValidatorIndex: 123}
s, err := state_native.InitializeFromProtoDeneb(pb)
require.NoError(t, err)
i, err := s.NextWithdrawalValidatorIndex()
require.NoError(t, err)
assert.Equal(t, primitives.ValidatorIndex(123), i)
})
t.Run("ok", func(t *testing.T) {
s := BeaconState{version: version.Capella, nextWithdrawalValidatorIndex: 123}
pb := &ethpb.BeaconStateCapella{NextWithdrawalValidatorIndex: 123}
s, err := state_native.InitializeFromProtoCapella(pb)
require.NoError(t, err)
i, err := s.NextWithdrawalValidatorIndex()
require.NoError(t, err)
assert.Equal(t, primitives.ValidatorIndex(123), i)
})
t.Run("version before Capella not supported", func(t *testing.T) {
s := BeaconState{version: version.Bellatrix}
_, err := s.NextWithdrawalValidatorIndex()
s, err := state_native.InitializeFromProtoBellatrix(&ethpb.BeaconStateBellatrix{})
require.NoError(t, err)
_, err = s.NextWithdrawalValidatorIndex()
assert.ErrorContains(t, "NextWithdrawalValidatorIndex is not supported", err)
})
}
func TestHasETH1WithdrawalCredentials(t *testing.T) {
creds := []byte{0xFA, 0xCC}
v := &ethpb.Validator{WithdrawalCredentials: creds}
require.Equal(t, false, hasETH1WithdrawalCredential(v))
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
v = &ethpb.Validator{WithdrawalCredentials: creds}
require.Equal(t, true, hasETH1WithdrawalCredential(v))
// No Withdrawal cred
v = &ethpb.Validator{}
require.Equal(t, false, hasETH1WithdrawalCredential(v))
}
func TestIsFullyWithdrawableValidator(t *testing.T) {
// No ETH1 prefix
creds := []byte{0xFA, 0xCC}
v := &ethpb.Validator{
WithdrawalCredentials: creds,
WithdrawableEpoch: 2,
}
require.Equal(t, false, isFullyWithdrawableValidator(v, 3))
// Wrong withdrawable epoch
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
v = &ethpb.Validator{
WithdrawalCredentials: creds,
WithdrawableEpoch: 2,
}
require.Equal(t, false, isFullyWithdrawableValidator(v, 1))
// Fully withdrawable
creds = []byte{params.BeaconConfig().ETH1AddressWithdrawalPrefixByte, 0xCC}
v = &ethpb.Validator{
WithdrawalCredentials: creds,
WithdrawableEpoch: 2,
}
require.Equal(t, true, isFullyWithdrawableValidator(v, 3))
}
func TestExpectedWithdrawals(t *testing.T) {
t.Run("no withdrawals", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
nextWithdrawalValidatorIndex: 20,
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.validators[3].WithdrawableEpoch = primitives.Epoch(0)
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: s.balances[3],
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially and one fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
s.validators[7].WithdrawableEpoch = primitives.Epoch(0)
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 2, len(expected))
for _, stateVersion := range []int{version.Capella, version.Deneb, version.Electra} {
t.Run(version.String(stateVersion), func(t *testing.T) {
t.Run("no withdrawals", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
vals[i] = val
}
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one fully withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
require.NoError(t, s.SetNextWithdrawalValidatorIndex(20))
withdrawalFull := &enginev1.Withdrawal{
Index: 1,
ValidatorIndex: 7,
Address: s.validators[7].WithdrawalCredentials[12:],
Amount: s.balances[7],
}
withdrawalPartial := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawalPartial, expected[0])
require.DeepEqual(t, withdrawalFull, expected[1])
})
t.Run("all partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := 0; i < 100; i++ {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
vals[i] = val
}
vals[3].WithdrawableEpoch = primitives.Epoch(0)
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: vals[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
require.NoError(t, s.SetNextWithdrawalValidatorIndex(20))
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := 0; i < 100; i++ {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
vals[i] = val
}
balances[3] += params.BeaconConfig().MinDepositAmount
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: vals[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially and one fully withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
balances[3] += params.BeaconConfig().MinDepositAmount
vals[7].WithdrawableEpoch = primitives.Epoch(0)
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 2, len(expected))
withdrawalFull := &enginev1.Withdrawal{
Index: 1,
ValidatorIndex: 7,
Address: vals[7].WithdrawalCredentials[12:],
Amount: balances[7],
}
withdrawalPartial := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: vals[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawalPartial, expected[0])
require.DeepEqual(t, withdrawalFull, expected[1])
})
t.Run("all partially withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: vals[0].WithdrawalCredentials[12:],
Amount: 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: vals[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully and partially withdrawable", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: vals[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance + 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one fully withdrawable but zero balance", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
require.NoError(t, s.SetNextWithdrawalValidatorIndex(20))
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
vals[3].WithdrawableEpoch = primitives.Epoch(0)
balances[3] = 0
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one partially withdrawable, one above sweep bound", func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, stateVersion)
vals := make([]*ethpb.Validator, 100)
balances := make([]uint64, 100)
for i := range vals {
balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
vals[i] = val
}
balances[3] += params.BeaconConfig().MinDepositAmount
balances[10] += params.BeaconConfig().MinDepositAmount
require.NoError(t, s.SetValidators(vals))
require.NoError(t, s.SetBalances(balances))
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = 10
expected, _, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: vals[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
})
})
}
t.Run("electra all pending partial withdrawals", func(t *testing.T) {
// Load a serialized Electra state from disk.
// This spectest has a fully hydrated beacon state with partial pending withdrawals.
serializedBytes, err := util.BazelFileBytes("tests/mainnet/electra/operations/execution_layer_withdrawal_request/pyspec_tests/pending_withdrawals_consume_all_excess_balance/pre.ssz_snappy")
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
serializedSSZ, err := snappy.Decode(nil /* dst */, serializedBytes)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully and partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
pb := &ethpb.BeaconStateElectra{}
require.NoError(t, pb.UnmarshalSSZ(serializedSSZ))
s, err := state_native.InitializeFromProtoElectra(pb)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance + 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one fully withdrawable but zero balance", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
nextWithdrawalValidatorIndex: 20,
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.validators[3].WithdrawableEpoch = primitives.Epoch(0)
s.balances[3] = 0
expected, err := s.ExpectedWithdrawals()
t.Log(s.NumPendingPartialWithdrawals())
expected, partialWithdrawalsCount, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one partially withdrawable, one above sweep bound", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
s.balances[10] += params.BeaconConfig().MinDepositAmount
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = 10
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
})
}
func TestExpectedWithdrawals_Deneb(t *testing.T) {
t.Run("no withdrawals", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
nextWithdrawalValidatorIndex: 20,
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.validators[3].WithdrawableEpoch = primitives.Epoch(0)
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: s.balances[3],
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one partially and one fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
val.WithdrawalCredentials[31] = byte(i)
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
s.validators[7].WithdrawableEpoch = primitives.Epoch(0)
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 2, len(expected))
withdrawalFull := &enginev1.Withdrawal{
Index: 1,
ValidatorIndex: 7,
Address: s.validators[7].WithdrawalCredentials[12:],
Amount: s.balances[7],
}
withdrawalPartial := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawalPartial, expected[0])
require.DeepEqual(t, withdrawalFull, expected[1])
})
t.Run("all partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("all fully and partially withdrawable", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance + 1
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(0),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxWithdrawalsPerPayload, uint64(len(expected)))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 0,
Address: s.validators[0].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MaxEffectiveBalance + 1,
}
require.DeepEqual(t, withdrawal, expected[0])
})
t.Run("one fully withdrawable but zero balance", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
nextWithdrawalValidatorIndex: 20,
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.validators[3].WithdrawableEpoch = primitives.Epoch(0)
s.balances[3] = 0
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one partially withdrawable, one above sweep bound", func(t *testing.T) {
s := BeaconState{
version: version.Deneb,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: primitives.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
s.balances[10] += params.BeaconConfig().MinDepositAmount
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = 10
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
Index: 0,
ValidatorIndex: 3,
Address: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
require.Equal(t, 8, len(expected))
require.Equal(t, uint64(8), partialWithdrawalsCount)
})
}

View File

@@ -3,6 +3,7 @@ package state_native
import (
"context"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
@@ -38,6 +39,10 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateCapellaFieldCount)
case version.Deneb:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateDenebFieldCount)
case version.Electra:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateElectraFieldCount)
default:
return nil, fmt.Errorf("unknown state version %s", version.String(state.version))
}
// Genesis time root.
@@ -247,6 +252,15 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[types.LatestExecutionPayloadHeaderDeneb.RealPosition()] = executionPayloadRoot[:]
}
if state.version == version.Electra {
// Execution payload root.
executionPayloadRoot, err := state.latestExecutionPayloadHeaderElectra.HashTreeRoot()
if err != nil {
return nil, err
}
fieldRoots[types.LatestExecutionPayloadHeaderElectra.RealPosition()] = executionPayloadRoot[:]
}
if state.version >= version.Capella {
// Next withdrawal index root.
nextWithdrawalIndexRoot := make([]byte, 32)
@@ -266,5 +280,52 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[types.HistoricalSummaries.RealPosition()] = historicalSummaryRoot[:]
}
if state.version >= version.Electra {
// DepositReceiptsStartIndex root.
drsiRoot := ssz.Uint64Root(state.depositReceiptsStartIndex)
fieldRoots[types.DepositReceiptsStartIndex.RealPosition()] = drsiRoot[:]
// DepositBalanceToConsume root.
dbtcRoot := ssz.Uint64Root(uint64(state.depositBalanceToConsume))
fieldRoots[types.DepositBalanceToConsume.RealPosition()] = dbtcRoot[:]
// ExitBalanceToConsume root.
ebtcRoot := ssz.Uint64Root(uint64(state.exitBalanceToConsume))
fieldRoots[types.ExitBalanceToConsume.RealPosition()] = ebtcRoot[:]
// EarliestExitEpoch root.
eeeRoot := ssz.Uint64Root(uint64(state.earliestExitEpoch))
fieldRoots[types.EarliestExitEpoch.RealPosition()] = eeeRoot[:]
// ConsolidationBalanceToConsume root.
cbtcRoot := ssz.Uint64Root(uint64(state.consolidationBalanceToConsume))
fieldRoots[types.ConsolidationBalanceToConsume.RealPosition()] = cbtcRoot[:]
// EarliestConsolidationEpoch root.
eceRoot := ssz.Uint64Root(uint64(state.earliestConsolidationEpoch))
fieldRoots[types.EarliestConsolidationEpoch.RealPosition()] = eceRoot[:]
// PendingBalanceDeposits root.
pbdRoot, err := stateutil.PendingBalanceDepositsRoot(state.pendingBalanceDeposits)
if err != nil {
return nil, errors.Wrap(err, "could not compute pending balance deposits merkleization")
}
fieldRoots[types.PendingBalanceDeposits.RealPosition()] = pbdRoot[:]
// PendingPartialWithdrawals root.
ppwRoot, err := stateutil.PendingPartialWithdrawalsRoot(state.pendingPartialWithdrawals)
if err != nil {
return nil, errors.Wrap(err, "could not compute pending partial withdrawals merkleization")
}
fieldRoots[types.PendingPartialWithdrawals.RealPosition()] = ppwRoot[:]
// PendingConsolidations root.
pcRoot, err := stateutil.PendingConsolidationsRoot(state.pendingConsolidations)
if err != nil {
return nil, errors.Wrap(err, "could not compute pending consolidations merkleization")
}
fieldRoots[types.PendingConsolidations.RealPosition()] = pcRoot[:]
}
return fieldRoots, nil
}

View File

@@ -0,0 +1,67 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// AppendPendingBalanceDeposit is a mutating call to the beacon state to create and append a pending
// balance deposit object on to the state. This method requires access to the Lock on the state and
// only applies in electra or later.
func (b *BeaconState) AppendPendingBalanceDeposit(index primitives.ValidatorIndex, amount uint64) error {
if b.version < version.Electra {
return errNotSupported("AppendPendingBalanceDeposit", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingBalanceDeposits].MinusRef()
b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1)
b.pendingBalanceDeposits = append(b.pendingBalanceDeposits, &ethpb.PendingBalanceDeposit{Index: index, Amount: amount})
b.markFieldAsDirty(types.PendingBalanceDeposits)
b.rebuildTrie[types.PendingBalanceDeposits] = true
return nil
}
// SetPendingBalanceDeposits is a mutating call to the beacon state which replaces the pending
// balance deposit slice with the provided value. This method requires access to the Lock on the
// state and only applies in electra or later.
func (b *BeaconState) SetPendingBalanceDeposits(val []*ethpb.PendingBalanceDeposit) error {
if b.version < version.Electra {
return errNotSupported("SetPendingBalanceDeposits", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingBalanceDeposits].MinusRef()
b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1)
b.pendingBalanceDeposits = val
b.markFieldAsDirty(types.PendingBalanceDeposits)
b.rebuildTrie[types.PendingBalanceDeposits] = true
return nil
}
// SetDepositBalanceToConsume is a mutating call to the beacon state which sets the deposit balance
// to consume value to the given value. This method requires access to the Lock on the state and
// only applies in electra or later.
func (b *BeaconState) SetDepositBalanceToConsume(dbtc math.Gwei) error {
if b.version < version.Electra {
return errNotSupported("SetDepositBalanceToConsume", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.depositBalanceToConsume = dbtc
b.markFieldAsDirty(types.DepositBalanceToConsume)
b.rebuildTrie[types.DepositBalanceToConsume] = true
return nil
}

View File

@@ -0,0 +1,61 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestAppendPendingBalanceDeposit(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
pbd, err := s.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 0, len(pbd))
require.NoError(t, s.AppendPendingBalanceDeposit(1, 10))
pbd, err = s.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 1, len(pbd))
require.Equal(t, primitives.ValidatorIndex(1), pbd[0].Index)
require.Equal(t, uint64(10), pbd[0].Amount)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.AppendPendingBalanceDeposit(1, 1))
}
func TestSetPendingBalanceDeposits(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
pbd, err := s.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 0, len(pbd))
require.NoError(t, s.SetPendingBalanceDeposits([]*eth.PendingBalanceDeposit{{}, {}, {}}))
pbd, err = s.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 3, len(pbd))
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.SetPendingBalanceDeposits([]*eth.PendingBalanceDeposit{{}, {}, {}}))
}
func TestSetDepositBalanceToConsume(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
require.NoError(t, s.SetDepositBalanceToConsume(10))
dbtc, err := s.DepositBalanceToConsume()
require.NoError(t, err)
require.Equal(t, math.Gwei(10), dbtc)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.SetDepositBalanceToConsume(10))
}

View File

@@ -0,0 +1,80 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// ExitEpochAndUpdateChurn computes the exit epoch and updates the churn. This method mutates the state.
//
// Spec definition:
//
// def compute_exit_epoch_and_update_churn(state: BeaconState, exit_balance: Gwei) -> Epoch:
// earliest_exit_epoch = max(state.earliest_exit_epoch, compute_activation_exit_epoch(get_current_epoch(state)))
// per_epoch_churn = get_activation_exit_churn_limit(state)
// # New epoch for exits.
// if state.earliest_exit_epoch < earliest_exit_epoch:
// exit_balance_to_consume = per_epoch_churn
// else:
// exit_balance_to_consume = state.exit_balance_to_consume
//
// # Exit doesn't fit in the current earliest epoch.
// if exit_balance > exit_balance_to_consume:
// balance_to_process = exit_balance - exit_balance_to_consume
// additional_epochs = (balance_to_process - 1) // per_epoch_churn + 1
// earliest_exit_epoch += additional_epochs
// exit_balance_to_consume += additional_epochs * per_epoch_churn
//
// # Consume the balance and update state variables.
// state.exit_balance_to_consume = exit_balance_to_consume - exit_balance
// state.earliest_exit_epoch = earliest_exit_epoch
//
// return state.earliest_exit_epoch
func (b *BeaconState) ExitEpochAndUpdateChurn(exitBalance math.Gwei) (primitives.Epoch, error) {
if b.version < version.Electra {
return 0, errNotSupported("ExitEpochAndUpdateChurn", b.version)
}
// This helper requires access to the RLock and cannot be called from within the write Lock.
activeBal, err := helpers.TotalActiveBalance(b)
if err != nil {
return 0, err
}
b.lock.Lock()
defer b.lock.Unlock()
earliestExitEpoch := max(b.earliestExitEpoch, helpers.ActivationExitEpoch(slots.ToEpoch(b.slot)))
perEpochChurn := helpers.ActivationExitChurnLimit(math.Gwei(activeBal)) // Guaranteed to be non-zero.
// New epoch for exits
var exitBalanceToConsume math.Gwei
if b.earliestExitEpoch < earliestExitEpoch {
exitBalanceToConsume = perEpochChurn
} else {
exitBalanceToConsume = b.exitBalanceToConsume
}
// Exit doesn't fit in the current earliest epoch.
if exitBalance > exitBalanceToConsume {
balanceToProcess := exitBalance - exitBalanceToConsume
additionalEpochs := primitives.Epoch((balanceToProcess-1)/perEpochChurn + 1)
earliestExitEpoch += additionalEpochs
exitBalanceToConsume += math.Gwei(additionalEpochs) * perEpochChurn
}
// Consume the balance and update state variables.
b.exitBalanceToConsume = exitBalanceToConsume - exitBalance
b.earliestExitEpoch = earliestExitEpoch
b.markFieldAsDirty(types.ExitBalanceToConsume)
b.rebuildTrie[types.ExitBalanceToConsume] = true
b.markFieldAsDirty(types.EarliestExitEpoch)
b.rebuildTrie[types.EarliestExitEpoch] = true
return b.earliestExitEpoch, nil
}

View File

@@ -0,0 +1,200 @@
package state_native_test
import (
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
func TestExitEpochAndUpdateChurn_SpectestCase(t *testing.T) {
// Load a serialized Electra state from disk.
// The spec tests shows that the exit epoch is 262 for validator 0 performing a voluntary exit.
serializedBytes, err := util.BazelFileBytes("tests/mainnet/electra/operations/voluntary_exit/pyspec_tests/exit_existing_churn_and_churn_limit_balance/pre.ssz_snappy")
require.NoError(t, err)
serializedSSZ, err := snappy.Decode(nil /* dst */, serializedBytes)
require.NoError(t, err)
pb := &eth.BeaconStateElectra{}
require.NoError(t, pb.UnmarshalSSZ(serializedSSZ))
s, err := state_native.InitializeFromProtoElectra(pb)
require.NoError(t, err)
val, err := s.ValidatorAtIndex(0)
require.NoError(t, err)
ee, err := s.ExitEpochAndUpdateChurn(math.Gwei(val.EffectiveBalance))
require.NoError(t, err)
require.Equal(t, primitives.Epoch(262), ee)
p := s.ToProto()
pb, ok := p.(*eth.BeaconStateElectra)
if !ok {
t.Fatal("wrong proto")
}
require.Equal(t, math.Gwei(127000000000), pb.ExitBalanceToConsume)
require.Equal(t, primitives.Epoch(262), pb.EarliestExitEpoch)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
_, err = s.ExitEpochAndUpdateChurn(10)
require.ErrorContains(t, "not supported", err)
}
func TestExitEpochAndUpdateChurn(t *testing.T) {
slot := primitives.Slot(10_000_000)
epoch := slots.ToEpoch(slot)
t.Run("state earliest exit epoch is old", func(t *testing.T) {
st, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
Slot: slot,
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra,
},
},
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalanceElectra},
EarliestExitEpoch: epoch - params.BeaconConfig().MaxSeedLookahead*2, // Old, relative to slot.
ExitBalanceToConsume: math.Gwei(20_000_000),
})
require.NoError(t, err)
activeBal, err := helpers.TotalActiveBalance(st)
require.NoError(t, err)
exitBal := math.Gwei(10_000_000)
wantExitBalToConsume := helpers.ActivationExitChurnLimit(math.Gwei(activeBal)) - exitBal
ee, err := st.ExitEpochAndUpdateChurn(exitBal)
require.NoError(t, err)
wantExitEpoch := helpers.ActivationExitEpoch(epoch)
require.Equal(t, wantExitEpoch, ee)
p := st.ToProto()
pb, ok := p.(*eth.BeaconStateElectra)
if !ok {
t.Fatal("wrong proto")
}
require.Equal(t, wantExitBalToConsume, pb.ExitBalanceToConsume)
require.Equal(t, wantExitEpoch, pb.EarliestExitEpoch)
})
t.Run("state exit bal to consume is less than activation exit churn limit", func(t *testing.T) {
st, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
Slot: slot,
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra,
},
},
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalanceElectra},
EarliestExitEpoch: epoch,
ExitBalanceToConsume: math.Gwei(20_000_000),
})
require.NoError(t, err)
activeBal, err := helpers.TotalActiveBalance(st)
require.NoError(t, err)
activationExitChurnLimit := helpers.ActivationExitChurnLimit(math.Gwei(activeBal))
exitBal := activationExitChurnLimit * 2
wantExitBalToConsume := math.Gwei(0)
ee, err := st.ExitEpochAndUpdateChurn(exitBal)
require.NoError(t, err)
wantExitEpoch := helpers.ActivationExitEpoch(epoch) + 1
require.Equal(t, wantExitEpoch, ee)
p := st.ToProto()
pb, ok := p.(*eth.BeaconStateElectra)
if !ok {
t.Fatal("wrong proto")
}
require.Equal(t, wantExitBalToConsume, pb.ExitBalanceToConsume)
require.Equal(t, wantExitEpoch, pb.EarliestExitEpoch)
})
t.Run("state earliest exit epoch is in the future and exit balance is less than state", func(t *testing.T) {
st, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
Slot: slot,
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra,
},
},
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalanceElectra},
EarliestExitEpoch: epoch + 10_000,
ExitBalanceToConsume: math.Gwei(20_000_000),
})
require.NoError(t, err)
exitBal := math.Gwei(10_000_000)
wantExitBalToConsume := math.Gwei(20_000_000) - exitBal
ee, err := st.ExitEpochAndUpdateChurn(exitBal)
require.NoError(t, err)
wantExitEpoch := epoch + 10_000
require.Equal(t, wantExitEpoch, ee)
p := st.ToProto()
pb, ok := p.(*eth.BeaconStateElectra)
if !ok {
t.Fatal("wrong proto")
}
require.Equal(t, wantExitBalToConsume, pb.ExitBalanceToConsume)
require.Equal(t, wantExitEpoch, pb.EarliestExitEpoch)
})
t.Run("state earliest exit epoch is in the future and exit balance exceeds state", func(t *testing.T) {
st, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{
Slot: slot,
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalanceElectra,
},
},
Balances: []uint64{params.BeaconConfig().MaxEffectiveBalanceElectra},
EarliestExitEpoch: epoch + 10_000,
ExitBalanceToConsume: math.Gwei(20_000_000),
})
require.NoError(t, err)
exitBal := math.Gwei(40_000_000)
activeBal, err := helpers.TotalActiveBalance(st)
require.NoError(t, err)
activationExitChurnLimit := helpers.ActivationExitChurnLimit(math.Gwei(activeBal))
wantExitBalToConsume := activationExitChurnLimit - 20_000_000
ee, err := st.ExitEpochAndUpdateChurn(exitBal)
require.NoError(t, err)
wantExitEpoch := epoch + 10_000 + 1
require.Equal(t, wantExitEpoch, ee)
p := st.ToProto()
pb, ok := p.(*eth.BeaconStateElectra)
if !ok {
t.Fatal("wrong proto")
}
require.Equal(t, wantExitBalToConsume, pb.ExitBalanceToConsume)
require.Equal(t, wantExitEpoch, pb.EarliestExitEpoch)
})
t.Run("earlier than electra returns error", func(t *testing.T) {
st, err := state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
_, err = st.ExitEpochAndUpdateChurn(0)
require.ErrorContains(t, "is not supported", err)
})
}

View File

@@ -0,0 +1,84 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// AppendPendingConsolidation is a mutating call to the beacon state which appends the provided
// pending consolidation to the end of the slice on the state. This method requires access to the
// Lock on the state and only applies in electra or later.
func (b *BeaconState) AppendPendingConsolidation(val *ethpb.PendingConsolidation) error {
if b.version < version.Electra {
return errNotSupported("AppendPendingConsolidation", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingConsolidations].MinusRef()
b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1)
b.pendingConsolidations = append(b.pendingConsolidations, val)
b.markFieldAsDirty(types.PendingConsolidations)
b.rebuildTrie[types.PendingConsolidations] = true
return nil
}
// SetPendingConsolidations is a mutating call to the beacon state which replaces the slice on the
// state with the given value. This method requires access to the Lock on the state and only applies
// in electra or later.
func (b *BeaconState) SetPendingConsolidations(val []*ethpb.PendingConsolidation) error {
if b.version < version.Electra {
return errNotSupported("SetPendingConsolidations", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingConsolidations].MinusRef()
b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1)
b.pendingConsolidations = val
b.markFieldAsDirty(types.PendingConsolidations)
b.rebuildTrie[types.PendingConsolidations] = true
return nil
}
// SetEarliestConsolidationEpoch is a mutating call to the beacon state which sets the earlest
// consolidation epoch value. This method requires access to the Lock on the state and only applies
// in electra or later.
func (b *BeaconState) SetEarliestConsolidationEpoch(epoch primitives.Epoch) error {
if b.version < version.Electra {
return errNotSupported("SetEarliestConsolidationEpoch", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.earliestConsolidationEpoch = epoch
b.markFieldAsDirty(types.EarliestConsolidationEpoch)
b.rebuildTrie[types.EarliestConsolidationEpoch] = true
return nil
}
// SetConsolidationBalanceToConsume is a mutating call to the beacon state which sets the value of
// the consolidation balance to consume to the provided value. This method requires access to the
// Lock on the state and only applies in electra or later.
func (b *BeaconState) SetConsolidationBalanceToConsume(balance math.Gwei) error {
if b.version < version.Electra {
return errNotSupported("SetConsolidationBalanceToConsume", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.consolidationBalanceToConsume = balance
b.markFieldAsDirty(types.ConsolidationBalanceToConsume)
b.rebuildTrie[types.ConsolidationBalanceToConsume] = true
return nil
}

View File

@@ -0,0 +1,76 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestAppendPendingConsolidation(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
num, err := s.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, uint64(0), num)
require.NoError(t, s.AppendPendingConsolidation(&eth.PendingConsolidation{}))
num, err = s.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, uint64(1), num)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.AppendPendingConsolidation(&eth.PendingConsolidation{}))
}
func TestSetPendingConsolidations(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
num, err := s.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, uint64(0), num)
require.NoError(t, s.SetPendingConsolidations([]*eth.PendingConsolidation{{}, {}, {}}))
num, err = s.NumPendingConsolidations()
require.NoError(t, err)
require.Equal(t, uint64(3), num)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.SetPendingConsolidations([]*eth.PendingConsolidation{{}, {}, {}}))
}
func TestSetEarliestConsolidationEpoch(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
ece, err := s.EarliestConsolidationEpoch()
require.NoError(t, err)
require.Equal(t, primitives.Epoch(0), ece)
require.NoError(t, s.SetEarliestConsolidationEpoch(10))
ece, err = s.EarliestConsolidationEpoch()
require.NoError(t, err)
require.Equal(t, primitives.Epoch(10), ece)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.SetEarliestConsolidationEpoch(10))
}
func TestSetConsolidationBalanceToConsume(t *testing.T) {
s, err := state_native.InitializeFromProtoElectra(&eth.BeaconStateElectra{})
require.NoError(t, err)
require.NoError(t, s.SetConsolidationBalanceToConsume(10))
cbtc, err := s.ConsolidationBalanceToConsume()
require.NoError(t, err)
require.Equal(t, math.Gwei(10), cbtc)
// Fails for versions older than electra
s, err = state_native.InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "not supported", s.SetConsolidationBalanceToConsume(10))
}

View File

@@ -0,0 +1,21 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// SetDepositReceiptsStartIndex for the beacon state. Updates the DepositReceiptsStartIndex
func (b *BeaconState) SetDepositReceiptsStartIndex(index uint64) error {
if b.version < version.Electra {
return errNotSupported("SetDepositReceiptsStartIndex", b.version)
}
b.lock.Lock()
defer b.lock.Unlock()
b.depositReceiptsStartIndex = index
b.markFieldAsDirty(types.DepositReceiptsStartIndex)
b.rebuildTrie[types.DepositReceiptsStartIndex] = true
return nil
}

View File

@@ -0,0 +1,27 @@
package state_native_test
import (
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestSetDepositReceiptsStartIndex(t *testing.T) {
t.Run("previous fork returns expected error", func(t *testing.T) {
dState, _ := util.DeterministicGenesisState(t, 1)
require.ErrorContains(t, "is not supported", dState.SetDepositReceiptsStartIndex(1))
})
t.Run("electra sets expected value", func(t *testing.T) {
old := uint64(2)
dState, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{DepositReceiptsStartIndex: old})
require.NoError(t, err)
want := uint64(3)
require.NoError(t, dState.SetDepositReceiptsStartIndex(want))
got, err := dState.DepositReceiptsStartIndex()
require.NoError(t, err)
require.Equal(t, want, got)
})
}

View File

@@ -1,6 +1,8 @@
package state_native
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
@@ -21,6 +23,9 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
switch header := val.Proto().(type) {
case *enginev1.ExecutionPayload:
if b.version != version.Bellatrix {
return fmt.Errorf("wrong state version (%s) for bellatrix execution payload", version.String(b.version))
}
latest, err := consensusblocks.PayloadToHeader(val)
if err != nil {
return errors.Wrap(err, "could not convert payload to header")
@@ -29,6 +34,9 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
b.markFieldAsDirty(types.LatestExecutionPayloadHeader)
return nil
case *enginev1.ExecutionPayloadCapella:
if b.version != version.Capella {
return fmt.Errorf("wrong state version (%s) for capella execution payload", version.String(b.version))
}
latest, err := consensusblocks.PayloadToHeaderCapella(val)
if err != nil {
return errors.Wrap(err, "could not convert payload to header")
@@ -37,6 +45,9 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderCapella)
return nil
case *enginev1.ExecutionPayloadDeneb:
if b.version != version.Deneb {
return fmt.Errorf("wrong state version (%s) for deneb execution payload", version.String(b.version))
}
latest, err := consensusblocks.PayloadToHeaderDeneb(val)
if err != nil {
return errors.Wrap(err, "could not convert payload to header")
@@ -44,18 +55,49 @@ func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionDa
b.latestExecutionPayloadHeaderDeneb = latest
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderDeneb)
return nil
case *enginev1.ExecutionPayloadElectra:
if b.version != version.Electra {
return fmt.Errorf("wrong state version (%s) for electra execution payload", version.String(b.version))
}
eVal, ok := val.(interfaces.ExecutionDataElectra)
if !ok {
return fmt.Errorf("could not cast %T to ExecutionDataElectra: %w", val, interfaces.ErrInvalidCast)
}
latest, err := consensusblocks.PayloadToHeaderElectra(eVal)
if err != nil {
return errors.Wrap(err, "could not convert payload to header")
}
b.latestExecutionPayloadHeaderElectra = latest
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderElectra)
return nil
case *enginev1.ExecutionPayloadHeader:
if b.version != version.Bellatrix {
return fmt.Errorf("wrong state version (%s) for bellatrix execution payload header", version.String(b.version))
}
b.latestExecutionPayloadHeader = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeader)
return nil
case *enginev1.ExecutionPayloadHeaderCapella:
if b.version != version.Capella {
return fmt.Errorf("wrong state version (%s) for capella execution payload header", version.String(b.version))
}
b.latestExecutionPayloadHeaderCapella = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderCapella)
return nil
case *enginev1.ExecutionPayloadHeaderDeneb:
if b.version != version.Deneb {
return fmt.Errorf("wrong state version (%s) for deneb execution payload header", version.String(b.version))
}
b.latestExecutionPayloadHeaderDeneb = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderDeneb)
return nil
case *enginev1.ExecutionPayloadHeaderElectra:
if b.version != version.Electra {
return fmt.Errorf("wrong state version (%s) for electra execution payload header", version.String(b.version))
}
b.latestExecutionPayloadHeaderElectra = header
b.markFieldAsDirty(types.LatestExecutionPayloadHeaderElectra)
return nil
default:
return errors.New("value must be an execution payload header")
}

View File

@@ -0,0 +1,107 @@
package state_native_test
import (
"fmt"
"testing"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestSetLatestExecutionPayloadHeader(t *testing.T) {
versionOffset := version.Bellatrix // PayloadHeader only applies in Bellatrix and beyond.
payloads := []interfaces.ExecutionData{
func() interfaces.ExecutionData {
e := util.NewBeaconBlockBellatrix().Block.Body.ExecutionPayload
ee, err := blocks.WrappedExecutionPayload(e)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBeaconBlockCapella().Block.Body.ExecutionPayload
ee, err := blocks.WrappedExecutionPayloadCapella(e, nil)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBeaconBlockDeneb().Block.Body.ExecutionPayload
ee, err := blocks.WrappedExecutionPayloadDeneb(e, nil)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBeaconBlockElectra().Block.Body.ExecutionPayload
ee, err := blocks.WrappedExecutionPayloadElectra(e, nil)
require.NoError(t, err)
return ee
}(),
}
payloadHeaders := []interfaces.ExecutionData{
func() interfaces.ExecutionData {
e := util.NewBlindedBeaconBlockBellatrix().Block.Body.ExecutionPayloadHeader
ee, err := blocks.WrappedExecutionPayloadHeader(e)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBlindedBeaconBlockCapella().Block.Body.ExecutionPayloadHeader
ee, err := blocks.WrappedExecutionPayloadHeaderCapella(e, nil)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBlindedBeaconBlockDeneb().Message.Body.ExecutionPayloadHeader
ee, err := blocks.WrappedExecutionPayloadHeaderDeneb(e, nil)
require.NoError(t, err)
return ee
}(),
func() interfaces.ExecutionData {
e := util.NewBlindedBeaconBlockElectra().Message.Body.ExecutionPayloadHeader
ee, err := blocks.WrappedExecutionPayloadHeaderElectra(e, nil)
require.NoError(t, err)
return ee
}(),
}
t.Run("can set payload", func(t *testing.T) {
for i, p := range payloads {
t.Run(version.String(i+versionOffset), func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, i+versionOffset)
require.NoError(t, s.SetLatestExecutionPayloadHeader(p))
})
}
})
t.Run("can set payload header", func(t *testing.T) {
for i, ph := range payloadHeaders {
t.Run(version.String(i+versionOffset), func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, i+versionOffset)
require.NoError(t, s.SetLatestExecutionPayloadHeader(ph))
})
}
})
t.Run("mismatched type version returns error", func(t *testing.T) {
require.Equal(t, len(payloads), len(payloadHeaders), "This test will fail if the payloads and payload headers are not same length")
for i := 0; i < len(payloads); i++ {
for j := 0; j < len(payloads); j++ {
if i == j {
continue
}
t.Run(fmt.Sprintf("%s state with %s payload", version.String(i+versionOffset), version.String(j+versionOffset)), func(t *testing.T) {
s := state_native.EmptyStateFromVersion(t, i+versionOffset)
p := payloads[j]
require.ErrorContains(t, "wrong state version", s.SetLatestExecutionPayloadHeader(p))
ph := payloadHeaders[j]
require.ErrorContains(t, "wrong state version", s.SetLatestExecutionPayloadHeader(ph))
})
}
}
})
}

View File

@@ -1,8 +1,12 @@
package state_native
import (
"errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
@@ -34,3 +38,56 @@ func (b *BeaconState) SetNextWithdrawalValidatorIndex(i primitives.ValidatorInde
b.markFieldAsDirty(types.NextWithdrawalValidatorIndex)
return nil
}
// AppendPendingPartialWithdrawal is a mutating call to the beacon state which appends the given
// value to the end of the pending partial withdrawals slice in the state. This method requires
// access to the Lock on the state and only applies in electra or later.
func (b *BeaconState) AppendPendingPartialWithdrawal(ppw *eth.PendingPartialWithdrawal) error {
if b.version < version.Electra {
return errNotSupported("AppendPendingPartialWithdrawal", b.version)
}
if ppw == nil {
return errors.New("cannot append nil pending partial withdrawal")
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingPartialWithdrawals].MinusRef()
b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1)
b.pendingPartialWithdrawals = append(b.pendingPartialWithdrawals, ppw)
b.markFieldAsDirty(types.PendingPartialWithdrawals)
b.rebuildTrie[types.PendingPartialWithdrawals] = true
return nil
}
// DequeuePartialWithdrawals removes the partial withdrawals from the beginning of the partial withdrawals list.
func (b *BeaconState) DequeuePartialWithdrawals(n uint64) error {
if b.version < version.Electra {
return errNotSupported("DequeuePartialWithdrawals", b.version)
}
if n > uint64(len(b.pendingPartialWithdrawals)) {
return errors.New("cannot dequeue more withdrawals than are in the queue")
}
if n == 0 {
return nil // Don't wait on a lock for no reason.
}
b.lock.Lock()
defer b.lock.Unlock()
b.sharedFieldReferences[types.PendingPartialWithdrawals].MinusRef()
b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1)
b.pendingPartialWithdrawals = b.pendingPartialWithdrawals[n:]
b.markFieldAsDirty(types.PendingPartialWithdrawals)
b.rebuildTrie[types.PendingPartialWithdrawals] = true
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
@@ -52,3 +53,68 @@ func TestSetNextWithdrawalValidatorIndex_Deneb(t *testing.T) {
require.Equal(t, primitives.ValidatorIndex(5), s.nextWithdrawalValidatorIndex)
require.Equal(t, true, s.dirtyFields[types.NextWithdrawalValidatorIndex])
}
func TestDequeuePendingWithdrawals(t *testing.T) {
s, err := InitializeFromProtoElectra(&eth.BeaconStateElectra{
PendingPartialWithdrawals: []*eth.PendingPartialWithdrawal{
{},
{},
{},
},
})
require.NoError(t, err)
// 2 of 3 should be OK
num, err := s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(3), num)
require.NoError(t, s.DequeuePartialWithdrawals(2))
num, err = s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(1), num)
// 2 of 1 exceeds the limit and an error should be returned
num, err = s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(1), num)
require.ErrorContains(t, "cannot dequeue more withdrawals than are in the queue", s.DequeuePartialWithdrawals(2))
// Removing all pending partial withdrawals should be OK.
num, err = s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(1), num)
require.NoError(t, s.DequeuePartialWithdrawals(1))
num, err = s.Copy().NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(0), num)
s, err = InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "is not supported", s.DequeuePartialWithdrawals(0))
}
func TestAppendPendingWithdrawals(t *testing.T) {
s, err := InitializeFromProtoElectra(&eth.BeaconStateElectra{
PendingPartialWithdrawals: []*eth.PendingPartialWithdrawal{
{},
{},
{},
},
})
require.NoError(t, err)
num, err := s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(3), num)
require.NoError(t, s.AppendPendingPartialWithdrawal(&eth.PendingPartialWithdrawal{}))
num, err = s.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(4), num)
require.ErrorContains(t, "cannot append nil pending partial withdrawal", s.AppendPendingPartialWithdrawal(nil))
s, err = InitializeFromProtoDeneb(&eth.BeaconStateDeneb{})
require.NoError(t, err)
require.ErrorContains(t, "is not supported", s.AppendPendingPartialWithdrawal(nil))
}

View File

@@ -7,7 +7,7 @@ import (
func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
switch b.version {
case version.Bellatrix, version.Capella, version.Deneb:
case version.Bellatrix, version.Capella, version.Deneb, version.Electra:
return params.BeaconConfig().ProportionalSlashingMultiplierBellatrix, nil
case version.Altair:
return params.BeaconConfig().ProportionalSlashingMultiplierAltair, nil
@@ -19,7 +19,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
switch b.version {
case version.Bellatrix, version.Capella, version.Deneb:
case version.Bellatrix, version.Capella, version.Deneb, version.Electra:
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix, nil
case version.Altair:
return params.BeaconConfig().InactivityPenaltyQuotientAltair, nil

View File

@@ -502,3 +502,13 @@ func generateState(t *testing.T) state.BeaconState {
assert.NoError(t, err)
return newState
}
func EmptyStateFromVersion(t *testing.T, v int) state.BeaconState {
gen := generateState(t)
s, ok := gen.(*BeaconState)
if !ok {
t.Fatal("not a beacon state")
}
s.version = v
return s
}

View File

@@ -2,6 +2,7 @@ package state_native
import (
"context"
"fmt"
"runtime"
"sort"
@@ -93,17 +94,36 @@ var denebFields = append(
types.HistoricalSummaries,
)
var electraFields = append(
altairFields,
types.NextWithdrawalIndex,
types.NextWithdrawalValidatorIndex,
types.HistoricalSummaries,
types.LatestExecutionPayloadHeaderElectra,
types.DepositReceiptsStartIndex,
types.DepositBalanceToConsume,
types.ExitBalanceToConsume,
types.EarliestExitEpoch,
types.ConsolidationBalanceToConsume,
types.EarliestConsolidationEpoch,
types.PendingBalanceDeposits,
types.PendingPartialWithdrawals,
types.PendingConsolidations,
)
const (
phase0SharedFieldRefCount = 10
altairSharedFieldRefCount = 11
bellatrixSharedFieldRefCount = 12
capellaSharedFieldRefCount = 14
denebSharedFieldRefCount = 14
electraSharedFieldRefCount = 17
experimentalStatePhase0SharedFieldRefCount = 5
experimentalStateAltairSharedFieldRefCount = 5
experimentalStateBellatrixSharedFieldRefCount = 6
experimentalStateCapellaSharedFieldRefCount = 8
experimentalStateDenebSharedFieldRefCount = 8
experimentalStateElectraSharedFieldRefCount = 11
)
// InitializeFromProtoPhase0 the beacon state from a protobuf representation.
@@ -131,6 +151,10 @@ func InitializeFromProtoDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconState, er
return InitializeFromProtoUnsafeDeneb(proto.Clone(st).(*ethpb.BeaconStateDeneb))
}
func InitializeFromProtoElectra(st *ethpb.BeaconStateElectra) (state.BeaconState, error) {
return InitializeFromProtoUnsafeElectra(proto.Clone(st).(*ethpb.BeaconStateElectra))
}
// 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) {
@@ -683,6 +707,131 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta
return b, nil
}
// InitializeFromProtoUnsafeElectra directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafeElectra(st *ethpb.BeaconStateElectra) (state.BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
for i, r := range st.HistoricalRoots {
hRoots[i] = bytesutil.ToBytes32(r)
}
fieldCount := params.BeaconConfig().BeaconStateElectraFieldCount
b := &BeaconState{
version: version.Electra,
genesisTime: st.GenesisTime,
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
slot: st.Slot,
fork: st.Fork,
latestBlockHeader: st.LatestBlockHeader,
historicalRoots: hRoots,
eth1Data: st.Eth1Data,
eth1DataVotes: st.Eth1DataVotes,
eth1DepositIndex: st.Eth1DepositIndex,
slashings: st.Slashings,
previousEpochParticipation: st.PreviousEpochParticipation,
currentEpochParticipation: st.CurrentEpochParticipation,
justificationBits: st.JustificationBits,
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
finalizedCheckpoint: st.FinalizedCheckpoint,
currentSyncCommittee: st.CurrentSyncCommittee,
nextSyncCommittee: st.NextSyncCommittee,
latestExecutionPayloadHeaderElectra: st.LatestExecutionPayloadHeader,
nextWithdrawalIndex: st.NextWithdrawalIndex,
nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex,
historicalSummaries: st.HistoricalSummaries,
depositReceiptsStartIndex: st.DepositReceiptsStartIndex,
depositBalanceToConsume: st.DepositBalanceToConsume,
exitBalanceToConsume: st.ExitBalanceToConsume,
earliestExitEpoch: st.EarliestExitEpoch,
consolidationBalanceToConsume: st.ConsolidationBalanceToConsume,
earliestConsolidationEpoch: st.EarliestConsolidationEpoch,
pendingBalanceDeposits: st.PendingBalanceDeposits,
pendingPartialWithdrawals: st.PendingPartialWithdrawals,
pendingConsolidations: st.PendingConsolidations,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
if features.Get().EnableExperimentalState {
b.blockRootsMultiValue = NewMultiValueBlockRoots(st.BlockRoots)
b.stateRootsMultiValue = NewMultiValueStateRoots(st.StateRoots)
b.randaoMixesMultiValue = NewMultiValueRandaoMixes(st.RandaoMixes)
b.balancesMultiValue = NewMultiValueBalances(st.Balances)
b.validatorsMultiValue = NewMultiValueValidators(st.Validators)
b.inactivityScoresMultiValue = NewMultiValueInactivityScores(st.InactivityScores)
b.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateElectraSharedFieldRefCount)
} else {
bRoots := make([][32]byte, fieldparams.BlockRootsLength)
for i, r := range st.BlockRoots {
bRoots[i] = bytesutil.ToBytes32(r)
}
b.blockRoots = bRoots
sRoots := make([][32]byte, fieldparams.StateRootsLength)
for i, r := range st.StateRoots {
sRoots[i] = bytesutil.ToBytes32(r)
}
b.stateRoots = sRoots
mixes := make([][32]byte, fieldparams.RandaoMixesLength)
for i, m := range st.RandaoMixes {
mixes[i] = bytesutil.ToBytes32(m)
}
b.randaoMixes = mixes
b.balances = st.Balances
b.validators = st.Validators
b.inactivityScores = st.InactivityScores
b.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, electraSharedFieldRefCount)
}
for _, f := range electraFields {
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.HistoricalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Eth1DataVotes] = 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.LatestExecutionPayloadHeaderElectra] = stateutil.NewRef(1) // New in Electra.
b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella.
b.sharedFieldReferences[types.PendingBalanceDeposits] = stateutil.NewRef(1) // New in Electra.
b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1) // New in Electra.
b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1) // New in Electra.
if !features.Get().EnableExperimentalState {
b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1)
b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1)
}
state.Count.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()
@@ -700,17 +849,25 @@ func (b *BeaconState) Copy() state.BeaconState {
fieldCount = params.BeaconConfig().BeaconStateCapellaFieldCount
case version.Deneb:
fieldCount = params.BeaconConfig().BeaconStateDenebFieldCount
case version.Electra:
fieldCount = params.BeaconConfig().BeaconStateElectraFieldCount
}
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,
genesisTime: b.genesisTime,
slot: b.slot,
eth1DepositIndex: b.eth1DepositIndex,
nextWithdrawalIndex: b.nextWithdrawalIndex,
nextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
depositReceiptsStartIndex: b.depositReceiptsStartIndex,
depositBalanceToConsume: b.depositBalanceToConsume,
exitBalanceToConsume: b.exitBalanceToConsume,
earliestExitEpoch: b.earliestExitEpoch,
consolidationBalanceToConsume: b.consolidationBalanceToConsume,
earliestConsolidationEpoch: b.earliestConsolidationEpoch,
// Large arrays, infrequently changed, constant size.
blockRoots: b.blockRoots,
@@ -735,6 +892,9 @@ func (b *BeaconState) Copy() state.BeaconState {
currentEpochParticipation: b.currentEpochParticipation,
inactivityScores: b.inactivityScores,
inactivityScoresMultiValue: b.inactivityScoresMultiValue,
pendingBalanceDeposits: b.pendingBalanceDeposits,
pendingPartialWithdrawals: b.pendingPartialWithdrawals,
pendingConsolidations: b.pendingConsolidations,
// Everything else, too small to be concerned about, constant size.
genesisValidatorsRoot: b.genesisValidatorsRoot,
@@ -750,6 +910,7 @@ func (b *BeaconState) Copy() state.BeaconState {
latestExecutionPayloadHeader: b.latestExecutionPayloadHeaderVal(),
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapellaVal(),
latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDenebVal(),
latestExecutionPayloadHeaderElectra: b.latestExecutionPayloadHeaderElectraVal(),
id: types.Enumerator.Inc(),
@@ -786,6 +947,8 @@ func (b *BeaconState) Copy() state.BeaconState {
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateCapellaSharedFieldRefCount)
case version.Deneb:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateDenebSharedFieldRefCount)
case version.Electra:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateElectraSharedFieldRefCount)
}
} else {
switch b.version {
@@ -799,6 +962,8 @@ func (b *BeaconState) Copy() state.BeaconState {
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount)
case version.Deneb:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, denebSharedFieldRefCount)
case version.Electra:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, electraSharedFieldRefCount)
}
}
@@ -891,6 +1056,10 @@ func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateCapellaFieldCount)
case version.Deneb:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateDenebFieldCount)
case version.Electra:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateElectraFieldCount)
default:
return fmt.Errorf("unknown state version (%s) when computing dirty fields in merklization", version.String(b.version))
}
return nil
@@ -1109,12 +1278,32 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
return b.latestExecutionPayloadHeaderCapella.HashTreeRoot()
case types.LatestExecutionPayloadHeaderDeneb:
return b.latestExecutionPayloadHeaderDeneb.HashTreeRoot()
case types.LatestExecutionPayloadHeaderElectra:
return b.latestExecutionPayloadHeaderElectra.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)
case types.DepositReceiptsStartIndex:
return ssz.Uint64Root(b.depositReceiptsStartIndex), nil
case types.DepositBalanceToConsume:
return ssz.Uint64Root(uint64(b.depositBalanceToConsume)), nil
case types.ExitBalanceToConsume:
return ssz.Uint64Root(uint64(b.exitBalanceToConsume)), nil
case types.EarliestExitEpoch:
return ssz.Uint64Root(uint64(b.earliestExitEpoch)), nil
case types.ConsolidationBalanceToConsume:
return ssz.Uint64Root(uint64(b.consolidationBalanceToConsume)), nil
case types.EarliestConsolidationEpoch:
return ssz.Uint64Root(uint64(b.earliestConsolidationEpoch)), nil
case types.PendingBalanceDeposits:
return stateutil.PendingBalanceDepositsRoot(b.pendingBalanceDeposits)
case types.PendingPartialWithdrawals:
return stateutil.PendingPartialWithdrawalsRoot(b.pendingPartialWithdrawals)
case types.PendingConsolidations:
return stateutil.PendingConsolidationsRoot(b.pendingConsolidations)
}
return [32]byte{}, errors.New("invalid field index provided")
}

View File

@@ -5,6 +5,8 @@ import (
"context"
"testing"
"github.com/golang/snappy"
"github.com/google/go-cmp/cmp"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
statenative "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/config/features"
@@ -14,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"google.golang.org/protobuf/testing/protocmp"
)
func TestInitializeFromProto_Phase0(t *testing.T) {
@@ -203,6 +206,42 @@ func TestInitializeFromProto_Deneb(t *testing.T) {
}
}
func TestInitializeFromProto_Electra(t *testing.T) {
type test struct {
name string
state *ethpb.BeaconStateElectra
error string
}
initTests := []test{
{
name: "nil state",
state: nil,
error: "received nil state",
},
{
name: "nil validators",
state: &ethpb.BeaconStateElectra{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconStateElectra{},
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := statenative.InitializeFromProtoElectra(tt.state)
if tt.error != "" {
require.ErrorContains(t, tt.error, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestInitializeFromProtoUnsafe_Phase0(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
pbState, err := statenative.ProtobufBeaconStatePhase0(testState.ToProtoUnsafe())
@@ -365,6 +404,37 @@ func TestInitializeFromProtoUnsafe_Deneb(t *testing.T) {
}
}
func TestInitializeFromProtoUnsafe_Electra(t *testing.T) {
type test struct {
name string
state *ethpb.BeaconStateElectra
error string
}
initTests := []test{
{
name: "nil validators",
state: &ethpb.BeaconStateElectra{
Slot: 4,
Validators: nil,
},
},
{
name: "empty state",
state: &ethpb.BeaconStateElectra{},
},
}
for _, tt := range initTests {
t.Run(tt.name, func(t *testing.T) {
_, err := statenative.InitializeFromProtoUnsafeElectra(tt.state)
if tt.error != "" {
assert.ErrorContains(t, tt.error, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestBeaconState_HashTreeRoot(t *testing.T) {
testState, _ := util.DeterministicGenesisState(t, 64)
@@ -772,3 +842,29 @@ func TestBeaconState_InitializeInactivityScoresCorrectly_Deneb(t *testing.T) {
require.DeepSSZEqual(t, rt, newRt)
}
func TestBeaconChainCopy_Electra(t *testing.T) {
// Load a serialized Electra state from disk.
// This is a fully hydrated random test case from spectests.
serializedBytes, err := util.BazelFileBytes("tests/mainnet/electra/ssz_static/BeaconState/ssz_random/case_0/serialized.ssz_snappy")
require.NoError(t, err)
serializedSSZ, err := snappy.Decode(nil /* dst */, serializedBytes)
require.NoError(t, err)
pb := &ethpb.BeaconStateElectra{}
require.NoError(t, pb.UnmarshalSSZ(serializedSSZ))
st, err := statenative.InitializeFromProtoElectra(pb)
require.NoError(t, err)
// Sanity check that InitializeFromProtoElectra and ToProto works
if !cmp.Equal(st.ToProto(), pb, protocmp.Transform()) {
t.Log(cmp.Diff(st.ToProto(), pb, protocmp.Transform()))
t.Fatal("InitializeFromProtoElectra does not match input proto")
}
// Perform the copy and check that the copied state matches the original state.
st2 := st.Copy()
if !cmp.Equal(st.ToProto(), st2.ToProto(), protocmp.Transform()) {
t.Log(cmp.Diff(st.ToProto(), st2.ToProto(), protocmp.Transform()))
t.Fatal("Copied state does not match original state")
}
}

View File

@@ -1,6 +1,8 @@
package types
import (
"fmt"
"github.com/pkg/errors"
consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types"
)
@@ -83,15 +85,37 @@ func (f FieldIndex) String() string {
case LatestExecutionPayloadHeader:
return "latestExecutionPayloadHeader"
case LatestExecutionPayloadHeaderCapella:
return "LatestExecutionPayloadHeaderCapella"
return "latestExecutionPayloadHeaderCapella"
case LatestExecutionPayloadHeaderDeneb:
return "latestExecutionPayloadHeaderDeneb"
case LatestExecutionPayloadHeaderElectra:
return "latestExecutionPayloadHeaderElectra"
case NextWithdrawalIndex:
return "NextWithdrawalIndex"
return "nextWithdrawalIndex"
case NextWithdrawalValidatorIndex:
return "NextWithdrawalValidatorIndex"
return "nextWithdrawalValidatorIndex"
case HistoricalSummaries:
return "HistoricalSummaries"
return "historicalSummaries"
case DepositReceiptsStartIndex:
return "depositReceiptsStartIndex"
case DepositBalanceToConsume:
return "depositBalanceToConsume"
case ExitBalanceToConsume:
return "exitBalanceToConsume"
case EarliestExitEpoch:
return "earliestExitEpoch"
case ConsolidationBalanceToConsume:
return "consolidationBalanceToConsume"
case EarliestConsolidationEpoch:
return "earliestConsolidationEpoch"
case PendingBalanceDeposits:
return "pendingBalanceDeposits"
case PendingPartialWithdrawals:
return "pendingPartialWithdrawals"
case PendingConsolidations:
return "pendingConsolidations"
default:
return ""
return fmt.Sprintf("unknown field index number: %d", f)
}
}
@@ -147,7 +171,7 @@ func (f FieldIndex) RealPosition() int {
return 22
case NextSyncCommittee:
return 23
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb:
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb, LatestExecutionPayloadHeaderElectra:
return 24
case NextWithdrawalIndex:
return 25
@@ -155,6 +179,24 @@ func (f FieldIndex) RealPosition() int {
return 26
case HistoricalSummaries:
return 27
case DepositReceiptsStartIndex:
return 28
case DepositBalanceToConsume:
return 29
case ExitBalanceToConsume:
return 30
case EarliestExitEpoch:
return 31
case ConsolidationBalanceToConsume:
return 32
case EarliestConsolidationEpoch:
return 33
case PendingBalanceDeposits:
return 34
case PendingPartialWithdrawals:
return 35
case PendingConsolidations:
return 36
default:
return -1
}
@@ -207,9 +249,19 @@ const (
LatestExecutionPayloadHeader
LatestExecutionPayloadHeaderCapella
LatestExecutionPayloadHeaderDeneb
LatestExecutionPayloadHeaderElectra
NextWithdrawalIndex
NextWithdrawalValidatorIndex
HistoricalSummaries
DepositReceiptsStartIndex // Electra: EIP-6110
DepositBalanceToConsume // Electra: EIP-7251
ExitBalanceToConsume // Electra: EIP-7251
EarliestExitEpoch // Electra: EIP-7251
ConsolidationBalanceToConsume // Electra: EIP-7251
EarliestConsolidationEpoch // Electra: EIP-7251
PendingBalanceDeposits // Electra: EIP-7251
PendingPartialWithdrawals // Electra: EIP-7251
PendingConsolidations // Electra: EIP-7251
)
// Enumerator keeps track of the number of states created since the node's start.

View File

@@ -12,7 +12,11 @@ go_library(
"historical_summaries_root.go",
"participation_bit_root.go",
"pending_attestation_root.go",
"pending_balance_deposits_root.go",
"pending_consolidations_root.go",
"pending_partial_withdrawals_root.go",
"reference.go",
"slice_root.go",
"sync_committee.root.go",
"trie_helpers.go",
"unrealized_justification.go",

View File

@@ -1,42 +1,10 @@
package stateutil
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
func HistoricalSummariesRoot(summaries []*ethpb.HistoricalSummary) ([32]byte, error) {
max := uint64(fieldparams.HistoricalRootsLength)
if uint64(len(summaries)) > max {
return [32]byte{}, fmt.Errorf("historical summary exceeds max length %d", max)
}
roots := make([][32]byte, len(summaries))
for i := 0; i < len(summaries); i++ {
r, err := summaries[i].HashTreeRoot()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not merkleize historical summary")
}
roots[i] = r
}
summariesRoot, err := ssz.BitwiseMerkleize(roots, uint64(len(roots)), fieldparams.HistoricalRootsLength)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute historical summaries merkleization")
}
summariesLenBuf := new(bytes.Buffer)
if err := binary.Write(summariesLenBuf, binary.LittleEndian, uint64(len(summaries))); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal historical summary length")
}
// We need to mix in the length of the slice.
summariesLenRoot := make([]byte, 32)
copy(summariesLenRoot, summariesLenBuf.Bytes())
res := ssz.MixInLength(summariesRoot, summariesLenRoot)
return res, nil
return SliceRoot(summaries, fieldparams.HistoricalRootsLength)
}

View File

@@ -0,0 +1,10 @@
package stateutil
import (
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
func PendingBalanceDepositsRoot(slice []*ethpb.PendingBalanceDeposit) ([32]byte, error) {
return SliceRoot(slice, fieldparams.PendingBalanceDepositsLimit)
}

View File

@@ -0,0 +1,10 @@
package stateutil
import (
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
func PendingConsolidationsRoot(slice []*ethpb.PendingConsolidation) ([32]byte, error) {
return SliceRoot(slice, fieldparams.PendingConsolidationsLimit)
}

View File

@@ -0,0 +1,10 @@
package stateutil
import (
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
func PendingPartialWithdrawalsRoot(slice []*ethpb.PendingPartialWithdrawal) ([32]byte, error) {
return SliceRoot(slice, fieldparams.PendingPartialWithdrawalsLimit)
}

View File

@@ -0,0 +1,41 @@
package stateutil
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
)
// SliceRoot computes the root of a slice of hashable objects.
func SliceRoot[T ssz.Hashable](slice []T, limit uint64) ([32]byte, error) {
max := limit
if uint64(len(slice)) > max {
return [32]byte{}, fmt.Errorf("slice exceeds max length %d", max)
}
roots := make([][32]byte, len(slice))
for i := 0; i < len(slice); i++ {
r, err := slice[i].HashTreeRoot()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not merkleize object")
}
roots[i] = r
}
sliceRoot, err := ssz.BitwiseMerkleize(roots, uint64(len(roots)), limit)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not slice merkleization")
}
sliceLenBuf := new(bytes.Buffer)
if err := binary.Write(sliceLenBuf, binary.LittleEndian, uint64(len(slice))); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal slice length")
}
// We need to mix in the length of the slice.
sliceLenRoot := make([]byte, 32)
copy(sliceLenRoot, sliceLenBuf.Bytes())
res := ssz.MixInLength(sliceRoot, sliceLenRoot)
return res, nil
}

View File

@@ -250,11 +250,11 @@ func AddInMixin(root [32]byte, length uint64) ([32]byte, error) {
// Merkleize 32-byte leaves into a Merkle trie for its adequate depth, returning
// the resulting layers of the trie based on the appropriate depth. This function
// pads the leaves to a length of 32.
// pads the leaves to a length of a multiple of 32.
func Merkleize(leaves [][]byte) [][][]byte {
hashFunc := hash.CustomSHA256Hasher()
layers := make([][][]byte, ssz.Depth(uint64(len(leaves)))+1)
for len(leaves) != 32 {
for len(leaves)%32 != 0 {
leaves = append(leaves, make([]byte, 32))
}
currentLayer := leaves

View File

@@ -236,20 +236,20 @@ type BeaconChainConfig struct {
MaxRequestBlocksDeneb uint64 `yaml:"MAX_REQUEST_BLOCKS_DENEB" spec:"true"` // MaxRequestBlocksDeneb is the maximum number of blocks in a single request after the deneb epoch.
// Values introduce in Electra upgrade
DataColumnSidecarSubnetCount uint64 `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT" spec:"true"` // DataColumnSidecarSubnetCount is the number of data column sidecar subnets used in the gossipsub protocol
MaxPerEpochActivationExitChurnLimit uint64 `yaml:"MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT" spec:"true"` // MaxPerEpochActivationExitChurnLimit represents the maximum combined activation and exit churn.
MinPerEpochChurnLimitElectra uint64 `yaml:"MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA" spec:"true"` // MinPerEpochChurnLimitElectra is the minimum amount of churn allotted for validator rotations for electra.
MaxRequestDataColumnSidecars uint64 `yaml:"MAX_REQUEST_DATA_COLUMN_SIDECARS" spec:"true"` // MaxRequestDataColumnSidecars is the maximum number of data column sidecars in a single request
MaxEffectiveBalanceElectra uint64 `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA" spec:"true"` // MaxEffectiveBalanceElectra is the maximal amount of Gwei that is effective for staking, increased in electra.
MinSlashingPenaltyQuotientElectra uint64 `yaml:"MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA" spec:"true"` // MinSlashingPenaltyQuotientElectra is used to calculate the minimum penalty to prevent DoS attacks, modified for electra.
WhistleBlowerRewardQuotientElectra uint64 `yaml:"WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA" spec:"true"` // WhistleBlowerRewardQuotientElectra is used to calculate whistle blower reward, modified in electra.
PendingBalanceDepositLimit uint64 `yaml:"PENDING_BALANCE_DEPOSITS_LIMIT" spec:"true"` // PendingBalanceDepositLimit is the maximum number of pending balance deposits allowed in the beacon state.
PendingPartialWithdrawalsLimit uint64 `yaml:"PENDING_PARTIAL_WITHDRAWALS_LIMIT" spec:"true"` // PendingPartialWithdrawalsLimit is the maximum number of pending partial withdrawals allowed in the beacon state.
PendingConsolidationsLimit uint64 `yaml:"PENDING_CONSOLIDATIONS_LIMIT" spec:"true"` // PendingConsolidationsLimit is the maximum number of pending validator consolidations allowed in the beacon state.
MaxConsolidations uint64 `yaml:"MAX_CONSOLIDATIONS" spec:"true"` // MaxConsolidations is the maximum number of consolidations in a block.
MaxPendingPartialsPerWithdrawalSweep uint64 `yaml:"MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP" spec:"true"` // MaxPendingPartialsPerWithdrawalSweep is the maximum number of pending partial withdrawals to process per payload.
FullExitRequestAmount uint64 `yaml:"FULL_EXIT_REQUEST_AMOUNT" spec:"true"` // FullExitRequestAmount is the amount of Gwei required to request a full exit.
MaxWithdrawalRequestsPerPayload uint64 `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD" spec:"true"` // MaxWithdrawalRequestsPerPayload is the maximum number of execution layer withdrawal requests in each payload.
DataColumnSidecarSubnetCount uint64 `yaml:"DATA_COLUMN_SIDECAR_SUBNET_COUNT" spec:"true"` // DataColumnSidecarSubnetCount is the number of data column sidecar subnets used in the gossipsub protocol
MaxPerEpochActivationExitChurnLimit uint64 `yaml:"MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT" spec:"true"` // MaxPerEpochActivationExitChurnLimit represents the maximum combined activation and exit churn.
MinPerEpochChurnLimitElectra uint64 `yaml:"MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA" spec:"true"` // MinPerEpochChurnLimitElectra is the minimum amount of churn allotted for validator rotations for electra.
MaxRequestDataColumnSidecars uint64 `yaml:"MAX_REQUEST_DATA_COLUMN_SIDECARS" spec:"true"` // MaxRequestDataColumnSidecars is the maximum number of data column sidecars in a single request
MaxEffectiveBalanceElectra uint64 `yaml:"MAX_EFFECTIVE_BALANCE_ELECTRA" spec:"true"` // MaxEffectiveBalanceElectra is the maximal amount of Gwei that is effective for staking, increased in electra.
MinSlashingPenaltyQuotientElectra uint64 `yaml:"MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA" spec:"true"` // MinSlashingPenaltyQuotientElectra is used to calculate the minimum penalty to prevent DoS attacks, modified for electra.
WhistleBlowerRewardQuotientElectra uint64 `yaml:"WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA" spec:"true"` // WhistleBlowerRewardQuotientElectra is used to calculate whistle blower reward, modified in electra.
PendingBalanceDepositLimit uint64 `yaml:"PENDING_BALANCE_DEPOSITS_LIMIT" spec:"true"` // PendingBalanceDepositLimit is the maximum number of pending balance deposits allowed in the beacon state.
PendingPartialWithdrawalsLimit uint64 `yaml:"PENDING_PARTIAL_WITHDRAWALS_LIMIT" spec:"true"` // PendingPartialWithdrawalsLimit is the maximum number of pending partial withdrawals allowed in the beacon state.
PendingConsolidationsLimit uint64 `yaml:"PENDING_CONSOLIDATIONS_LIMIT" spec:"true"` // PendingConsolidationsLimit is the maximum number of pending validator consolidations allowed in the beacon state.
MaxConsolidations uint64 `yaml:"MAX_CONSOLIDATIONS" spec:"true"` // MaxConsolidations is the maximum number of consolidations in a block.
MaxPendingPartialsPerWithdrawalsSweep uint64 `yaml:"MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP" spec:"true"` // MaxPendingPartialsPerWithdrawalsSweep is the maximum number of pending partial withdrawals to process per payload.
FullExitRequestAmount uint64 `yaml:"FULL_EXIT_REQUEST_AMOUNT" spec:"true"` // FullExitRequestAmount is the amount of Gwei required to request a full exit.
MaxWithdrawalRequestsPerPayload uint64 `yaml:"MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD" spec:"true"` // MaxWithdrawalRequestsPerPayload is the maximum number of execution layer withdrawal requests in each payload.
// Networking Specific Parameters
GossipMaxSize uint64 `yaml:"GOSSIP_MAX_SIZE" spec:"true"` // GossipMaxSize is the maximum allowed size of uncompressed gossip messages.
@@ -288,6 +288,7 @@ func configForkSchedule(b *BeaconChainConfig) map[[fieldparams.VersionLength]byt
fvs[bytesutil.ToBytes4(b.BellatrixForkVersion)] = b.BellatrixForkEpoch
fvs[bytesutil.ToBytes4(b.CapellaForkVersion)] = b.CapellaForkEpoch
fvs[bytesutil.ToBytes4(b.DenebForkVersion)] = b.DenebForkEpoch
fvs[bytesutil.ToBytes4(b.ElectraForkVersion)] = b.ElectraForkEpoch
return fvs
}
@@ -309,6 +310,7 @@ func ConfigForkVersions(b *BeaconChainConfig) map[[fieldparams.VersionLength]byt
bytesutil.ToBytes4(b.BellatrixForkVersion): version.Bellatrix,
bytesutil.ToBytes4(b.CapellaForkVersion): version.Capella,
bytesutil.ToBytes4(b.DenebForkVersion): version.Deneb,
bytesutil.ToBytes4(b.ElectraForkVersion): version.Electra,
}
}

View File

@@ -11,6 +11,7 @@ func InteropConfig() *BeaconChainConfig {
c.BellatrixForkVersion = []byte{2, 0, 0, 235}
c.CapellaForkVersion = []byte{3, 0, 0, 235}
c.DenebForkVersion = []byte{4, 0, 0, 235}
c.ElectraForkVersion = []byte{5, 0, 0, 235}
c.InitializeForkSchedule()
return c

View File

@@ -214,6 +214,8 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
fmt.Sprintf("BLOB_SIDECAR_SUBNET_COUNT: %d", cfg.BlobsidecarSubnetCount),
fmt.Sprintf("DENEB_FORK_EPOCH: %d", cfg.DenebForkEpoch),
fmt.Sprintf("DENEB_FORK_VERSION: %#x", cfg.DenebForkVersion),
fmt.Sprintf("ELECTRA_FORK_EPOCH: %d", cfg.ElectraForkEpoch),
fmt.Sprintf("ELECTRA_FORK_VERSION: %#x", cfg.ElectraForkVersion),
fmt.Sprintf("EPOCHS_PER_SUBNET_SUBSCRIPTION: %d", cfg.EpochsPerSubnetSubscription),
fmt.Sprintf("ATTESTATION_SUBNET_EXTRA_BITS: %d", cfg.AttestationSubnetExtraBits),
fmt.Sprintf("ATTESTATION_SUBNET_PREFIX_BITS: %d", cfg.AttestationSubnetPrefixBits),

View File

@@ -275,21 +275,21 @@ var mainnetBeaconConfig = &BeaconChainConfig{
MaxRequestBlocksDeneb: 128,
// Values related to electra
MaxRequestDataColumnSidecars: 16384,
DataColumnSidecarSubnetCount: 32,
MinPerEpochChurnLimitElectra: 128_000_000_000,
MaxPerEpochActivationExitChurnLimit: 256_000_000_000,
MaxEffectiveBalanceElectra: 2048_000_000_000,
MinSlashingPenaltyQuotientElectra: 4096,
WhistleBlowerRewardQuotientElectra: 4096,
PendingBalanceDepositLimit: 134_217_728,
PendingPartialWithdrawalsLimit: 134_217_728,
PendingConsolidationsLimit: 262_144,
MinActivationBalance: 32_000_000_000,
MaxConsolidations: 1,
MaxPendingPartialsPerWithdrawalSweep: 8,
FullExitRequestAmount: 0,
MaxWithdrawalRequestsPerPayload: 16,
MaxRequestDataColumnSidecars: 16384,
DataColumnSidecarSubnetCount: 32,
MinPerEpochChurnLimitElectra: 128_000_000_000,
MaxPerEpochActivationExitChurnLimit: 256_000_000_000,
MaxEffectiveBalanceElectra: 2048_000_000_000,
MinSlashingPenaltyQuotientElectra: 4096,
WhistleBlowerRewardQuotientElectra: 4096,
PendingBalanceDepositLimit: 134_217_728,
PendingPartialWithdrawalsLimit: 134_217_728,
PendingConsolidationsLimit: 262_144,
MinActivationBalance: 32_000_000_000,
MaxConsolidations: 1,
MaxPendingPartialsPerWithdrawalsSweep: 8,
FullExitRequestAmount: 0,
MaxWithdrawalRequestsPerPayload: 16,
// Values related to networking parameters.
GossipMaxSize: 10 * 1 << 20, // 10 MiB
@@ -332,16 +332,19 @@ func FillTestVersions(c *BeaconChainConfig, b byte) {
c.BellatrixForkVersion = make([]byte, fieldparams.VersionLength)
c.CapellaForkVersion = make([]byte, fieldparams.VersionLength)
c.DenebForkVersion = make([]byte, fieldparams.VersionLength)
c.ElectraForkVersion = make([]byte, fieldparams.VersionLength)
c.GenesisForkVersion[fieldparams.VersionLength-1] = b
c.AltairForkVersion[fieldparams.VersionLength-1] = b
c.BellatrixForkVersion[fieldparams.VersionLength-1] = b
c.CapellaForkVersion[fieldparams.VersionLength-1] = b
c.DenebForkVersion[fieldparams.VersionLength-1] = b
c.ElectraForkVersion[fieldparams.VersionLength-1] = b
c.GenesisForkVersion[0] = 0
c.AltairForkVersion[0] = 1
c.BellatrixForkVersion[0] = 2
c.CapellaForkVersion[0] = 3
c.DenebForkVersion[0] = 4
c.ElectraForkVersion[0] = 5
}

View File

@@ -108,7 +108,7 @@ func MinimalSpecConfig() *BeaconChainConfig {
minimalConfig.MaxPartialWithdrawalsPerPayload = 1
minimalConfig.MaxWithdrawalRequestsPerPayload = 2
minimalConfig.PendingPartialWithdrawalsLimit = 64
minimalConfig.MaxPendingPartialsPerWithdrawalSweep = 1
minimalConfig.MaxPendingPartialsPerWithdrawalsSweep = 1
// Ethereum PoW parameters.
minimalConfig.DepositChainID = 5 // Chain ID of eth1 goerli.

View File

@@ -44,6 +44,9 @@ CAPELLA_FORK_EPOCH: 10
# Deneb
DENEB_FORK_VERSION: 0x040000fd
DENEB_FORK_EPOCH: 12
# Electra
ELECTRA_FORK_VERSION: 0x050000fd
ELECTRA_FORK_EPOCH: 18446744073709551615
# Time parameters

View File

@@ -1,10 +1,13 @@
package params
import "math"
const (
AltairE2EForkEpoch = 6
BellatrixE2EForkEpoch = 8
CapellaE2EForkEpoch = 10
DenebE2EForkEpoch = 12
ElectraE2EForkEpoch = math.MaxUint64
)
// E2ETestConfig retrieves the configurations made specifically for E2E testing.
@@ -40,6 +43,7 @@ func E2ETestConfig() *BeaconChainConfig {
e2eConfig.BellatrixForkEpoch = BellatrixE2EForkEpoch
e2eConfig.CapellaForkEpoch = CapellaE2EForkEpoch
e2eConfig.DenebForkEpoch = DenebE2EForkEpoch
e2eConfig.ElectraForkEpoch = ElectraE2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "480"
@@ -51,6 +55,7 @@ func E2ETestConfig() *BeaconChainConfig {
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 253}
e2eConfig.CapellaForkVersion = []byte{3, 0, 0, 253}
e2eConfig.DenebForkVersion = []byte{4, 0, 0, 253}
e2eConfig.ElectraForkVersion = []byte{5, 0, 0, 253}
e2eConfig.InitializeForkSchedule()
return e2eConfig
@@ -82,6 +87,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
e2eConfig.BellatrixForkEpoch = BellatrixE2EForkEpoch
e2eConfig.CapellaForkEpoch = CapellaE2EForkEpoch
e2eConfig.DenebForkEpoch = DenebE2EForkEpoch
e2eConfig.ElectraForkEpoch = ElectraE2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "480"
@@ -93,6 +99,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 254}
e2eConfig.CapellaForkVersion = []byte{3, 0, 0, 254}
e2eConfig.DenebForkVersion = []byte{4, 0, 0, 254}
e2eConfig.ElectraForkVersion = []byte{5, 0, 0, 254}
// Deneb changes.
e2eConfig.MinPerEpochChurnLimit = 2

View File

@@ -1,5 +1,7 @@
package params
import "math"
// UseHoleskyNetworkConfig uses the Holesky beacon chain specific network config.
func UseHoleskyNetworkConfig() {
cfg := BeaconNetworkConfig().Copy()
@@ -36,6 +38,8 @@ func HoleskyConfig() *BeaconChainConfig {
cfg.CapellaForkVersion = []byte{0x4, 0x1, 0x70, 0x0}
cfg.DenebForkEpoch = 29696
cfg.DenebForkVersion = []byte{0x05, 0x1, 0x70, 0x0}
cfg.ElectraForkEpoch = math.MaxUint64
cfg.ElectraForkVersion = []byte{0x06, 0x1, 0x70, 0x0} // TODO: Define holesky fork version for electra. This is a placeholder value.
cfg.TerminalTotalDifficulty = "0"
cfg.DepositContractAddress = "0x4242424242424242424242424242424242424242"
cfg.EjectionBalance = 28000000000

View File

@@ -1,6 +1,8 @@
package params
import (
"math"
eth1Params "github.com/ethereum/go-ethereum/params"
)
@@ -37,6 +39,8 @@ func SepoliaConfig() *BeaconChainConfig {
cfg.CapellaForkVersion = []byte{0x90, 0x00, 0x00, 0x72}
cfg.DenebForkEpoch = 132608
cfg.DenebForkVersion = []byte{0x90, 0x00, 0x00, 0x73}
cfg.ElectraForkEpoch = math.MaxUint64
cfg.ElectraForkVersion = []byte{0x90, 0x00, 0x00, 0x74} // TODO: Define sepolia fork version for electra. This is a placeholder value.
cfg.TerminalTotalDifficulty = "17000000000000000"
cfg.DepositContractAddress = "0x7f02C3E3c98b133055B8B348B2Ac625669Ed295D"
cfg.InitializeForkSchedule()

View File

@@ -87,6 +87,8 @@ func FromForkVersion(cv [fieldparams.VersionLength]byte) (*VersionedUnmarshaler,
fork = version.Capella
case bytesutil.ToBytes4(cfg.DenebForkVersion):
fork = version.Deneb
case bytesutil.ToBytes4(cfg.ElectraForkVersion):
fork = version.Electra
default:
return nil, errors.Wrapf(ErrForkNotFound, "version=%#x", cv)
}
@@ -152,6 +154,16 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconState(marshaled []byte) (s state.
if err != nil {
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
}
case version.Electra:
st := &ethpb.BeaconStateElectra{}
err = st.UnmarshalSSZ(marshaled)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
}
s, err = state_native.InitializeFromProtoUnsafeElectra(st)
if err != nil {
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
}
default:
return nil, fmt.Errorf("unable to initialize BeaconState for fork version=%s", forkName)
}
@@ -200,6 +212,8 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (interfac
blk = &ethpb.SignedBeaconBlockCapella{}
case version.Deneb:
blk = &ethpb.SignedBeaconBlockDeneb{}
case version.Electra:
blk = &ethpb.SignedBeaconBlockElectra{}
default:
forkName := version.String(cf.Fork)
return nil, fmt.Errorf("unable to initialize ReadOnlyBeaconBlock for fork version=%s at slot=%d", forkName, slot)
@@ -235,6 +249,8 @@ func (cf *VersionedUnmarshaler) UnmarshalBlindedBeaconBlock(marshaled []byte) (i
blk = &ethpb.SignedBlindedBeaconBlockCapella{}
case version.Deneb:
blk = &ethpb.SignedBlindedBeaconBlockDeneb{}
case version.Electra:
blk = &ethpb.SignedBlindedBeaconBlockElectra{}
default:
forkName := version.String(cf.Fork)
return nil, fmt.Errorf("unable to initialize ReadOnlyBeaconBlock for fork version=%s at slot=%d", forkName, slot)

View File

@@ -47,7 +47,7 @@ func TestSlotFromBlock(t *testing.T) {
}
func TestByState(t *testing.T) {
undo := util.HackDenebMaxuint(t)
undo := util.HackElectraMaxuint(t)
defer undo()
bc := params.BeaconConfig()
altairSlot, err := slots.EpochStart(bc.AltairForkEpoch)
@@ -58,6 +58,8 @@ func TestByState(t *testing.T) {
require.NoError(t, err)
denebSlot, err := slots.EpochStart(bc.DenebForkEpoch)
require.NoError(t, err)
electraSlot, err := slots.EpochStart(bc.ElectraForkEpoch)
require.NoError(t, err)
cases := []struct {
name string
version int
@@ -94,6 +96,12 @@ func TestByState(t *testing.T) {
slot: denebSlot,
forkversion: bytesutil.ToBytes4(bc.DenebForkVersion),
},
{
name: "electra",
version: version.Electra,
slot: electraSlot,
forkversion: bytesutil.ToBytes4(bc.ElectraForkVersion),
},
}
for _, c := range cases {
st, err := stateForVersion(c.version)
@@ -126,6 +134,8 @@ func stateForVersion(v int) (state.BeaconState, error) {
return util.NewBeaconStateCapella()
case version.Deneb:
return util.NewBeaconStateDeneb()
case version.Electra:
return util.NewBeaconStateElectra()
default:
return nil, fmt.Errorf("unrecognized version %d", v)
}
@@ -133,7 +143,7 @@ func stateForVersion(v int) (state.BeaconState, error) {
func TestUnmarshalState(t *testing.T) {
ctx := context.Background()
undo := util.HackDenebMaxuint(t)
undo := util.HackElectraMaxuint(t)
defer undo()
bc := params.BeaconConfig()
altairSlot, err := slots.EpochStart(bc.AltairForkEpoch)
@@ -144,6 +154,8 @@ func TestUnmarshalState(t *testing.T) {
require.NoError(t, err)
denebSlot, err := slots.EpochStart(bc.DenebForkEpoch)
require.NoError(t, err)
electraSlot, err := slots.EpochStart(bc.ElectraForkEpoch)
require.NoError(t, err)
cases := []struct {
name string
version int
@@ -180,6 +192,12 @@ func TestUnmarshalState(t *testing.T) {
slot: denebSlot,
forkversion: bytesutil.ToBytes4(bc.DenebForkVersion),
},
{
name: "electra",
version: version.Electra,
slot: electraSlot,
forkversion: bytesutil.ToBytes4(bc.ElectraForkVersion),
},
}
for _, c := range cases {
st, err := stateForVersion(c.version)
@@ -205,7 +223,7 @@ func TestUnmarshalState(t *testing.T) {
}
func TestDetectAndUnmarshalBlock(t *testing.T) {
undo := util.HackDenebMaxuint(t)
undo := util.HackElectraMaxuint(t)
defer undo()
altairS, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
require.NoError(t, err)
@@ -215,6 +233,8 @@ func TestDetectAndUnmarshalBlock(t *testing.T) {
require.NoError(t, err)
denebS, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
electraS, err := slots.EpochStart(params.BeaconConfig().ElectraForkEpoch)
require.NoError(t, err)
cases := []struct {
b func(*testing.T, primitives.Slot) interfaces.ReadOnlySignedBeaconBlock
name string
@@ -260,6 +280,11 @@ func TestDetectAndUnmarshalBlock(t *testing.T) {
b: signedTestBlockDeneb,
slot: denebS,
},
{
name: "first slot of electra",
b: signedTestBlockElectra,
slot: electraS,
},
{
name: "bellatrix block in altair slot",
b: signedTestBlockBellatrix,
@@ -296,13 +321,14 @@ func TestDetectAndUnmarshalBlock(t *testing.T) {
}
func TestUnmarshalBlock(t *testing.T) {
undo := util.HackDenebMaxuint(t)
undo := util.HackElectraMaxuint(t)
defer undo()
genv := bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)
altairv := bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion)
bellav := bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion)
capellaV := bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion)
denebV := bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion)
electraV := bytesutil.ToBytes4(params.BeaconConfig().ElectraForkVersion)
altairS, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
require.NoError(t, err)
bellaS, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
@@ -311,6 +337,8 @@ func TestUnmarshalBlock(t *testing.T) {
require.NoError(t, err)
denebS, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
electraS, err := slots.EpochStart(params.BeaconConfig().ElectraForkEpoch)
require.NoError(t, err)
cases := []struct {
b func(*testing.T, primitives.Slot) interfaces.ReadOnlySignedBeaconBlock
name string
@@ -365,6 +393,12 @@ func TestUnmarshalBlock(t *testing.T) {
version: denebV,
slot: denebS,
},
{
name: "first slot of electra",
b: signedTestBlockElectra,
version: electraV,
slot: electraS,
},
{
name: "bellatrix block in altair slot",
b: signedTestBlockBellatrix,
@@ -409,13 +443,14 @@ func TestUnmarshalBlock(t *testing.T) {
}
func TestUnmarshalBlindedBlock(t *testing.T) {
undo := util.HackDenebMaxuint(t)
undo := util.HackElectraMaxuint(t)
defer undo()
genv := bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)
altairv := bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion)
bellav := bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion)
capellaV := bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion)
denebV := bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion)
electraV := bytesutil.ToBytes4(params.BeaconConfig().ElectraForkVersion)
altairS, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
require.NoError(t, err)
bellaS, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
@@ -424,6 +459,8 @@ func TestUnmarshalBlindedBlock(t *testing.T) {
require.NoError(t, err)
denebS, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
require.NoError(t, err)
electraS, err := slots.EpochStart(params.BeaconConfig().ElectraForkEpoch)
require.NoError(t, err)
cases := []struct {
b func(*testing.T, primitives.Slot) interfaces.ReadOnlySignedBeaconBlock
name string
@@ -485,6 +522,12 @@ func TestUnmarshalBlindedBlock(t *testing.T) {
version: denebV,
slot: denebS,
},
{
name: "first slot of electra",
b: signedTestBlindedBlockElectra,
version: electraV,
slot: electraS,
},
{
name: "genesis block in altair slot",
b: signedTestBlockGenesis,
@@ -577,6 +620,14 @@ func signedTestBlockDeneb(t *testing.T, slot primitives.Slot) interfaces.ReadOnl
return s
}
func signedTestBlockElectra(t *testing.T, slot primitives.Slot) interfaces.ReadOnlySignedBeaconBlock {
b := util.NewBeaconBlockElectra()
b.Block.Slot = slot
s, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return s
}
func signedTestBlindedBlockDeneb(t *testing.T, slot primitives.Slot) interfaces.ReadOnlySignedBeaconBlock {
b := util.NewBlindedBeaconBlockDeneb()
b.Message.Slot = slot
@@ -584,3 +635,11 @@ func signedTestBlindedBlockDeneb(t *testing.T, slot primitives.Slot) interfaces.
require.NoError(t, err)
return s
}
func signedTestBlindedBlockElectra(t *testing.T, slot primitives.Slot) interfaces.ReadOnlySignedBeaconBlock {
b := util.NewBlindedBeaconBlockElectra()
b.Message.Slot = slot
s, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return s
}

View File

@@ -5,7 +5,10 @@ go_library(
srcs = ["math_helper.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/math",
visibility = ["//visibility:public"],
deps = ["@com_github_thomaso_mirodin_intmath//u64:go_default_library"],
deps = [
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_thomaso_mirodin_intmath//u64:go_default_library",
],
)
go_test(

View File

@@ -3,11 +3,13 @@ package math
import (
"errors"
"fmt"
stdmath "math"
"math/big"
"math/bits"
"sync"
fssz "github.com/prysmaticlabs/fastssz"
"github.com/thomaso-mirodin/intmath/u64"
)
@@ -216,9 +218,53 @@ func AddInt(i ...int) (int, error) {
// Wei is the smallest unit of Ether, represented as a pointer to a bigInt.
type Wei *big.Int
var _ fssz.HashRoot = (Gwei)(0)
var _ fssz.Marshaler = (*Gwei)(nil)
var _ fssz.Unmarshaler = (*Gwei)(nil)
// Gwei is a denomination of 1e9 Wei represented as an uint64.
type Gwei uint64
// HashTreeRoot --
func (g Gwei) HashTreeRoot() ([32]byte, error) {
return fssz.HashWithDefaultHasher(g)
}
// HashTreeRootWith --
func (g Gwei) HashTreeRootWith(hh *fssz.Hasher) error {
hh.PutUint64(uint64(g))
return nil
}
// UnmarshalSSZ --
func (g *Gwei) UnmarshalSSZ(buf []byte) error {
if len(buf) != g.SizeSSZ() {
return fmt.Errorf("expected buffer of length %d received %d", g.SizeSSZ(), len(buf))
}
*g = Gwei(fssz.UnmarshallUint64(buf))
return nil
}
// MarshalSSZTo --
func (g *Gwei) MarshalSSZTo(dst []byte) ([]byte, error) {
marshalled, err := g.MarshalSSZ()
if err != nil {
return nil, err
}
return append(dst, marshalled...), nil
}
// MarshalSSZ --
func (g *Gwei) MarshalSSZ() ([]byte, error) {
marshalled := fssz.MarshalUint64([]byte{}, uint64(*g))
return marshalled, nil
}
// SizeSSZ --
func (g *Gwei) SizeSSZ() int {
return 8
}
// WeiToGwei converts big int wei to uint64 gwei.
// The input `v` is copied before being modified.
func WeiToGwei(v Wei) Gwei {

View File

@@ -51,6 +51,7 @@ ssz_gen_marshal(
includes = [
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//math:go_default_library",
],
objs = [
"BeaconBlockAltair",
@@ -162,6 +163,7 @@ go_proto_library(
visibility = ["//visibility:public"],
deps = [
"//consensus-types/primitives:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/ext:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
@@ -219,6 +221,7 @@ go_library(
visibility = ["//visibility:public"],
deps = SSZ_DEPS + [
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/ext:go_default_library",
"//runtime/version:go_default_library",

View File

@@ -12,6 +12,7 @@ import (
github_com_prysmaticlabs_go_bitfield "github.com/prysmaticlabs/go-bitfield"
github_com_prysmaticlabs_prysm_v5_consensus_types_primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
github_com_prysmaticlabs_prysm_v5_math "github.com/prysmaticlabs/prysm/v5/math"
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
_ "github.com/prysmaticlabs/prysm/v5/proto/eth/ext"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
@@ -1868,10 +1869,10 @@ type BeaconStateElectra struct {
NextWithdrawalValidatorIndex github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.ValidatorIndex `protobuf:"varint,11002,opt,name=next_withdrawal_validator_index,json=nextWithdrawalValidatorIndex,proto3" json:"next_withdrawal_validator_index,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.ValidatorIndex"`
HistoricalSummaries []*HistoricalSummary `protobuf:"bytes,11003,rep,name=historical_summaries,json=historicalSummaries,proto3" json:"historical_summaries,omitempty" ssz-max:"16777216"`
DepositReceiptsStartIndex uint64 `protobuf:"varint,12001,opt,name=deposit_receipts_start_index,json=depositReceiptsStartIndex,proto3" json:"deposit_receipts_start_index,omitempty"`
DepositBalanceToConsume uint64 `protobuf:"varint,12002,opt,name=deposit_balance_to_consume,json=depositBalanceToConsume,proto3" json:"deposit_balance_to_consume,omitempty"`
ExitBalanceToConsume uint64 `protobuf:"varint,12003,opt,name=exit_balance_to_consume,json=exitBalanceToConsume,proto3" json:"exit_balance_to_consume,omitempty"`
DepositBalanceToConsume github_com_prysmaticlabs_prysm_v5_math.Gwei `protobuf:"varint,12002,opt,name=deposit_balance_to_consume,json=depositBalanceToConsume,proto3" json:"deposit_balance_to_consume,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/math.Gwei"`
ExitBalanceToConsume github_com_prysmaticlabs_prysm_v5_math.Gwei `protobuf:"varint,12003,opt,name=exit_balance_to_consume,json=exitBalanceToConsume,proto3" json:"exit_balance_to_consume,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/math.Gwei"`
EarliestExitEpoch github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch `protobuf:"varint,12004,opt,name=earliest_exit_epoch,json=earliestExitEpoch,proto3" json:"earliest_exit_epoch,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"`
ConsolidationBalanceToConsume uint64 `protobuf:"varint,12005,opt,name=consolidation_balance_to_consume,json=consolidationBalanceToConsume,proto3" json:"consolidation_balance_to_consume,omitempty"`
ConsolidationBalanceToConsume github_com_prysmaticlabs_prysm_v5_math.Gwei `protobuf:"varint,12005,opt,name=consolidation_balance_to_consume,json=consolidationBalanceToConsume,proto3" json:"consolidation_balance_to_consume,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/math.Gwei"`
EarliestConsolidationEpoch github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch `protobuf:"varint,12006,opt,name=earliest_consolidation_epoch,json=earliestConsolidationEpoch,proto3" json:"earliest_consolidation_epoch,omitempty" cast-type:"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"`
PendingBalanceDeposits []*PendingBalanceDeposit `protobuf:"bytes,12007,rep,name=pending_balance_deposits,json=pendingBalanceDeposits,proto3" json:"pending_balance_deposits,omitempty" ssz-max:"134217728"`
PendingPartialWithdrawals []*PendingPartialWithdrawal `protobuf:"bytes,12008,rep,name=pending_partial_withdrawals,json=pendingPartialWithdrawals,proto3" json:"pending_partial_withdrawals,omitempty" ssz-max:"134217728"`
@@ -2113,18 +2114,18 @@ func (x *BeaconStateElectra) GetDepositReceiptsStartIndex() uint64 {
return 0
}
func (x *BeaconStateElectra) GetDepositBalanceToConsume() uint64 {
func (x *BeaconStateElectra) GetDepositBalanceToConsume() github_com_prysmaticlabs_prysm_v5_math.Gwei {
if x != nil {
return x.DepositBalanceToConsume
}
return 0
return github_com_prysmaticlabs_prysm_v5_math.Gwei(0)
}
func (x *BeaconStateElectra) GetExitBalanceToConsume() uint64 {
func (x *BeaconStateElectra) GetExitBalanceToConsume() github_com_prysmaticlabs_prysm_v5_math.Gwei {
if x != nil {
return x.ExitBalanceToConsume
}
return 0
return github_com_prysmaticlabs_prysm_v5_math.Gwei(0)
}
func (x *BeaconStateElectra) GetEarliestExitEpoch() github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch {
@@ -2134,11 +2135,11 @@ func (x *BeaconStateElectra) GetEarliestExitEpoch() github_com_prysmaticlabs_pry
return github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch(0)
}
func (x *BeaconStateElectra) GetConsolidationBalanceToConsume() uint64 {
func (x *BeaconStateElectra) GetConsolidationBalanceToConsume() github_com_prysmaticlabs_prysm_v5_math.Gwei {
if x != nil {
return x.ConsolidationBalanceToConsume
}
return 0
return github_com_prysmaticlabs_prysm_v5_math.Gwei(0)
}
func (x *BeaconStateElectra) GetEarliestConsolidationEpoch() github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch {
@@ -3010,7 +3011,7 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_rawDesc = []byte{
0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72,
0x79, 0x42, 0x0c, 0x92, 0xb5, 0x18, 0x08, 0x31, 0x36, 0x37, 0x37, 0x37, 0x32, 0x31, 0x36, 0x52,
0x13, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61,
0x72, 0x69, 0x65, 0x73, 0x22, 0xfa, 0x17, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53,
0x72, 0x69, 0x65, 0x73, 0x22, 0x8d, 0x19, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53,
0x74, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x67,
0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0xe9, 0x07, 0x20, 0x01,
0x28, 0x04, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12,
@@ -3151,85 +3152,94 @@ var file_proto_prysm_v1alpha1_beacon_state_proto_rawDesc = []byte{
0x69, 0x70, 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
0x18, 0xe1, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x49, 0x6e, 0x64,
0x65, 0x78, 0x12, 0x3c, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x62, 0x61,
0x65, 0x78, 0x12, 0x6d, 0x0a, 0x1a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x62, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
0x18, 0xe2, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
0x12, 0x36, 0x0a, 0x17, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65,
0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x18, 0xe3, 0x5d, 0x20, 0x01,
0x28, 0x04, 0x52, 0x14, 0x65, 0x78, 0x69, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x54,
0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x77, 0x0a, 0x13, 0x65, 0x61, 0x72, 0x6c,
0x69, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18,
0xe4, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69, 0x74, 0x68,
0x18, 0xe2, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2f, 0x82, 0xb5, 0x18, 0x2b, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69,
0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x6d,
0x61, 0x74, 0x68, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x17, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d,
0x65, 0x12, 0x67, 0x0a, 0x17, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x18, 0xe3, 0x5d, 0x20,
0x01, 0x28, 0x04, 0x42, 0x2f, 0x82, 0xb5, 0x18, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e,
0x47, 0x77, 0x65, 0x69, 0x52, 0x14, 0x65, 0x78, 0x69, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x54, 0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x77, 0x0a, 0x13, 0x65, 0x61,
0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63,
0x68, 0x18, 0xe4, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74,
0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f,
0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f,
0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68,
0x52, 0x11, 0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x45, 0x78, 0x69, 0x74, 0x45, 0x70,
0x6f, 0x63, 0x68, 0x12, 0x79, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x5f,
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x18, 0xe5, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x42, 0x2f,
0x82, 0xb5, 0x18, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70,
0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79,
0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52,
0x1d, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x89,
0x01, 0x0a, 0x1c, 0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73,
0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18,
0xe6, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63,
0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x63, 0x6f,
0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72,
0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x11,
0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x45, 0x78, 0x69, 0x74, 0x45, 0x70, 0x6f, 0x63,
0x68, 0x12, 0x48, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x6f,
0x6e, 0x73, 0x75, 0x6d, 0x65, 0x18, 0xe5, 0x5d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1d, 0x63, 0x6f,
0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x54, 0x6f, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x89, 0x01, 0x0a, 0x1c,
0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69,
0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0xe6, 0x5d, 0x20,
0x01, 0x28, 0x04, 0x42, 0x46, 0x82, 0xb5, 0x18, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65,
0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69,
0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x1a, 0x65, 0x61, 0x72,
0x6c, 0x69, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x76, 0x0a, 0x18, 0x70, 0x65, 0x6e, 0x64, 0x69,
0x6e, 0x67, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x73, 0x18, 0xe7, 0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68,
0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
0x61, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x33,
0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12,
0x7f, 0x0a, 0x1b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69,
0x61, 0x6c, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x18, 0xe8,
0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d,
0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68,
0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x33, 0x34, 0x32,
0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61,
0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73,
0x12, 0x6f, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x73,
0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe9, 0x5d, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68,
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e,
0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a,
0x92, 0xb5, 0x18, 0x06, 0x32, 0x36, 0x32, 0x31, 0x34, 0x34, 0x52, 0x15, 0x70, 0x65, 0x6e, 0x64,
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x22, 0x8d, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x25,
0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f,
0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02,
0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x31,
0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c,
0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32,
0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
0x79, 0x22, 0x7f, 0x0a, 0x11, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x61, 0x6c, 0x53,
0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x12, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, 0x12,
0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f,
0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32,
0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f,
0x6f, 0x74, 0x42, 0x9b, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x1a,
0x65, 0x61, 0x72, 0x6c, 0x69, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x76, 0x0a, 0x18, 0x70, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x64, 0x65,
0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0xe7, 0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e,
0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x0d, 0x92, 0xb5, 0x18,
0x09, 0x31, 0x33, 0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64,
0x69, 0x6e, 0x67, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x73, 0x12, 0x7f, 0x0a, 0x1b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61,
0x72, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c,
0x73, 0x18, 0xe8, 0x5d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
0x42, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f,
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x70,
0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72,
0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68,
0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e,
0x56, 0x31, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57,
0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31,
0x33, 0x34, 0x32, 0x31, 0x37, 0x37, 0x32, 0x38, 0x52, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e,
0x67, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77,
0x61, 0x6c, 0x73, 0x12, 0x6f, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63,
0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe9, 0x5d,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e,
0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x6e,
0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x42, 0x0a, 0x92, 0xb5, 0x18, 0x06, 0x32, 0x36, 0x32, 0x31, 0x34, 0x34, 0x52, 0x15, 0x70,
0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x69, 0x64, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x77, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65,
0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a,
0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73,
0x68, 0x12, 0x31, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69,
0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18,
0x02, 0x33, 0x32, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63,
0x75, 0x6c, 0x74, 0x79, 0x22, 0x7f, 0x0a, 0x11, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63,
0x61, 0x6c, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x12, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x10, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x6f, 0x6f, 0x74, 0x12,
0x34, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79,
0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18,
0x02, 0x33, 0x32, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72,
0x79, 0x52, 0x6f, 0x6f, 0x74, 0x42, 0x9b, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x42, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x6c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b,
0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45,
0x74, 0x68, 0x2e, 0x56, 0x31, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -397,10 +397,10 @@ message BeaconStateElectra {
// Fields introduced in EIP-7251 fork [12001-13000]
uint64 deposit_receipts_start_index = 12001;
uint64 deposit_balance_to_consume = 12002;
uint64 exit_balance_to_consume = 12003;
uint64 deposit_balance_to_consume = 12002 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/math.Gwei"];
uint64 exit_balance_to_consume = 12003 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/math.Gwei"];
uint64 earliest_exit_epoch = 12004 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"];
uint64 consolidation_balance_to_consume = 12005;
uint64 consolidation_balance_to_consume = 12005 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/math.Gwei"];
uint64 earliest_consolidation_epoch = 12006 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives.Epoch"];
repeated PendingBalanceDeposit pending_balance_deposits = 12007 [(ethereum.eth.ext.ssz_max) = "pending_balance_deposits_limit"];
repeated PendingPartialWithdrawal pending_partial_withdrawals = 12008 [(ethereum.eth.ext.ssz_max) = "pending_partial_withdrawals_limit"];

View File

@@ -1,10 +1,11 @@
// Code generated by fastssz. DO NOT EDIT.
// Hash: ae92821338b1fc2296cda7b35445bbd7005303c62eab0bcef3ef9c4c7cf0b9ef
// Hash: 4e7c07afe12b97ee034415333c33967cab8cf624f3fd43f37562cd6307760ec3
package eth
import (
ssz "github.com/prysmaticlabs/fastssz"
github_com_prysmaticlabs_prysm_v5_consensus_types_primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
github_com_prysmaticlabs_prysm_v5_math "github.com/prysmaticlabs/prysm/v5/math"
v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
)
@@ -17646,16 +17647,16 @@ func (b *BeaconStateElectra) MarshalSSZTo(buf []byte) (dst []byte, err error) {
dst = ssz.MarshalUint64(dst, b.DepositReceiptsStartIndex)
// Field (29) 'DepositBalanceToConsume'
dst = ssz.MarshalUint64(dst, b.DepositBalanceToConsume)
dst = ssz.MarshalUint64(dst, uint64(b.DepositBalanceToConsume))
// Field (30) 'ExitBalanceToConsume'
dst = ssz.MarshalUint64(dst, b.ExitBalanceToConsume)
dst = ssz.MarshalUint64(dst, uint64(b.ExitBalanceToConsume))
// Field (31) 'EarliestExitEpoch'
dst = ssz.MarshalUint64(dst, uint64(b.EarliestExitEpoch))
// Field (32) 'ConsolidationBalanceToConsume'
dst = ssz.MarshalUint64(dst, b.ConsolidationBalanceToConsume)
dst = ssz.MarshalUint64(dst, uint64(b.ConsolidationBalanceToConsume))
// Field (33) 'EarliestConsolidationEpoch'
dst = ssz.MarshalUint64(dst, uint64(b.EarliestConsolidationEpoch))
@@ -17979,16 +17980,16 @@ func (b *BeaconStateElectra) UnmarshalSSZ(buf []byte) error {
b.DepositReceiptsStartIndex = ssz.UnmarshallUint64(buf[2736653:2736661])
// Field (29) 'DepositBalanceToConsume'
b.DepositBalanceToConsume = ssz.UnmarshallUint64(buf[2736661:2736669])
b.DepositBalanceToConsume = github_com_prysmaticlabs_prysm_v5_math.Gwei(ssz.UnmarshallUint64(buf[2736661:2736669]))
// Field (30) 'ExitBalanceToConsume'
b.ExitBalanceToConsume = ssz.UnmarshallUint64(buf[2736669:2736677])
b.ExitBalanceToConsume = github_com_prysmaticlabs_prysm_v5_math.Gwei(ssz.UnmarshallUint64(buf[2736669:2736677]))
// Field (31) 'EarliestExitEpoch'
b.EarliestExitEpoch = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch(ssz.UnmarshallUint64(buf[2736677:2736685]))
// Field (32) 'ConsolidationBalanceToConsume'
b.ConsolidationBalanceToConsume = ssz.UnmarshallUint64(buf[2736685:2736693])
b.ConsolidationBalanceToConsume = github_com_prysmaticlabs_prysm_v5_math.Gwei(ssz.UnmarshallUint64(buf[2736685:2736693]))
// Field (33) 'EarliestConsolidationEpoch'
b.EarliestConsolidationEpoch = github_com_prysmaticlabs_prysm_v5_consensus_types_primitives.Epoch(ssz.UnmarshallUint64(buf[2736693:2736701]))
@@ -18567,16 +18568,16 @@ func (b *BeaconStateElectra) HashTreeRootWith(hh *ssz.Hasher) (err error) {
hh.PutUint64(b.DepositReceiptsStartIndex)
// Field (29) 'DepositBalanceToConsume'
hh.PutUint64(b.DepositBalanceToConsume)
hh.PutUint64(uint64(b.DepositBalanceToConsume))
// Field (30) 'ExitBalanceToConsume'
hh.PutUint64(b.ExitBalanceToConsume)
hh.PutUint64(uint64(b.ExitBalanceToConsume))
// Field (31) 'EarliestExitEpoch'
hh.PutUint64(uint64(b.EarliestExitEpoch))
// Field (32) 'ConsolidationBalanceToConsume'
hh.PutUint64(b.ConsolidationBalanceToConsume)
hh.PutUint64(uint64(b.ConsolidationBalanceToConsume))
// Field (33) 'EarliestConsolidationEpoch'
hh.PutUint64(uint64(b.EarliestConsolidationEpoch))

View File

@@ -7,8 +7,10 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/ssz_static",
visibility = ["//testing/spectest:__subpackages__"],
deps = [
"//beacon-chain/state/state-native:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//testing/spectest/shared/common/ssz_static:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
],

View File

@@ -1,12 +1,15 @@
package ssz_static
import (
"context"
"errors"
"testing"
fssz "github.com/prysmaticlabs/fastssz"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
common "github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/common/ssz_static"
)
@@ -16,26 +19,21 @@ func RunSSZStaticTests(t *testing.T, config string) {
}
func customHtr(t *testing.T, htrs []common.HTR, object interface{}) []common.HTR {
// TODO: Replace BeaconStateDeneb with BeaconStateElectra below and uncomment the code
//_, ok := object.(*ethpb.BeaconStateDeneb)
//if !ok {
// return htrs
//}
//
//htrs = append(htrs, func(s interface{}) ([32]byte, error) {
// beaconState, err := state_native.InitializeFromProtoDeneb(s.(*ethpb.BeaconStateDeneb))
// require.NoError(t, err)
// return beaconState.HashTreeRoot(context.Background())
//})
_, ok := object.(*ethpb.BeaconStateElectra)
if !ok {
return htrs
}
htrs = append(htrs, func(s interface{}) ([32]byte, error) {
beaconState, err := state_native.InitializeFromProtoElectra(s.(*ethpb.BeaconStateElectra))
require.NoError(t, err)
return beaconState.HashTreeRoot(context.Background())
})
return htrs
}
// UnmarshalledSSZ unmarshalls serialized input.
func UnmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) {
// TODO: Remove this check once BeaconState custom HTR function is ready
if folderName == "BeaconState" {
t.Skip("BeaconState is not ready")
}
var obj interface{}
switch folderName {
case "ExecutionPayload":

View File

@@ -16,6 +16,7 @@ go_library(
"deneb.go",
"deneb_state.go",
"deposits.go",
"electra.go",
"helpers.go",
"merge.go",
"state.go",

View File

@@ -1140,6 +1140,19 @@ func HydrateSignedBeaconBlockDeneb(b *ethpb.SignedBeaconBlockDeneb) *ethpb.Signe
return b
}
// HydrateSignedBeaconBlockElectra hydrates a signed beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateSignedBeaconBlockElectra(b *ethpb.SignedBeaconBlockElectra) *ethpb.SignedBeaconBlockElectra {
if b == nil {
b = &ethpb.SignedBeaconBlockElectra{}
}
if b.Signature == nil {
b.Signature = make([]byte, fieldparams.BLSSignatureLength)
}
b.Block = HydrateBeaconBlockElectra(b.Block)
return b
}
// HydrateSignedBeaconBlockContentsDeneb hydrates a signed beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateSignedBeaconBlockContentsDeneb(b *ethpb.SignedBeaconBlockContentsDeneb) *ethpb.SignedBeaconBlockContentsDeneb {
@@ -1173,6 +1186,22 @@ func HydrateBeaconBlockDeneb(b *ethpb.BeaconBlockDeneb) *ethpb.BeaconBlockDeneb
return b
}
// HydrateBeaconBlockElectra hydrates a beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateBeaconBlockElectra(b *ethpb.BeaconBlockElectra) *ethpb.BeaconBlockElectra {
if b == nil {
b = &ethpb.BeaconBlockElectra{}
}
if b.ParentRoot == nil {
b.ParentRoot = make([]byte, fieldparams.RootLength)
}
if b.StateRoot == nil {
b.StateRoot = make([]byte, fieldparams.RootLength)
}
b.Body = HydrateBeaconBlockBodyElectra(b.Body)
return b
}
// HydrateV2BeaconBlockDeneb hydrates a v2 beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV2BeaconBlockDeneb(b *v2.BeaconBlockDeneb) *v2.BeaconBlockDeneb {
@@ -1231,6 +1260,50 @@ func HydrateBeaconBlockBodyDeneb(b *ethpb.BeaconBlockBodyDeneb) *ethpb.BeaconBlo
return b
}
// HydrateBeaconBlockBodyElectra hydrates a beacon block body with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateBeaconBlockBodyElectra(b *ethpb.BeaconBlockBodyElectra) *ethpb.BeaconBlockBodyElectra {
if b == nil {
b = &ethpb.BeaconBlockBodyElectra{}
}
if b.RandaoReveal == nil {
b.RandaoReveal = make([]byte, fieldparams.BLSSignatureLength)
}
if b.Graffiti == nil {
b.Graffiti = make([]byte, fieldparams.RootLength)
}
if b.Eth1Data == nil {
b.Eth1Data = &ethpb.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
}
}
if b.SyncAggregate == nil {
b.SyncAggregate = &ethpb.SyncAggregate{
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
}
}
if b.ExecutionPayload == nil {
b.ExecutionPayload = &enginev1.ExecutionPayloadElectra{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
Withdrawals: make([]*enginev1.Withdrawal, 0),
DepositReceipts: make([]*enginev1.DepositReceipt, 0),
WithdrawalRequests: make([]*enginev1.ExecutionLayerWithdrawalRequest, 0),
}
}
return b
}
// HydrateV2BeaconBlockBodyDeneb hydrates a v2 beacon block body with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV2BeaconBlockBodyDeneb(b *v2.BeaconBlockBodyDeneb) *v2.BeaconBlockBodyDeneb {
@@ -1283,6 +1356,16 @@ func HydrateSignedBlindedBeaconBlockDeneb(b *ethpb.SignedBlindedBeaconBlockDeneb
return b
}
// HydrateSignedBlindedBeaconBlockElectra hydrates a signed blinded beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateSignedBlindedBeaconBlockElectra(b *ethpb.SignedBlindedBeaconBlockElectra) *ethpb.SignedBlindedBeaconBlockElectra {
if b.Signature == nil {
b.Signature = make([]byte, fieldparams.BLSSignatureLength)
}
b.Message = HydrateBlindedBeaconBlockElectra(b.Message)
return b
}
// HydrateV2SignedBlindedBeaconBlockDeneb hydrates a signed v2 blinded beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV2SignedBlindedBeaconBlockDeneb(b *v2.SignedBlindedBeaconBlockDeneb) *v2.SignedBlindedBeaconBlockDeneb {
@@ -1309,6 +1392,22 @@ func HydrateBlindedBeaconBlockDeneb(b *ethpb.BlindedBeaconBlockDeneb) *ethpb.Bli
return b
}
// HydrateBlindedBeaconBlockElectra hydrates a blinded beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateBlindedBeaconBlockElectra(b *ethpb.BlindedBeaconBlockElectra) *ethpb.BlindedBeaconBlockElectra {
if b == nil {
b = &ethpb.BlindedBeaconBlockElectra{}
}
if b.ParentRoot == nil {
b.ParentRoot = make([]byte, fieldparams.RootLength)
}
if b.StateRoot == nil {
b.StateRoot = make([]byte, fieldparams.RootLength)
}
b.Body = HydrateBlindedBeaconBlockBodyElectra(b.Body)
return b
}
// HydrateV2BlindedBeaconBlockDeneb hydrates a v2 blinded beacon block with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV2BlindedBeaconBlockDeneb(b *v2.BlindedBeaconBlockDeneb) *v2.BlindedBeaconBlockDeneb {
@@ -1367,6 +1466,50 @@ func HydrateBlindedBeaconBlockBodyDeneb(b *ethpb.BlindedBeaconBlockBodyDeneb) *e
return b
}
// HydrateBlindedBeaconBlockBodyElectra hydrates a blinded beacon block body with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateBlindedBeaconBlockBodyElectra(b *ethpb.BlindedBeaconBlockBodyElectra) *ethpb.BlindedBeaconBlockBodyElectra {
if b == nil {
b = &ethpb.BlindedBeaconBlockBodyElectra{}
}
if b.RandaoReveal == nil {
b.RandaoReveal = make([]byte, fieldparams.BLSSignatureLength)
}
if b.Graffiti == nil {
b.Graffiti = make([]byte, 32)
}
if b.Eth1Data == nil {
b.Eth1Data = &ethpb.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, 32),
}
}
if b.SyncAggregate == nil {
b.SyncAggregate = &ethpb.SyncAggregate{
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
}
}
if b.ExecutionPayloadHeader == nil {
b.ExecutionPayloadHeader = &enginev1.ExecutionPayloadHeaderElectra{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, 32),
BlockHash: make([]byte, 32),
TransactionsRoot: make([]byte, fieldparams.RootLength),
WithdrawalsRoot: make([]byte, fieldparams.RootLength),
WithdrawalRequestsRoot: make([]byte, fieldparams.RootLength),
DepositReceiptsRoot: make([]byte, fieldparams.RootLength),
}
}
return b
}
// HydrateV2BlindedBeaconBlockBodyDeneb hydrates a blinded v2 beacon block body with correct field length sizes
// to comply with fssz marshalling and unmarshalling rules.
func HydrateV2BlindedBeaconBlockBodyDeneb(b *v2.BlindedBeaconBlockBodyDeneb) *v2.BlindedBeaconBlockBodyDeneb {

View File

@@ -2,7 +2,6 @@ package util
import (
"encoding/binary"
"math"
"math/big"
"testing"
@@ -187,19 +186,3 @@ func ExtendBlocksPlusBlobs(t *testing.T, blks []blocks.ROBlock, size int) ([]blo
return blks, blobs
}
// HackDenebMaxuint is helpful for tests that need to set up cases where the deneb fork has passed.
// We have unit tests that assert our config matches the upstream config, where the next fork is always
// set to MaxUint64 until the fork epoch is formally set. This creates an issue for tests that want to
// work with slots that are defined to be after deneb because converting the max epoch to a slot leads
// to multiplication overflow.
// Monkey patching tests with this function is the simplest workaround in these cases.
func HackDenebMaxuint(t *testing.T) func() {
bc := params.MainnetConfig().Copy()
bc.DenebForkEpoch = math.MaxUint32
undo, err := params.SetActiveWithUndo(bc)
require.NoError(t, err)
return func() {
require.NoError(t, undo())
}
}

25
testing/util/electra.go Normal file
View File

@@ -0,0 +1,25 @@
package util
import (
"math"
"testing"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
// HackElectraMaxuint is helpful for tests that need to set up cases where the electra fork has passed.
// We have unit tests that assert our config matches the upstream config, where the next fork is always
// set to MaxUint64 until the fork epoch is formally set. This creates an issue for tests that want to
// work with slots that are defined to be after electra because converting the max epoch to a slot leads
// to multiplication overflow.
// Monkey patching tests with this function is the simplest workaround in these cases.
func HackElectraMaxuint(t *testing.T) func() {
bc := params.MainnetConfig().Copy()
bc.ElectraForkEpoch = math.MaxUint32
undo, err := params.SetActiveWithUndo(bc)
require.NoError(t, err)
return func() {
require.NoError(t, undo())
}
}

View File

@@ -35,6 +35,10 @@ func NewBeaconBlockDeneb() *ethpb.SignedBeaconBlockDeneb {
return HydrateSignedBeaconBlockDeneb(&ethpb.SignedBeaconBlockDeneb{})
}
func NewBeaconBlockElectra() *ethpb.SignedBeaconBlockElectra {
return HydrateSignedBeaconBlockElectra(&ethpb.SignedBeaconBlockElectra{})
}
// NewBeaconBlockContentsDeneb creates a beacon block with minimum marshalable fields.
func NewBeaconBlockContentsDeneb() *ethpb.SignedBeaconBlockContentsDeneb {
return HydrateSignedBeaconBlockContentsDeneb(&ethpb.SignedBeaconBlockContentsDeneb{})
@@ -45,6 +49,11 @@ func NewBlindedBeaconBlockDeneb() *ethpb.SignedBlindedBeaconBlockDeneb {
return HydrateSignedBlindedBeaconBlockDeneb(&ethpb.SignedBlindedBeaconBlockDeneb{})
}
// NewBlindedBeaconBlockElectra creates a blinded beacon block with minimum marshalable fields.
func NewBlindedBeaconBlockElectra() *ethpb.SignedBlindedBeaconBlockElectra {
return HydrateSignedBlindedBeaconBlockElectra(&ethpb.SignedBlindedBeaconBlockElectra{})
}
// NewBlindedBeaconBlockCapellaV2 creates a blinded beacon block with minimum marshalable fields.
func NewBlindedBeaconBlockCapellaV2() *v2.SignedBlindedBeaconBlockCapella {
return HydrateV2SignedBlindedBeaconBlockCapella(&v2.SignedBlindedBeaconBlockCapella{})

View File

@@ -386,6 +386,76 @@ func NewBeaconStateDeneb(options ...func(state *ethpb.BeaconStateDeneb) error) (
return st.Copy(), nil
}
// NewBeaconStateElectra creates a beacon state with minimum marshalable fields.
func NewBeaconStateElectra(options ...func(state *ethpb.BeaconStateElectra) error) (state.BeaconState, error) {
pubkeys := make([][]byte, 512)
for i := range pubkeys {
pubkeys[i] = make([]byte, 48)
}
seed := &ethpb.BeaconStateElectra{
BlockRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32),
StateRoots: filledByteSlice2D(uint64(params.BeaconConfig().SlotsPerHistoricalRoot), 32),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
RandaoMixes: filledByteSlice2D(uint64(params.BeaconConfig().EpochsPerHistoricalVector), 32),
Validators: make([]*ethpb.Validator, 0),
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, 32),
},
Fork: &ethpb.Fork{
PreviousVersion: make([]byte, 4),
CurrentVersion: make([]byte, 4),
},
Eth1DataVotes: make([]*ethpb.Eth1Data, 0),
HistoricalRoots: make([][]byte, 0),
JustificationBits: bitfield.Bitvector4{0x0},
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
LatestBlockHeader: HydrateBeaconHeader(&ethpb.BeaconBlockHeader{}),
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, fieldparams.RootLength)},
PreviousEpochParticipation: make([]byte, 0),
CurrentEpochParticipation: make([]byte, 0),
CurrentSyncCommittee: &ethpb.SyncCommittee{
Pubkeys: pubkeys,
AggregatePubkey: make([]byte, 48),
},
NextSyncCommittee: &ethpb.SyncCommittee{
Pubkeys: pubkeys,
AggregatePubkey: make([]byte, 48),
},
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderElectra{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32),
ReceiptsRoot: make([]byte, 32),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, 32),
BlockHash: make([]byte, 32),
TransactionsRoot: make([]byte, 32),
WithdrawalsRoot: make([]byte, 32),
DepositReceiptsRoot: make([]byte, 32),
WithdrawalRequestsRoot: make([]byte, 32),
},
}
for _, opt := range options {
err := opt(seed)
if err != nil {
return nil, err
}
}
var st, err = state_native.InitializeFromProtoUnsafeElectra(seed)
if err != nil {
return nil, err
}
return st.Copy(), nil
}
// SSZ will fill 2D byte slices with their respective values, so we must fill these in too for round
// trip testing.
func filledByteSlice2D(length, innerLen uint64) [][]byte {

View File

@@ -59,6 +59,16 @@ func TestNewBeaconStateDeneb(t *testing.T) {
assert.DeepEqual(t, st.ToProtoUnsafe(), got)
}
func TestNewBeaconStateElectra(t *testing.T) {
st, err := NewBeaconStateElectra()
require.NoError(t, err)
b, err := st.MarshalSSZ()
require.NoError(t, err)
got := &ethpb.BeaconStateElectra{}
require.NoError(t, got.UnmarshalSSZ(b))
assert.DeepEqual(t, st.ToProtoUnsafe(), got)
}
func TestNewBeaconState_HashTreeRoot(t *testing.T) {
st, err := NewBeaconState()
require.NoError(t, err)
@@ -76,4 +86,12 @@ func TestNewBeaconState_HashTreeRoot(t *testing.T) {
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
st, err = NewBeaconStateDeneb()
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
st, err = NewBeaconStateElectra()
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
}