Compare commits

...

13 Commits

Author SHA1 Message Date
Potuz
636beebd3c Merge branch 'develop' into active_validator_count_helper 2024-05-08 14:49:43 -03:00
Brandon Liu
41edee9fe9 use time.NewTimer() to avoid possible memory leaks (#13800)
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2024-05-08 17:41:47 +00:00
Potuz
e9029beaf5 Add helper to get active validator count 2024-05-08 14:34:53 -03:00
james-prysm
2fa3694746 Electra: upgrade (#13933)
* wip fork logic upgrade

* fixing replay and fork.go

* improving process function and adding tests for transition

* updating unit tests and temporarily removing some fields on state_trie.go

* updating state

* wip adding upgrade to electra code

* adding some comments

* adding spec tests

* fixing values used in state transition logic

* updating upgrade test

* gofmt

* avoid dup word linting

* fixing spec tests for fork

* gaz

* fixing tests

* improving unit test with new getters

* fixing bazel for minimal fork test

* adding bazel file

* Update beacon-chain/core/electra/upgrade.go

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>

* addressing some comments and adding more tests

* addressing more feedback

* one more feedback

* changing value to interface after talking to preston

* adding missed review feedback

* fixing linting

* noticed I was using the wrong function in the state upgrade

* fixing and ignoring some deepsource issues

* moving core electra validator functions to helper to remove circular dependencies in other PRs

* missed deepsource complaint

* Update upgrade.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/util/electra_state.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/util/electra_state.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* addressing feedback

* removing deepsoure ignore comments

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2024-05-08 15:16:31 +00:00
Nishant Das
e9606b3635 Handle Each Blob In Its Own Goroutine (#13959) 2024-05-07 22:21:27 +00:00
dependabot[bot]
ed7c4bb6a7 Bump golang.org/x/net from 0.21.0 to 0.23.0 (#13895)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 21:57:45 +00:00
Preston Van Loon
c93fea4ec4 Update spectests to v1.5.0-alpha.2 (#13961) 2024-05-07 20:56:08 +00:00
Nishant Das
aa847991e0 Update Libp2p Dependencies (#13960)
* Update Libp2p

* Update Go Sum
2024-05-07 15:10:18 +00:00
Radosław Kapka
5f1b903bdf EIP-7549 beacon spec (#13946)
* EIP-7549 beacon spec

* reviews

* change signature of AttestingIndices
2024-05-07 13:48:23 +00:00
terence
49f3531aed Remove unused validator map copy method (#13954) 2024-05-07 00:47:05 +00:00
Preston Van Loon
9b2934f1f6 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>
2024-05-06 18:04:33 +00:00
Preston Van Loon
26355768a0 Spectest: v1.5.0-alpha.1 (#13934)
* Update spectests to v1.5.0-alpha.1

* Add PeerDAS config change
2024-05-03 18:53:46 +00:00
kasey
80bff0dc2d Fork-specific consensus-types interfaces (#13948)
* fork-specific interface for electra

* add electra to wrapped payload switch

* use electra body in block factory

* deepsource

* rm pb getters from electra payload

---------

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2024-05-03 17:12:27 +00:00
146 changed files with 4952 additions and 1159 deletions

View File

@@ -227,7 +227,7 @@ filegroup(
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
)
consensus_spec_version = "v1.5.0-alpha.0"
consensus_spec_version = "v1.5.0-alpha.2"
bls_test_version = "v0.1.1"
@@ -243,7 +243,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "33c5547772b6d8d6f041dff7e7d26b0358c2392daed34394a3aa81147812a81c",
integrity = "sha256-NNXBa7SZ2sFb68HPNahgu1p0yDBpjuKJuLfRCl7vvoQ=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/general.tar.gz" % consensus_spec_version,
)
@@ -259,7 +259,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "06f286199cf2fedd4700487fb8feb0904e0ae18daaa4b3f70ea430ca9c388167",
integrity = "sha256-7BnlBvGWU92iAB100cMaAXVQhRrqpMQbavgrI+/paCw=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/minimal.tar.gz" % consensus_spec_version,
)
@@ -275,7 +275,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "5f2a4452b323075eba6bf950003f7d91fd04ebcbde5bd087beafb5d6f6325ad4",
integrity = "sha256-VCHhcNt+fynf/sHK11qbRBAy608u9T1qAafvAGfxQhA=",
url = "https://github.com/ethereum/consensus-spec-tests/releases/download/%s/mainnet.tar.gz" % consensus_spec_version,
)
@@ -290,7 +290,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "fd7e83e8cbeb3e297f2aeb93776305f7d606272c97834d8d9be673984501ed36",
integrity = "sha256-a2aCNFyFkYLtf6QSwGOHdx7xXHjA2NNT8x8ZuxB0aes=",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)

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

@@ -370,7 +370,7 @@ func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.Re
if err != nil {
return err
}
indices, err := attestation.AttestingIndices(a.GetAggregationBits(), committee)
indices, err := attestation.AttestingIndices(a, committee)
if err != nil {
return err
}

View File

@@ -70,7 +70,7 @@ func ProcessAttestationNoVerifySignature(
if err != nil {
return nil, err
}
indices, err := attestation.AttestingIndices(att.GetAggregationBits(), committee)
indices, err := attestation.AttestingIndices(att, committee)
if err != nil {
return nil, err
}

View File

@@ -215,7 +215,7 @@ func TestProcessAttestations_OK(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
require.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
sigs := make([]bls.Signature, len(attestingIndices))
for i, indice := range attestingIndices {
@@ -273,7 +273,7 @@ func TestProcessAttestationNoVerify_SourceTargetHead(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
require.NoError(t, err)
indices, err := attestation.AttestingIndices(att.AggregationBits, committee)
indices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
for _, index := range indices {
has, err := altair.HasValidatorFlag(p[index], params.BeaconConfig().TimelyHeadFlagIndex)

View File

@@ -158,7 +158,7 @@ func TranslateParticipation(ctx context.Context, state state.BeaconState, atts [
if err != nil {
return nil, err
}
indices, err := attestation.AttestingIndices(att.AggregationBits, committee)
indices, err := attestation.AttestingIndices(att, committee)
if err != nil {
return nil, err
}

View File

@@ -55,7 +55,7 @@ func TestTranslateParticipation(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(ctx, s, pendingAtts[0].Data.Slot, pendingAtts[0].Data.CommitteeIndex)
require.NoError(t, err)
indices, err := attestation.AttestingIndices(pendingAtts[0].AggregationBits, committee)
indices, err := attestation.AttestingIndices(pendingAtts[0], committee)
require.NoError(t, err)
for _, index := range indices {
has, err := altair.HasValidatorFlag(participation[index], params.BeaconConfig().TimelySourceFlagIndex)

View File

@@ -107,22 +107,53 @@ func VerifyAttestationNoVerifySignature(
return err
}
c := helpers.SlotCommitteeCount(activeValidatorCount)
if uint64(att.GetData().CommitteeIndex) >= c {
return fmt.Errorf("committee index %d >= committee count %d", att.GetData().CommitteeIndex, c)
}
if err := helpers.VerifyAttestationBitfieldLengths(ctx, beaconState, att); err != nil {
return errors.Wrap(err, "could not verify attestation bitfields")
}
var indexedAtt ethpb.IndexedAtt
// Verify attesting indices are correct.
committee, err := helpers.BeaconCommitteeFromState(ctx, beaconState, att.GetData().Slot, att.GetData().CommitteeIndex)
if err != nil {
return err
}
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
if err != nil {
return err
if att.Version() < version.Electra {
if uint64(att.GetData().CommitteeIndex) >= c {
return fmt.Errorf("committee index %d >= committee count %d", att.GetData().CommitteeIndex, c)
}
if err = helpers.VerifyAttestationBitfieldLengths(ctx, beaconState, att); err != nil {
return errors.Wrap(err, "could not verify attestation bitfields")
}
// Verify attesting indices are correct.
committee, err := helpers.BeaconCommitteeFromState(ctx, beaconState, att.GetData().Slot, att.GetData().CommitteeIndex)
if err != nil {
return err
}
indexedAtt, err = attestation.ConvertToIndexed(ctx, att, committee)
if err != nil {
return err
}
} else {
if att.GetData().CommitteeIndex != 0 {
return errors.New("committee index must be 0 post-Electra")
}
committeeIndices := att.GetCommitteeBitsVal().BitIndices()
committees := make([][]primitives.ValidatorIndex, len(committeeIndices))
participantsCount := 0
var err error
for i, ci := range committeeIndices {
if uint64(ci) >= c {
return fmt.Errorf("committee index %d >= committee count %d", ci, c)
}
committees[i], err = helpers.BeaconCommitteeFromState(ctx, beaconState, att.GetData().Slot, primitives.CommitteeIndex(ci))
if err != nil {
return err
}
participantsCount += len(committees[i])
}
if att.GetAggregationBits().Len() != uint64(participantsCount) {
return fmt.Errorf("aggregation bits count %d is different than participant count %d", att.GetAggregationBits().Len(), participantsCount)
}
indexedAtt, err = attestation.ConvertToIndexed(ctx, att, committees...)
if err != nil {
return err
}
}
return attestation.IsValidAttestationIndices(ctx, indexedAtt)

View File

@@ -45,7 +45,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att1.Data.Slot, att1.Data.CommitteeIndex)
require.NoError(t, err)
attestingIndices1, err := attestation.AttestingIndices(att1.AggregationBits, committee)
attestingIndices1, err := attestation.AttestingIndices(att1, committee)
require.NoError(t, err)
sigs := make([]bls.Signature, len(attestingIndices1))
for i, indice := range attestingIndices1 {
@@ -67,7 +67,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) {
committee, err = helpers.BeaconCommitteeFromState(context.Background(), beaconState, att2.Data.Slot, att2.Data.CommitteeIndex)
require.NoError(t, err)
attestingIndices2, err := attestation.AttestingIndices(att2.AggregationBits, committee)
attestingIndices2, err := attestation.AttestingIndices(att2, committee)
require.NoError(t, err)
sigs = make([]bls.Signature, len(attestingIndices2))
for i, indice := range attestingIndices2 {
@@ -222,6 +222,83 @@ func TestVerifyAttestationNoVerifySignature_BadAttIdx(t *testing.T) {
require.ErrorContains(t, "committee index 100 >= committee count 1", err)
}
func TestVerifyAttestationNoVerifySignature_Electra(t *testing.T) {
var mockRoot [32]byte
copy(mockRoot[:], "hello-world")
var zeroSig [fieldparams.BLSSignatureLength]byte
beaconState, _ := util.DeterministicGenesisState(t, 100)
err := beaconState.SetSlot(beaconState.Slot() + params.BeaconConfig().MinAttestationInclusionDelay)
require.NoError(t, err)
ckp := beaconState.CurrentJustifiedCheckpoint()
copy(ckp.Root, "hello-world")
require.NoError(t, beaconState.SetCurrentJustifiedCheckpoint(ckp))
require.NoError(t, beaconState.AppendCurrentEpochAttestations(&ethpb.PendingAttestation{}))
t.Run("ok", func(t *testing.T) {
aggBits := bitfield.NewBitlist(3)
aggBits.SetBitAt(1, true)
committeeBits := bitfield.NewBitvector64()
committeeBits.SetBitAt(0, true)
att := &ethpb.AttestationElectra{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
},
AggregationBits: aggBits,
CommitteeBits: committeeBits,
}
att.Signature = zeroSig[:]
err = blocks.VerifyAttestationNoVerifySignature(context.TODO(), beaconState, att)
assert.NoError(t, err)
})
t.Run("non-zero committee index", func(t *testing.T) {
att := &ethpb.AttestationElectra{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
CommitteeIndex: 1,
},
AggregationBits: bitfield.NewBitlist(1),
CommitteeBits: bitfield.NewBitvector64(),
}
err = blocks.VerifyAttestationNoVerifySignature(context.TODO(), beaconState, att)
assert.ErrorContains(t, "committee index must be 0 post-Electra", err)
})
t.Run("index of committee too big", func(t *testing.T) {
aggBits := bitfield.NewBitlist(3)
committeeBits := bitfield.NewBitvector64()
committeeBits.SetBitAt(63, true)
att := &ethpb.AttestationElectra{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
},
AggregationBits: aggBits,
CommitteeBits: committeeBits,
}
att.Signature = zeroSig[:]
err = blocks.VerifyAttestationNoVerifySignature(context.TODO(), beaconState, att)
assert.ErrorContains(t, "committee index 63 >= committee count 1", err)
})
t.Run("wrong aggregation bits count", func(t *testing.T) {
aggBits := bitfield.NewBitlist(123)
committeeBits := bitfield.NewBitvector64()
committeeBits.SetBitAt(0, true)
att := &ethpb.AttestationElectra{
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0, Root: mockRoot[:]},
Target: &ethpb.Checkpoint{Epoch: 0, Root: make([]byte, 32)},
},
AggregationBits: aggBits,
CommitteeBits: committeeBits,
}
att.Signature = zeroSig[:]
err = blocks.VerifyAttestationNoVerifySignature(context.TODO(), beaconState, att)
assert.ErrorContains(t, "aggregation bits count 123 is different than participant count 3", err)
})
}
func TestConvertToIndexed_OK(t *testing.T) {
helpers.ClearCache()
validators := make([]*ethpb.Validator, 2*params.BeaconConfig().SlotsPerEpoch)

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

@@ -0,0 +1,41 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["upgrade.go"],
importpath = "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra",
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/state-native:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["upgrade_test.go"],
deps = [
":go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
],
)

View File

@@ -0,0 +1,312 @@
package electra
import (
"sort"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
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/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// UpgradeToElectra updates inputs a generic state to return the version Electra state.
// def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
//
// epoch = deneb.get_current_epoch(pre)
// latest_execution_payload_header = ExecutionPayloadHeader(
// parent_hash=pre.latest_execution_payload_header.parent_hash,
// fee_recipient=pre.latest_execution_payload_header.fee_recipient,
// state_root=pre.latest_execution_payload_header.state_root,
// receipts_root=pre.latest_execution_payload_header.receipts_root,
// logs_bloom=pre.latest_execution_payload_header.logs_bloom,
// prev_randao=pre.latest_execution_payload_header.prev_randao,
// block_number=pre.latest_execution_payload_header.block_number,
// gas_limit=pre.latest_execution_payload_header.gas_limit,
// gas_used=pre.latest_execution_payload_header.gas_used,
// timestamp=pre.latest_execution_payload_header.timestamp,
// extra_data=pre.latest_execution_payload_header.extra_data,
// base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
// block_hash=pre.latest_execution_payload_header.block_hash,
// transactions_root=pre.latest_execution_payload_header.transactions_root,
// withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
// blob_gas_used=pre.latest_execution_payload_header.blob_gas_used,
// excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas,
// deposit_receipts_root=Root(), # [New in Electra:EIP6110]
// withdrawal_requests_root=Root(), # [New in Electra:EIP7002],
// )
//
// exit_epochs = [v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
// if not exit_epochs:
// exit_epochs = [get_current_epoch(pre)]
// earliest_exit_epoch = max(exit_epochs) + 1
//
// post = BeaconState(
// # Versioning
// genesis_time=pre.genesis_time,
// genesis_validators_root=pre.genesis_validators_root,
// slot=pre.slot,
// fork=Fork(
// previous_version=pre.fork.current_version,
// current_version=ELECTRA_FORK_VERSION, # [Modified in Electra:EIP6110]
// epoch=epoch,
// ),
// # History
// latest_block_header=pre.latest_block_header,
// block_roots=pre.block_roots,
// state_roots=pre.state_roots,
// historical_roots=pre.historical_roots,
// # Eth1
// eth1_data=pre.eth1_data,
// eth1_data_votes=pre.eth1_data_votes,
// eth1_deposit_index=pre.eth1_deposit_index,
// # Registry
// validators=pre.validators,
// balances=pre.balances,
// # Randomness
// randao_mixes=pre.randao_mixes,
// # Slashings
// slashings=pre.slashings,
// # Participation
// previous_epoch_participation=pre.previous_epoch_participation,
// current_epoch_participation=pre.current_epoch_participation,
// # Finality
// justification_bits=pre.justification_bits,
// previous_justified_checkpoint=pre.previous_justified_checkpoint,
// current_justified_checkpoint=pre.current_justified_checkpoint,
// finalized_checkpoint=pre.finalized_checkpoint,
// # Inactivity
// inactivity_scores=pre.inactivity_scores,
// # Sync
// current_sync_committee=pre.current_sync_committee,
// next_sync_committee=pre.next_sync_committee,
// # Execution-layer
// latest_execution_payload_header=latest_execution_payload_header, # [Modified in Electra:EIP6110:EIP7002]
// # Withdrawals
// next_withdrawal_index=pre.next_withdrawal_index,
// next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
// # Deep history valid from Capella onwards
// historical_summaries=pre.historical_summaries,
// # [New in Electra:EIP6110]
// deposit_receipts_start_index=UNSET_DEPOSIT_RECEIPTS_START_INDEX,
// # [New in Electra:EIP7251]
// deposit_balance_to_consume=0,
// exit_balance_to_consume=0,
// earliest_exit_epoch=earliest_exit_epoch,
// consolidation_balance_to_consume=0,
// earliest_consolidation_epoch=compute_activation_exit_epoch(get_current_epoch(pre)),
// pending_balance_deposits=[],
// pending_partial_withdrawals=[],
// pending_consolidations=[],
// )
//
// post.exit_balance_to_consume = get_activation_exit_churn_limit(post)
// post.consolidation_balance_to_consume = get_consolidation_churn_limit(post)
//
// # [New in Electra:EIP7251]
// # add validators that are not yet active to pending balance deposits
// pre_activation = sorted([
// index for index, validator in enumerate(post.validators)
// if validator.activation_epoch == FAR_FUTURE_EPOCH
// ], key=lambda index: (
// post.validators[index].activation_eligibility_epoch,
// index
// ))
//
// for index in pre_activation:
// queue_entire_balance_and_reset_validator(post, ValidatorIndex(index))
//
// # Ensure early adopters of compounding credentials go through the activation churn
// for index, validator in enumerate(post.validators):
// if has_compounding_withdrawal_credential(validator):
// queue_excess_active_balance(post, ValidatorIndex(index))
//
// return post
func UpgradeToElectra(beaconState state.BeaconState) (state.BeaconState, error) {
currentSyncCommittee, err := beaconState.CurrentSyncCommittee()
if err != nil {
return nil, err
}
nextSyncCommittee, err := beaconState.NextSyncCommittee()
if err != nil {
return nil, err
}
prevEpochParticipation, err := beaconState.PreviousEpochParticipation()
if err != nil {
return nil, err
}
currentEpochParticipation, err := beaconState.CurrentEpochParticipation()
if err != nil {
return nil, err
}
inactivityScores, err := beaconState.InactivityScores()
if err != nil {
return nil, err
}
payloadHeader, err := beaconState.LatestExecutionPayloadHeader()
if err != nil {
return nil, err
}
txRoot, err := payloadHeader.TransactionsRoot()
if err != nil {
return nil, err
}
wdRoot, err := payloadHeader.WithdrawalsRoot()
if err != nil {
return nil, err
}
wi, err := beaconState.NextWithdrawalIndex()
if err != nil {
return nil, err
}
vi, err := beaconState.NextWithdrawalValidatorIndex()
if err != nil {
return nil, err
}
summaries, err := beaconState.HistoricalSummaries()
if err != nil {
return nil, err
}
historicalRoots, err := beaconState.HistoricalRoots()
if err != nil {
return nil, err
}
excessBlobGas, err := payloadHeader.ExcessBlobGas()
if err != nil {
return nil, err
}
blobGasUsed, err := payloadHeader.BlobGasUsed()
if err != nil {
return nil, err
}
// [New in Electra:EIP7251]
earliestExitEpoch := time.CurrentEpoch(beaconState)
preActivationIndices := make([]primitives.ValidatorIndex, 0)
compoundWithdrawalIndices := make([]primitives.ValidatorIndex, 0)
if err = beaconState.ReadFromEveryValidator(func(index int, val state.ReadOnlyValidator) error {
if val.ExitEpoch() != params.BeaconConfig().FarFutureEpoch && val.ExitEpoch() > earliestExitEpoch {
earliestExitEpoch = val.ExitEpoch()
}
if val.ActivationEpoch() == params.BeaconConfig().FarFutureEpoch {
preActivationIndices = append(preActivationIndices, primitives.ValidatorIndex(index))
}
if helpers.HasCompoundingWithdrawalCredential(val) {
compoundWithdrawalIndices = append(compoundWithdrawalIndices, primitives.ValidatorIndex(index))
}
return nil
}); err != nil {
return nil, err
}
earliestExitEpoch++ // Increment to find the earliest possible exit epoch
// note: should be the same in prestate and post beaconState.
// we are deviating from the specs a bit as it calls for using the post beaconState
tab, err := helpers.TotalActiveBalance(beaconState)
if err != nil {
return nil, errors.Wrap(err, "failed to get total active balance")
}
s := &ethpb.BeaconStateElectra{
GenesisTime: beaconState.GenesisTime(),
GenesisValidatorsRoot: beaconState.GenesisValidatorsRoot(),
Slot: beaconState.Slot(),
Fork: &ethpb.Fork{
PreviousVersion: beaconState.Fork().CurrentVersion,
CurrentVersion: params.BeaconConfig().ElectraForkVersion,
Epoch: time.CurrentEpoch(beaconState),
},
LatestBlockHeader: beaconState.LatestBlockHeader(),
BlockRoots: beaconState.BlockRoots(),
StateRoots: beaconState.StateRoots(),
HistoricalRoots: historicalRoots,
Eth1Data: beaconState.Eth1Data(),
Eth1DataVotes: beaconState.Eth1DataVotes(),
Eth1DepositIndex: beaconState.Eth1DepositIndex(),
Validators: beaconState.Validators(),
Balances: beaconState.Balances(),
RandaoMixes: beaconState.RandaoMixes(),
Slashings: beaconState.Slashings(),
PreviousEpochParticipation: prevEpochParticipation,
CurrentEpochParticipation: currentEpochParticipation,
JustificationBits: beaconState.JustificationBits(),
PreviousJustifiedCheckpoint: beaconState.PreviousJustifiedCheckpoint(),
CurrentJustifiedCheckpoint: beaconState.CurrentJustifiedCheckpoint(),
FinalizedCheckpoint: beaconState.FinalizedCheckpoint(),
InactivityScores: inactivityScores,
CurrentSyncCommittee: currentSyncCommittee,
NextSyncCommittee: nextSyncCommittee,
LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderElectra{
ParentHash: payloadHeader.ParentHash(),
FeeRecipient: payloadHeader.FeeRecipient(),
StateRoot: payloadHeader.StateRoot(),
ReceiptsRoot: payloadHeader.ReceiptsRoot(),
LogsBloom: payloadHeader.LogsBloom(),
PrevRandao: payloadHeader.PrevRandao(),
BlockNumber: payloadHeader.BlockNumber(),
GasLimit: payloadHeader.GasLimit(),
GasUsed: payloadHeader.GasUsed(),
Timestamp: payloadHeader.Timestamp(),
ExtraData: payloadHeader.ExtraData(),
BaseFeePerGas: payloadHeader.BaseFeePerGas(),
BlockHash: payloadHeader.BlockHash(),
TransactionsRoot: txRoot,
WithdrawalsRoot: wdRoot,
ExcessBlobGas: excessBlobGas,
BlobGasUsed: blobGasUsed,
DepositReceiptsRoot: bytesutil.Bytes32(0), // [New in Electra:EIP6110]
WithdrawalRequestsRoot: bytesutil.Bytes32(0), // [New in Electra:EIP7002]
},
NextWithdrawalIndex: wi,
NextWithdrawalValidatorIndex: vi,
HistoricalSummaries: summaries,
DepositReceiptsStartIndex: params.BeaconConfig().UnsetDepositReceiptsStartIndex,
DepositBalanceToConsume: 0,
ExitBalanceToConsume: helpers.ActivationExitChurnLimit(math.Gwei(tab)),
EarliestExitEpoch: earliestExitEpoch,
ConsolidationBalanceToConsume: helpers.ConsolidationChurnLimit(math.Gwei(tab)),
EarliestConsolidationEpoch: helpers.ActivationExitEpoch(slots.ToEpoch(beaconState.Slot())),
PendingBalanceDeposits: make([]*ethpb.PendingBalanceDeposit, 0),
PendingPartialWithdrawals: make([]*ethpb.PendingPartialWithdrawal, 0),
PendingConsolidations: make([]*ethpb.PendingConsolidation, 0),
}
// Sorting preActivationIndices based on a custom criteria
sort.Slice(preActivationIndices, func(i, j int) bool {
// Comparing based on ActivationEligibilityEpoch and then by index if the epochs are the same
if s.Validators[preActivationIndices[i]].ActivationEligibilityEpoch == s.Validators[preActivationIndices[j]].ActivationEligibilityEpoch {
return preActivationIndices[i] < preActivationIndices[j]
}
return s.Validators[preActivationIndices[i]].ActivationEligibilityEpoch < s.Validators[preActivationIndices[j]].ActivationEligibilityEpoch
})
// need to cast the beaconState to use in helper functions
post, err := state_native.InitializeFromProtoUnsafeElectra(s)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize post electra beaconState")
}
for _, index := range preActivationIndices {
if err := helpers.QueueEntireBalanceAndResetValidator(post, index); err != nil {
return nil, errors.Wrap(err, "failed to queue entire balance and reset validator")
}
}
// Ensure early adopters of compounding credentials go through the activation churn
for _, index := range compoundWithdrawalIndices {
if err := helpers.QueueExcessActiveBalance(post, index); err != nil {
return nil, errors.Wrap(err, "failed to queue excess active balance")
}
}
return post, nil
}

View File

@@ -0,0 +1,189 @@
package electra_test
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
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"
"github.com/prysmaticlabs/prysm/v5/testing/util"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
func TestUpgradeToElectra(t *testing.T) {
st, _ := util.DeterministicGenesisStateDeneb(t, params.BeaconConfig().MaxValidatorsPerCommittee)
require.NoError(t, st.SetHistoricalRoots([][]byte{{1}}))
vals := st.Validators()
vals[0].ActivationEpoch = params.BeaconConfig().FarFutureEpoch
vals[1].WithdrawalCredentials = []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte}
require.NoError(t, st.SetValidators(vals))
bals := st.Balances()
bals[1] = params.BeaconConfig().MinActivationBalance + 1000
require.NoError(t, st.SetBalances(bals))
preForkState := st.Copy()
mSt, err := electra.UpgradeToElectra(st)
require.NoError(t, err)
require.Equal(t, preForkState.GenesisTime(), mSt.GenesisTime())
require.DeepSSZEqual(t, preForkState.GenesisValidatorsRoot(), mSt.GenesisValidatorsRoot())
require.Equal(t, preForkState.Slot(), mSt.Slot())
require.DeepSSZEqual(t, preForkState.LatestBlockHeader(), mSt.LatestBlockHeader())
require.DeepSSZEqual(t, preForkState.BlockRoots(), mSt.BlockRoots())
require.DeepSSZEqual(t, preForkState.StateRoots(), mSt.StateRoots())
require.DeepSSZEqual(t, preForkState.Validators()[2:], mSt.Validators()[2:])
require.DeepSSZEqual(t, preForkState.Balances()[2:], mSt.Balances()[2:])
require.DeepSSZEqual(t, preForkState.Eth1Data(), mSt.Eth1Data())
require.DeepSSZEqual(t, preForkState.Eth1DataVotes(), mSt.Eth1DataVotes())
require.DeepSSZEqual(t, preForkState.Eth1DepositIndex(), mSt.Eth1DepositIndex())
require.DeepSSZEqual(t, preForkState.RandaoMixes(), mSt.RandaoMixes())
require.DeepSSZEqual(t, preForkState.Slashings(), mSt.Slashings())
require.DeepSSZEqual(t, preForkState.JustificationBits(), mSt.JustificationBits())
require.DeepSSZEqual(t, preForkState.PreviousJustifiedCheckpoint(), mSt.PreviousJustifiedCheckpoint())
require.DeepSSZEqual(t, preForkState.CurrentJustifiedCheckpoint(), mSt.CurrentJustifiedCheckpoint())
require.DeepSSZEqual(t, preForkState.FinalizedCheckpoint(), mSt.FinalizedCheckpoint())
require.Equal(t, len(preForkState.Validators()), len(mSt.Validators()))
preVal, err := preForkState.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, preVal.EffectiveBalance)
preVal2, err := preForkState.ValidatorAtIndex(1)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, preVal2.EffectiveBalance)
mVal, err := mSt.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, uint64(0), mVal.EffectiveBalance)
mVal2, err := mSt.ValidatorAtIndex(1)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance, mVal2.EffectiveBalance)
numValidators := mSt.NumValidators()
p, err := mSt.PreviousEpochParticipation()
require.NoError(t, err)
require.DeepSSZEqual(t, make([]byte, numValidators), p)
p, err = mSt.CurrentEpochParticipation()
require.NoError(t, err)
require.DeepSSZEqual(t, make([]byte, numValidators), p)
s, err := mSt.InactivityScores()
require.NoError(t, err)
require.DeepSSZEqual(t, make([]uint64, numValidators), s)
hr1, err := preForkState.HistoricalRoots()
require.NoError(t, err)
hr2, err := mSt.HistoricalRoots()
require.NoError(t, err)
require.DeepEqual(t, hr1, hr2)
f := mSt.Fork()
require.DeepSSZEqual(t, &ethpb.Fork{
PreviousVersion: st.Fork().CurrentVersion,
CurrentVersion: params.BeaconConfig().ElectraForkVersion,
Epoch: time.CurrentEpoch(st),
}, f)
csc, err := mSt.CurrentSyncCommittee()
require.NoError(t, err)
psc, err := preForkState.CurrentSyncCommittee()
require.NoError(t, err)
require.DeepSSZEqual(t, psc, csc)
nsc, err := mSt.NextSyncCommittee()
require.NoError(t, err)
psc, err = preForkState.NextSyncCommittee()
require.NoError(t, err)
require.DeepSSZEqual(t, psc, nsc)
header, err := mSt.LatestExecutionPayloadHeader()
require.NoError(t, err)
protoHeader, ok := header.Proto().(*enginev1.ExecutionPayloadHeaderElectra)
require.Equal(t, true, ok)
prevHeader, err := preForkState.LatestExecutionPayloadHeader()
require.NoError(t, err)
txRoot, err := prevHeader.TransactionsRoot()
require.NoError(t, err)
wdRoot, err := prevHeader.WithdrawalsRoot()
require.NoError(t, err)
wanted := &enginev1.ExecutionPayloadHeaderElectra{
ParentHash: prevHeader.ParentHash(),
FeeRecipient: prevHeader.FeeRecipient(),
StateRoot: prevHeader.StateRoot(),
ReceiptsRoot: prevHeader.ReceiptsRoot(),
LogsBloom: prevHeader.LogsBloom(),
PrevRandao: prevHeader.PrevRandao(),
BlockNumber: prevHeader.BlockNumber(),
GasLimit: prevHeader.GasLimit(),
GasUsed: prevHeader.GasUsed(),
Timestamp: prevHeader.Timestamp(),
ExtraData: prevHeader.ExtraData(),
BaseFeePerGas: prevHeader.BaseFeePerGas(),
BlockHash: prevHeader.BlockHash(),
TransactionsRoot: txRoot,
WithdrawalsRoot: wdRoot,
DepositReceiptsRoot: bytesutil.Bytes32(0),
WithdrawalRequestsRoot: bytesutil.Bytes32(0),
}
require.DeepEqual(t, wanted, protoHeader)
nwi, err := mSt.NextWithdrawalIndex()
require.NoError(t, err)
require.Equal(t, uint64(0), nwi)
lwvi, err := mSt.NextWithdrawalValidatorIndex()
require.NoError(t, err)
require.Equal(t, primitives.ValidatorIndex(0), lwvi)
summaries, err := mSt.HistoricalSummaries()
require.NoError(t, err)
require.Equal(t, 0, len(summaries))
startIndex, err := mSt.DepositReceiptsStartIndex()
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().UnsetDepositReceiptsStartIndex, startIndex)
balance, err := mSt.DepositBalanceToConsume()
require.NoError(t, err)
require.Equal(t, math.Gwei(0), balance)
tab, err := helpers.TotalActiveBalance(mSt)
require.NoError(t, err)
ebtc, err := mSt.ExitBalanceToConsume()
require.NoError(t, err)
require.Equal(t, helpers.ActivationExitChurnLimit(math.Gwei(tab)), ebtc)
eee, err := mSt.EarliestExitEpoch()
require.NoError(t, err)
require.Equal(t, primitives.Epoch(1), eee)
cbtc, err := mSt.ConsolidationBalanceToConsume()
require.NoError(t, err)
require.Equal(t, helpers.ConsolidationChurnLimit(math.Gwei(tab)), cbtc)
earliestConsolidationEpoch, err := mSt.EarliestConsolidationEpoch()
require.NoError(t, err)
require.Equal(t, helpers.ActivationExitEpoch(slots.ToEpoch(preForkState.Slot())), earliestConsolidationEpoch)
pendingBalanceDeposits, err := mSt.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 2, len(pendingBalanceDeposits))
require.Equal(t, uint64(1000), pendingBalanceDeposits[1].Amount)
numPendingPartialWithdrawals, err := mSt.NumPendingPartialWithdrawals()
require.NoError(t, err)
require.Equal(t, uint64(0), numPendingPartialWithdrawals)
consolidations, err := mSt.PendingConsolidations()
require.NoError(t, err)
require.Equal(t, 0, len(consolidations))
}

View File

@@ -474,7 +474,7 @@ func UnslashedAttestingIndices(ctx context.Context, state state.ReadOnlyBeaconSt
if err != nil {
return nil, err
}
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
if err != nil {
return nil, err
}

View File

@@ -58,7 +58,7 @@ func ProcessAttestations(
if err != nil {
return nil, nil, err
}
indices, err := attestation.AttestingIndices(a.AggregationBits, committee)
indices, err := attestation.AttestingIndices(a, committee)
if err != nil {
return nil, nil, err
}

View File

@@ -211,7 +211,7 @@ func TestProcessAttestations(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att1.Data.Slot, att1.Data.CommitteeIndex)
require.NoError(t, err)
indices, err := attestation.AttestingIndices(att1.AggregationBits, committee)
indices, err := attestation.AttestingIndices(att1, committee)
require.NoError(t, err)
for _, i := range indices {
if !pVals[i].IsPrevEpochAttester {
@@ -220,7 +220,7 @@ func TestProcessAttestations(t *testing.T) {
}
committee, err = helpers.BeaconCommitteeFromState(context.Background(), beaconState, att2.Data.Slot, att2.Data.CommitteeIndex)
require.NoError(t, err)
indices, err = attestation.AttestingIndices(att2.AggregationBits, committee)
indices, err = attestation.AttestingIndices(att2, committee)
require.NoError(t, err)
for _, i := range indices {
assert.Equal(t, true, pVals[i].IsPrevEpochAttester, "Not a prev epoch attester")

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

@@ -295,6 +295,21 @@ func ShuffledIndices(s state.ReadOnlyBeaconState, epoch primitives.Epoch) ([]pri
return UnshuffleList(indices, seed)
}
// CommitteeIndices return beacon committee indices corresponding to bits that are set on the argument bitfield.
//
// Spec pseudocode definition:
//
// def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]:
// return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit]
func CommitteeIndices(committeeBits bitfield.Bitfield) []primitives.CommitteeIndex {
indices := committeeBits.BitIndices()
committeeIndices := make([]primitives.CommitteeIndex, len(indices))
for i, ix := range indices {
committeeIndices[i] = primitives.CommitteeIndex(uint64(ix))
}
return committeeIndices
}
// UpdateCommitteeCache gets called at the beginning of every epoch to cache the committee shuffled indices
// list with committee index and epoch number. It caches the shuffled indices for the input epoch.
func UpdateCommitteeCache(ctx context.Context, state state.ReadOnlyBeaconState, e primitives.Epoch) error {

View File

@@ -699,3 +699,12 @@ func TestPrecomputeProposerIndices_Ok(t *testing.T) {
}
assert.DeepEqual(t, wantedProposerIndices, proposerIndices, "Did not precompute proposer indices correctly")
}
func TestCommitteeIndices(t *testing.T) {
bitfield := bitfield.NewBitvector4()
bitfield.SetBitAt(0, true)
bitfield.SetBitAt(1, true)
bitfield.SetBitAt(3, true)
indices := helpers.CommitteeIndices(bitfield)
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices)
}

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

@@ -12,6 +12,7 @@ import (
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/crypto/hash"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
@@ -520,11 +521,11 @@ func isETH1WithdrawalCredential(creds []byte) bool {
// Check if ``validator`` has an 0x02 prefixed "compounding" withdrawal credential.
// """
// return is_compounding_withdrawal_credential(validator.withdrawal_credentials)
func HasCompoundingWithdrawalCredential(v *ethpb.Validator) bool {
func HasCompoundingWithdrawalCredential(v interfaces.WithWithdrawalCredentials) bool {
if v == nil {
return false
}
return isCompoundingWithdrawalCredential(v.WithdrawalCredentials)
return isCompoundingWithdrawalCredential(v.GetWithdrawalCredentials())
}
// isCompoundingWithdrawalCredential checks if the credentials are a compounding withdrawal credential.
@@ -673,3 +674,68 @@ func ValidatorMaxEffectiveBalance(val *ethpb.Validator) uint64 {
}
return params.BeaconConfig().MinActivationBalance
}
// QueueExcessActiveBalance queues validators with balances above the min activation balance and adds to pending balance deposit.
//
// Spec definition:
//
// def queue_excess_active_balance(state: BeaconState, index: ValidatorIndex) -> None:
// balance = state.balances[index]
// if balance > MIN_ACTIVATION_BALANCE:
// excess_balance = balance - MIN_ACTIVATION_BALANCE
// state.balances[index] = MIN_ACTIVATION_BALANCE
// state.pending_balance_deposits.append(
// PendingBalanceDeposit(index=index, amount=excess_balance)
// )
func QueueExcessActiveBalance(s state.BeaconState, idx primitives.ValidatorIndex) error {
bal, err := s.BalanceAtIndex(idx)
if err != nil {
return err
}
if bal > params.BeaconConfig().MinActivationBalance {
excessBalance := bal - params.BeaconConfig().MinActivationBalance
if err := s.UpdateBalancesAtIndex(idx, params.BeaconConfig().MinActivationBalance); err != nil {
return err
}
return s.AppendPendingBalanceDeposit(idx, excessBalance)
}
return nil
}
// QueueEntireBalanceAndResetValidator queues the entire balance and resets the validator. This is used in electra fork logic.
//
// Spec definition:
//
// def queue_entire_balance_and_reset_validator(state: BeaconState, index: ValidatorIndex) -> None:
// balance = state.balances[index]
// validator = state.validators[index]
// state.balances[index] = 0
// validator.effective_balance = 0
// validator.activation_eligibility_epoch = FAR_FUTURE_EPOCH
// state.pending_balance_deposits.append(
// PendingBalanceDeposit(index=index, amount=balance)
// )
func QueueEntireBalanceAndResetValidator(s state.BeaconState, idx primitives.ValidatorIndex) error {
bal, err := s.BalanceAtIndex(idx)
if err != nil {
return err
}
if err := s.UpdateBalancesAtIndex(idx, 0); err != nil {
return err
}
v, err := s.ValidatorAtIndex(idx)
if err != nil {
return err
}
v.EffectiveBalance = 0
v.ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
if err := s.UpdateValidatorAtIndex(idx, v); err != nil {
return err
}
return s.AppendPendingBalanceDeposit(idx, bal)
}

View File

@@ -18,6 +18,7 @@ import (
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/assert"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestIsActiveValidator_OK(t *testing.T) {
@@ -1119,3 +1120,40 @@ func TestValidatorMaxEffectiveBalance(t *testing.T) {
// Sanity check that MinActivationBalance equals (pre-electra) MaxEffectiveBalance
assert.Equal(t, params.BeaconConfig().MinActivationBalance, params.BeaconConfig().MaxEffectiveBalance)
}
func TestQueueExcessActiveBalance_Ok(t *testing.T) {
st, _ := util.DeterministicGenesisStateElectra(t, params.BeaconConfig().MaxValidatorsPerCommittee)
bals := st.Balances()
bals[0] = params.BeaconConfig().MinActivationBalance + 1000
require.NoError(t, st.SetBalances(bals))
err := helpers.QueueExcessActiveBalance(st, 0)
require.NoError(t, err)
pbd, err := st.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, uint64(1000), pbd[0].Amount)
bals = st.Balances()
require.Equal(t, params.BeaconConfig().MinActivationBalance, bals[0])
}
func TestQueueEntireBalanceAndResetValidator_Ok(t *testing.T) {
st, _ := util.DeterministicGenesisStateElectra(t, params.BeaconConfig().MaxValidatorsPerCommittee)
val, err := st.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, val.EffectiveBalance)
pbd, err := st.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 0, len(pbd))
err = helpers.QueueEntireBalanceAndResetValidator(st, 0)
require.NoError(t, err)
pbd, err = st.PendingBalanceDeposits()
require.NoError(t, err)
require.Equal(t, 1, len(pbd))
val, err = st.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, uint64(0), val.EffectiveBalance)
}

View File

@@ -90,6 +90,15 @@ func CanUpgradeToDeneb(slot primitives.Slot) bool {
return epochStart && DenebEpoch
}
// CanUpgradeToElectra returns true if the input `slot` can upgrade to Electra.
// Spec code:
// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == ELECTRA_FORK_EPOCH
func CanUpgradeToElectra(slot primitives.Slot) bool {
epochStart := slots.IsEpochStart(slot)
electraEpoch := slots.ToEpoch(slot) == params.BeaconConfig().ElectraForkEpoch
return epochStart && electraEpoch
}
// CanProcessEpoch checks the eligibility to process epoch.
// The epoch can be processed at the end of the last slot of every epoch.
//

View File

@@ -333,3 +333,38 @@ func TestCanUpgradeToDeneb(t *testing.T) {
})
}
}
func TestCanUpgradeToElectra(t *testing.T) {
params.SetupTestConfigCleanup(t)
bc := params.BeaconConfig()
bc.ElectraForkEpoch = 5
params.OverrideBeaconConfig(bc)
tests := []struct {
name string
slot primitives.Slot
want bool
}{
{
name: "not epoch start",
slot: 1,
want: false,
},
{
name: "not electra epoch",
slot: params.BeaconConfig().SlotsPerEpoch,
want: false,
},
{
name: "electra epoch",
slot: primitives.Slot(params.BeaconConfig().ElectraForkEpoch) * params.BeaconConfig().SlotsPerEpoch,
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := time.CanUpgradeToElectra(tt.slot); got != tt.want {
t.Errorf("CanUpgradeToElectra() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -19,6 +19,7 @@ go_library(
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/capella:go_default_library",
"//beacon-chain/core/deneb:go_default_library",
"//beacon-chain/core/electra:go_default_library",
"//beacon-chain/core/epoch:go_default_library",
"//beacon-chain/core/epoch/precompute:go_default_library",
"//beacon-chain/core/execution:go_default_library",

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/capella"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/deneb"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
e "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/execution"
@@ -320,6 +321,14 @@ func UpgradeState(ctx context.Context, state state.BeaconState) (state.BeaconSta
return nil, err
}
}
if time.CanUpgradeToElectra(state.Slot()) {
state, err = electra.UpgradeToElectra(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
return state, nil
}

View File

@@ -311,7 +311,7 @@ func createFullBlockWithOperations(t *testing.T) (state.BeaconState,
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, blockAtt.Data.Slot, blockAtt.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(blockAtt.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(blockAtt, committee)
require.NoError(t, err)
assert.NoError(t, err)
hashTreeRoot, err = signing.ComputeSigningRoot(blockAtt.Data, domain)
@@ -651,6 +651,20 @@ func TestProcessSlots_ThroughDenebEpoch(t *testing.T) {
require.Equal(t, params.BeaconConfig().SlotsPerEpoch*10, st.Slot())
}
func TestProcessSlots_ThroughElectraEpoch(t *testing.T) {
transition.SkipSlotCache.Disable()
params.SetupTestConfigCleanup(t)
conf := params.BeaconConfig()
conf.ElectraForkEpoch = 5
params.OverrideBeaconConfig(conf)
st, _ := util.DeterministicGenesisStateDeneb(t, params.BeaconConfig().MaxValidatorsPerCommittee)
st, err := transition.ProcessSlots(context.Background(), st, params.BeaconConfig().SlotsPerEpoch*10)
require.NoError(t, err)
require.Equal(t, version.Electra, st.Version())
require.Equal(t, params.BeaconConfig().SlotsPerEpoch*10, st.Slot())
}
func TestProcessSlotsUsingNextSlotCache(t *testing.T) {
s, _ := util.DeterministicGenesisState(t, 1)
r := []byte{'a'}

View File

@@ -38,7 +38,7 @@ func attestingIndices(ctx context.Context, state state.BeaconState, att interfac
if err != nil {
return nil, err
}
return attestation.AttestingIndices(att.GetAggregationBits(), committee)
return attestation.AttestingIndices(att, committee)
}
// logMessageTimelyFlagsForIndex returns the log message with performance info for the attestation (head, source, target)

View File

@@ -404,7 +404,7 @@ func valContainerFromReadOnlyVal(
Status: valStatus.String(),
Validator: &structs.Validator{
Pubkey: hexutil.Encode(pubkey[:]),
WithdrawalCredentials: hexutil.Encode(val.WithdrawalCredentials()),
WithdrawalCredentials: hexutil.Encode(val.GetWithdrawalCredentials()),
EffectiveBalance: strconv.FormatUint(val.EffectiveBalance(), 10),
Slashed: val.Slashed(),
ActivationEligibilityEpoch: strconv.FormatUint(uint64(val.ActivationEligibilityEpoch()), 10),

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
@@ -148,6 +148,8 @@ func TestGetSpec(t *testing.T) {
config.MaxAttesterSlashingsElectra = 88
config.MaxAttestationsElectra = 89
config.MaxWithdrawalRequestsPerPayload = 90
config.MaxCellsInExtendedMatrix = 91
config.UnsetDepositReceiptsStartIndex = 92
var dbp [4]byte
copy(dbp[:], []byte{'0', '0', '0', '1'})
@@ -190,7 +192,7 @@ func TestGetSpec(t *testing.T) {
data, ok := resp.Data.(map[string]interface{})
require.Equal(t, true, ok)
assert.Equal(t, 152, len(data))
assert.Equal(t, 154, len(data))
for k, v := range data {
t.Run(k, func(t *testing.T) {
switch k {
@@ -521,6 +523,10 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "89", v)
case "MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD":
assert.Equal(t, "90", v)
case "MAX_CELLS_IN_EXTENDED_MATRIX":
assert.Equal(t, "91", v)
case "UNSET_DEPOSIT_RECEIPTS_START_INDEX":
assert.Equal(t, "92", v)
default:
t.Errorf("Incorrect key: %s", k)
}

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

@@ -79,7 +79,7 @@ func (ds *Server) GetInclusionSlot(ctx context.Context, req *pbrpc.InclusionSlot
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get committee: %v", err)
}
indices, err := attestation.AttestingIndices(a.GetAggregationBits(), c)
indices, err := attestation.AttestingIndices(a, c)
if err != nil {
return nil, err
}

View File

@@ -227,7 +227,7 @@ func generateAtt(state state.ReadOnlyBeaconState, index uint64, privKeys []bls.S
if err != nil {
return nil, err
}
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
if err != nil {
return nil, err
}
@@ -266,7 +266,7 @@ func generateUnaggregatedAtt(state state.ReadOnlyBeaconState, index uint64, priv
if err != nil {
return nil, err
}
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
if err != nil {
return nil, err
}

View File

@@ -30,6 +30,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -368,25 +369,32 @@ func (vs *Server) broadcastReceiveBlock(ctx context.Context, block interfaces.Si
// broadcastAndReceiveBlobs handles the broadcasting and reception of blob sidecars.
func (vs *Server) broadcastAndReceiveBlobs(ctx context.Context, sidecars []*ethpb.BlobSidecar, root [32]byte) error {
eg, eCtx := errgroup.WithContext(ctx)
for i, sc := range sidecars {
if err := vs.P2P.BroadcastBlob(ctx, uint64(i), sc); err != nil {
return errors.Wrap(err, "broadcast blob failed")
}
readOnlySc, err := blocks.NewROBlobWithRoot(sc, root)
if err != nil {
return errors.Wrap(err, "ROBlob creation failed")
}
verifiedBlob := blocks.NewVerifiedROBlob(readOnlySc)
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedBlob); err != nil {
return errors.Wrap(err, "receive blob failed")
}
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BlobSidecarReceived,
Data: &operation.BlobSidecarReceivedData{Blob: &verifiedBlob},
// Copy the iteration instance to a local variable to give each go-routine its own copy to play with.
// See https://golang.org/doc/faq#closures_and_goroutines for more details.
subIdx := i
sCar := sc
eg.Go(func() error {
if err := vs.P2P.BroadcastBlob(eCtx, uint64(subIdx), sCar); err != nil {
return errors.Wrap(err, "broadcast blob failed")
}
readOnlySc, err := blocks.NewROBlobWithRoot(sCar, root)
if err != nil {
return errors.Wrap(err, "ROBlob creation failed")
}
verifiedBlob := blocks.NewVerifiedROBlob(readOnlySc)
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedBlob); err != nil {
return errors.Wrap(err, "receive blob failed")
}
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BlobSidecarReceived,
Data: &operation.BlobSidecarReceivedData{Blob: &verifiedBlob},
})
return nil
})
}
return nil
return eg.Wait()
}
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.

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

@@ -2440,7 +2440,7 @@ func TestProposer_FilterAttestation(t *testing.T) {
})
committee, err := helpers.BeaconCommitteeFromState(context.Background(), st, atts[i].GetData().Slot, atts[i].GetData().CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(atts[i].GetAggregationBits(), committee)
attestingIndices, err := attestation.AttestingIndices(atts[i], committee)
require.NoError(t, err)
assert.NoError(t, err)
domain, err := signing.Domain(st.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, params.BeaconConfig().ZeroHash[:])

View File

@@ -99,10 +99,15 @@ func (vs *Server) WaitForActivation(req *ethpb.ValidatorActivationRequest, strea
return status.Errorf(codes.Internal, "Could not send response over stream: %v", err)
}
waitTime := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
timer := time.NewTimer(waitTime)
defer timer.Stop()
for {
timer.Reset(waitTime)
select {
// Pinging every slot for activation.
case <-time.After(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second):
case <-timer.C:
activeValidatorExists, validatorStatuses, err := vs.activationStatus(stream.Context(), req.PublicKeys)
if err != nil {
return status.Errorf(codes.Internal, "Could not fetch validator status: %v", 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"
)
@@ -48,6 +49,7 @@ type ReadOnlyBeaconState interface {
ReadOnlyStateRoots
ReadOnlyRandaoMixes
ReadOnlyEth1Data
ReadOnlyExits
ReadOnlyValidators
ReadOnlyBalances
ReadOnlyCheckpoint
@@ -56,6 +58,8 @@ type ReadOnlyBeaconState interface {
ReadOnlyParticipation
ReadOnlyInactivity
ReadOnlySyncCommittee
ReadOnlyDeposits
ReadOnlyConsolidations
ToProtoUnsafe() interface{}
ToProto() interface{}
GenesisTime() uint64
@@ -87,6 +91,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 +105,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()
}
@@ -111,7 +116,7 @@ type ReadOnlyValidator interface {
WithdrawableEpoch() primitives.Epoch
ExitEpoch() primitives.Epoch
PublicKey() [fieldparams.BLSPubkeyLength]byte
WithdrawalCredentials() []byte
GetWithdrawalCredentials() []byte
Slashed() bool
IsNil() bool
}
@@ -126,6 +131,7 @@ type ReadOnlyValidators interface {
PublicKeys() ([][fieldparams.BLSPubkeyLength]byte, error)
PubkeyAtIndex(idx primitives.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte
NumValidators() int
NumActiveValidators() int
ReadFromEveryValidator(f func(idx int, val ReadOnlyValidator) error) error
}
@@ -134,6 +140,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.
@@ -174,6 +181,12 @@ type ReadOnlyEth1Data interface {
Eth1DepositIndex() uint64
}
// ReadOnlyExits defines a struct which only has read access to Exit related methods.
type ReadOnlyExits interface {
ExitBalanceToConsume() (math.Gwei, error)
EarliestExitEpoch() (primitives.Epoch, error)
}
// ReadOnlyAttestations defines a struct which only has read access to attestations methods.
type ReadOnlyAttestations interface {
PreviousEpochAttestations() ([]*ethpb.PendingAttestation, error)
@@ -182,9 +195,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 +219,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 +250,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 +312,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,12 +3,18 @@ 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_exit.go",
"getters_misc.go",
"getters_participation.go",
"getters_payload_header.go",
@@ -22,8 +28,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 +48,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 +90,12 @@ 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_exit_test.go",
"getters_participation_test.go",
"getters_test.go",
"getters_validator_test.go",
@@ -94,9 +106,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 +122,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 +136,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 +151,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

@@ -0,0 +1,29 @@
package state_native
import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)
// ExitBalanceToConsume is used for returning the ExitBalanceToConsume as part of eip 7251
func (b *BeaconState) ExitBalanceToConsume() (math.Gwei, error) {
if b.version < version.Electra {
return 0, errNotSupported("ExitBalanceToConsume", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.exitBalanceToConsume, nil
}
// EarliestExitEpoch is used for returning the EarliestExitEpoch as part of eip 7251
func (b *BeaconState) EarliestExitEpoch() (primitives.Epoch, error) {
if b.version < version.Electra {
return 0, errNotSupported("EarliestExitEpoch", b.version)
}
b.lock.RLock()
defer b.lock.RUnlock()
return b.earliestExitEpoch, nil
}

View File

@@ -0,0 +1,44 @@
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"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestExitBalanceToConsume(t *testing.T) {
t.Run("previous fork returns expected error", func(t *testing.T) {
dState, _ := util.DeterministicGenesisState(t, 1)
_, err := dState.ExitBalanceToConsume()
require.ErrorContains(t, "is not supported", err)
})
t.Run("electra returns expected value", func(t *testing.T) {
want := math.Gwei(2)
dState, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{ExitBalanceToConsume: want})
require.NoError(t, err)
got, err := dState.ExitBalanceToConsume()
require.NoError(t, err)
require.Equal(t, want, got)
})
}
func TestEarliestExitEpoch(t *testing.T) {
t.Run("previous fork returns expected error", func(t *testing.T) {
dState, _ := util.DeterministicGenesisState(t, 1)
_, err := dState.EarliestExitEpoch()
require.ErrorContains(t, "is not supported", err)
})
t.Run("electra returns expected value", func(t *testing.T) {
want := primitives.Epoch(2)
dState, err := state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{EarliestExitEpoch: want})
require.NoError(t, err)
got, err := dState.EarliestExitEpoch()
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"
@@ -11,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// Validators participating in consensus on the beacon chain.
@@ -251,6 +253,21 @@ func (b *BeaconState) NumValidators() int {
return b.validatorsLen()
}
// NumActiveValidators returns the size of the active validators in the
// registry
func (b *BeaconState) NumActiveValidators() int {
epoch := slots.ToEpoch(b.Slot())
total := 0
err := b.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error {
if helpers.IsActiveValidatorUsingTrie(val, epoch) {
total++
}
return nil
})
_ = err
return total
}
// ReadFromEveryValidator reads values from every validator and applies it to the provided function.
//
// WARNING: This method is potentially unsafe, as it exposes the actual validator registry.
@@ -413,3 +430,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,15 +1,20 @@
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"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "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 TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice_Phase0(t *testing.T) {
@@ -66,3 +71,114 @@ func TestValidatorIndexes(t *testing.T) {
require.Equal(t, hexutil.Encode(readOnlyBytes[:]), hexutil.Encode(byteValue[:]))
})
}
func TestNumActiveValidators(t *testing.T) {
currentSlot := primitives.Slot(5 * 32)
currentEpoch := slots.ToEpoch(currentSlot)
vals := []*ethpb.Validator{
{
ActivationEpoch: 0,
ExitEpoch: currentEpoch + 1,
},
{
ActivationEpoch: currentEpoch + 1,
ExitEpoch: currentEpoch + 2,
},
{
ActivationEpoch: 0,
ExitEpoch: currentEpoch - 1,
},
}
pb := &ethpb.BeaconStateElectra{
Slot: currentSlot,
Validators: vals,
}
s, err := statenative.InitializeFromProtoElectra(pb)
require.NoError(t, err)
require.Equal(t, 1, s.NumActiveValidators())
pb.Validators = append(pb.Validators, &ethpb.Validator{
ActivationEpoch: currentEpoch - 1,
ExitEpoch: currentEpoch + 1,
})
s, err = statenative.InitializeFromProtoElectra(pb)
require.NoError(t, err)
require.Equal(t, 2, s.NumActiveValidators())
}
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

@@ -77,7 +77,7 @@ func (v readOnlyValidator) publicKeySlice() []byte {
// WithdrawalCredentials returns the withdrawal credentials of the
// read only validator.
func (v readOnlyValidator) WithdrawalCredentials() []byte {
func (v readOnlyValidator) GetWithdrawalCredentials() []byte {
creds := make([]byte, len(v.validator.WithdrawalCredentials))
copy(creds, v.validator.WithdrawalCredentials)
return creds

View File

@@ -63,7 +63,7 @@ func TestReadOnlyValidator_WithdrawalCredentials(t *testing.T) {
creds := []byte{0xFA, 0xCC}
v, err := statenative.NewValidator(&ethpb.Validator{WithdrawalCredentials: creds})
require.NoError(t, err)
assert.DeepEqual(t, creds, v.WithdrawalCredentials())
assert.DeepEqual(t, creds, v.GetWithdrawalCredentials())
}
func TestReadOnlyValidator_Slashed(t *testing.T) {

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

@@ -20,34 +20,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/testing/require"
)
func TestValidatorMap_DistinctCopy(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
for i := uint64(1); i < count; i++ {
var someRoot [32]byte
var someKey [fieldparams.BLSPubkeyLength]byte
copy(someRoot[:], strconv.Itoa(int(i)))
copy(someKey[:], strconv.Itoa(int(i)))
vals = append(vals, &ethpb.Validator{
PublicKey: someKey[:],
WithdrawalCredentials: someRoot[:],
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 1,
ActivationEpoch: 1,
ExitEpoch: 1,
WithdrawableEpoch: 1,
})
}
handler := stateutil.NewValMapHandler(vals)
newHandler := handler.Copy()
wantedPubkey := strconv.Itoa(22)
handler.Set(bytesutil.ToBytes48([]byte(wantedPubkey)), 27)
val1, _ := handler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
val2, _ := newHandler.Get(bytesutil.ToBytes48([]byte(wantedPubkey)))
assert.NotEqual(t, val1, val2, "Values are supposed to be unequal due to copy")
}
func TestBeaconState_NoDeadlock_Phase0(t *testing.T) {
count := uint64(100)
vals := make([]*ethpb.Validator, 0, count)
@@ -502,3 +474,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

@@ -23,6 +23,7 @@ go_library(
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/capella:go_default_library",
"//beacon-chain/core/deneb:go_default_library",
"//beacon-chain/core/electra:go_default_library",
"//beacon-chain/core/execution:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/time:go_default_library",

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/capella"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/deneb"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/execution"
prysmtime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
@@ -26,7 +27,7 @@ import (
// ReplayBlocks replays the input blocks on the input state until the target slot is reached.
//
// WARNING Blocks passed to the function must be in decreasing slots order.
func (_ *State) replayBlocks(
func (*State) replayBlocks(
ctx context.Context,
state state.BeaconState,
signed []interfaces.ReadOnlySignedBeaconBlock,
@@ -201,21 +202,18 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
return nil, errors.Wrap(err, "could not process slot")
}
if prysmtime.CanProcessEpoch(state) {
switch state.Version() {
case version.Phase0:
if state.Version() == version.Phase0 {
state, err = transition.ProcessEpochPrecompute(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
case version.Altair, version.Bellatrix, version.Capella, version.Deneb:
} else {
state, err = altair.ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch")
}
default:
return nil, fmt.Errorf("unsupported beacon state version: %s", version.String(state.Version()))
}
}
if err := state.SetSlot(state.Slot() + 1); err != nil {
@@ -254,6 +252,14 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot primi
return nil, err
}
}
if prysmtime.CanUpgradeToElectra(state.Slot()) {
state, err = electra.UpgradeToElectra(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
return state, nil

View File

@@ -140,7 +140,7 @@ func TestReplayBlocks_ThroughForkBoundary(t *testing.T) {
assert.Equal(t, version.Altair, newState.Version())
}
func TestReplayBlocks_ThroughCapellaForkBoundary(t *testing.T) {
func TestReplayBlocks_ThroughFutureForkBoundaries(t *testing.T) {
params.SetupTestConfigCleanup(t)
bCfg := params.BeaconConfig().Copy()
bCfg.AltairForkEpoch = 1
@@ -149,6 +149,10 @@ func TestReplayBlocks_ThroughCapellaForkBoundary(t *testing.T) {
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.BellatrixForkVersion)] = 2
bCfg.CapellaForkEpoch = 3
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.CapellaForkVersion)] = 3
bCfg.DenebForkEpoch = 4
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.DenebForkVersion)] = 4
bCfg.ElectraForkEpoch = 5
bCfg.ForkVersionSchedule[bytesutil.ToBytes4(bCfg.ElectraForkVersion)] = 5
params.OverrideBeaconConfig(bCfg)
beaconState, _ := util.DeterministicGenesisState(t, 32)
@@ -177,6 +181,20 @@ func TestReplayBlocks_ThroughCapellaForkBoundary(t *testing.T) {
// Verify state is version Capella.
assert.Equal(t, version.Capella, newState.Version())
targetSlot = params.BeaconConfig().SlotsPerEpoch * 4
newState, err = service.replayBlocks(context.Background(), newState, []interfaces.ReadOnlySignedBeaconBlock{}, targetSlot)
require.NoError(t, err)
// Verify state is version Deneb.
assert.Equal(t, version.Deneb, newState.Version())
targetSlot = params.BeaconConfig().SlotsPerEpoch * 5
newState, err = service.replayBlocks(context.Background(), newState, []interfaces.ReadOnlySignedBeaconBlock{}, targetSlot)
require.NoError(t, err)
// Verify state is version Electra.
assert.Equal(t, version.Electra, newState.Version())
}
func TestLoadBlocks_FirstBranch(t *testing.T) {

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

@@ -36,24 +36,6 @@ func (v *ValidatorMapHandler) IsNil() bool {
return v.mapRef == nil || v.valIdxMap == nil
}
// Copy the whole map and returns a map handler with the copied map.
func (v *ValidatorMapHandler) Copy() *ValidatorMapHandler {
if v == nil || v.valIdxMap == nil {
return &ValidatorMapHandler{valIdxMap: map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex{}, mapRef: new(Reference), RWMutex: new(sync.RWMutex)}
}
v.RLock()
defer v.RUnlock()
m := make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex, len(v.valIdxMap))
for k, v := range v.valIdxMap {
m[k] = v
}
return &ValidatorMapHandler{
valIdxMap: m,
mapRef: &Reference{refs: 1},
RWMutex: new(sync.RWMutex),
}
}
// Get the validator index using the corresponding public key.
func (v *ValidatorMapHandler) Get(key [fieldparams.BLSPubkeyLength]byte) (primitives.ValidatorIndex, bool) {
v.RLock()

View File

@@ -82,7 +82,7 @@ func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
require.NoError(t, err)
@@ -205,7 +205,7 @@ func TestProcessPendingAtts_NoBroadcastWithBadSignature(t *testing.T) {
}
committee, err := helpers.BeaconCommitteeFromState(context.Background(), s, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
attesterDomain, err := signing.Domain(s.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, s.GenesisValidatorsRoot())
require.NoError(t, err)
@@ -285,7 +285,7 @@ func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
require.NoError(t, err)

View File

@@ -50,7 +50,7 @@ func TestVerifyIndexInCommittee_CanVerify(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), s, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
indices, err := attestation.AttestingIndices(att.AggregationBits, committee)
indices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
result, err := service.validateIndexInCommittee(ctx, s, att, primitives.ValidatorIndex(indices[0]))
require.NoError(t, err)
@@ -354,7 +354,7 @@ func TestValidateAggregateAndProof_CanValidate(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
assert.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
@@ -458,7 +458,7 @@ func TestVerifyIndexInCommittee_SeenAggregatorEpoch(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
require.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
require.NoError(t, err)
@@ -577,7 +577,7 @@ func TestValidateAggregateAndProof_BadBlock(t *testing.T) {
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
assert.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())
@@ -668,7 +668,7 @@ func TestValidateAggregateAndProof_RejectWhenAttEpochDoesntEqualTargetEpoch(t *t
committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att.Data.Slot, att.Data.CommitteeIndex)
assert.NoError(t, err)
attestingIndices, err := attestation.AttestingIndices(att.AggregationBits, committee)
attestingIndices, err := attestation.AttestingIndices(att, committee)
require.NoError(t, err)
assert.NoError(t, err)
attesterDomain, err := signing.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorsRoot())

View File

@@ -236,20 +236,21 @@ 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.
UnsetDepositReceiptsStartIndex uint64 `yaml:"UNSET_DEPOSIT_RECEIPTS_START_INDEX" spec:"true"` // UnsetDepositReceiptsStartIndex is used to check the start index for eip6110
// Networking Specific Parameters
GossipMaxSize uint64 `yaml:"GOSSIP_MAX_SIZE" spec:"true"` // GossipMaxSize is the maximum allowed size of uncompressed gossip messages.
@@ -270,7 +271,8 @@ type BeaconChainConfig struct {
NodeIdBits uint64 `yaml:"NODE_ID_BITS" spec:"true"` // NodeIdBits defines the bit length of a node id.
// PeerDAS
NumberOfColumns uint64 `yaml:"NUMBER_OF_COLUMNS" spec:"true"` // NumberOfColumns in the extended data matrix.
NumberOfColumns uint64 `yaml:"NUMBER_OF_COLUMNS" spec:"true"` // NumberOfColumns in the extended data matrix.
MaxCellsInExtendedMatrix uint64 `yaml:"MAX_CELLS_IN_EXTENDED_MATRIX" spec:"true"` // MaxCellsInExtendedMatrix is the full data of one-dimensional erasure coding extended blobs (in row major format).
}
// InitializeForkSchedule initializes the schedules forks baked into the config.
@@ -287,6 +289,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
}
@@ -308,6 +311,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),
@@ -230,6 +232,8 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
fmt.Sprintf("MESSAGE_DOMAIN_INVALID_SNAPPY: %#x", cfg.MessageDomainInvalidSnappy),
fmt.Sprintf("MESSAGE_DOMAIN_VALID_SNAPPY: %#x", cfg.MessageDomainValidSnappy),
fmt.Sprintf("MIN_EPOCHS_FOR_BLOCK_REQUESTS: %d", int(cfg.MinEpochsForBlockRequests)),
fmt.Sprintf("ELECTRA_FORK_EPOCH: %d", cfg.ElectraForkEpoch),
fmt.Sprintf("ELECTRA_FORK_VERSION: %#x", cfg.ElectraForkVersion),
}
yamlFile := []byte(strings.Join(lines, "\n"))

View File

@@ -147,12 +147,14 @@ func assertEqualConfigs(t *testing.T, name string, fields []string, expected, ac
assert.Equal(t, expected.BellatrixForkEpoch, actual.BellatrixForkEpoch, "%s: BellatrixForkEpoch", name)
assert.Equal(t, expected.CapellaForkEpoch, actual.CapellaForkEpoch, "%s: CapellaForkEpoch", name)
assert.Equal(t, expected.DenebForkEpoch, actual.DenebForkEpoch, "%s: DenebForkEpoch", name)
assert.Equal(t, expected.ElectraForkEpoch, actual.ElectraForkEpoch, "%s: ElectraForkEpoch", name)
assert.Equal(t, expected.SqrRootSlotsPerEpoch, actual.SqrRootSlotsPerEpoch, "%s: SqrRootSlotsPerEpoch", name)
assert.DeepEqual(t, expected.GenesisForkVersion, actual.GenesisForkVersion, "%s: GenesisForkVersion", name)
assert.DeepEqual(t, expected.AltairForkVersion, actual.AltairForkVersion, "%s: AltairForkVersion", name)
assert.DeepEqual(t, expected.BellatrixForkVersion, actual.BellatrixForkVersion, "%s: BellatrixForkVersion", name)
assert.DeepEqual(t, expected.CapellaForkVersion, actual.CapellaForkVersion, "%s: CapellaForkVersion", name)
assert.DeepEqual(t, expected.DenebForkVersion, actual.DenebForkVersion, "%s: DenebForkVersion", name)
assert.DeepEqual(t, expected.ElectraForkVersion, actual.ElectraForkVersion, "%s: ElectraForkVersion", name)
assertYamlFieldsMatch(t, name, fields, expected, actual)
}

View File

@@ -275,21 +275,22 @@ 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,
UnsetDepositReceiptsStartIndex: math.MaxUint64,
// Values related to networking parameters.
GossipMaxSize: 10 * 1 << 20, // 10 MiB
@@ -310,7 +311,8 @@ var mainnetBeaconConfig = &BeaconChainConfig{
NodeIdBits: 256,
// PeerDAS
NumberOfColumns: 128,
NumberOfColumns: 128,
MaxCellsInExtendedMatrix: 768,
}
// MainnetTestConfig provides a version of the mainnet config that has a different name
@@ -331,16 +333,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

Some files were not shown because too many files have changed in this diff Show More