Skip to content

Make the multi-value slice permanent #15414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions beacon-chain/blockchain/defragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package blockchain

import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/features"
"github.com/OffchainLabs/prysm/v6/time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
Expand All @@ -17,9 +16,6 @@ var stateDefragmentationTime = promauto.NewSummary(prometheus.SummaryOpts{
// a higher number of fragmented indexes are reallocated to a new separate slice for
// that field.
func (s *Service) defragmentState(st state.BeaconState) {
if !features.Get().EnableExperimentalState {
return
}
startTime := time.Now()
st.Defragment()
elapsedTime := time.Since(startTime)
Expand Down
10 changes: 0 additions & 10 deletions beacon-chain/blockchain/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,6 @@ import (
"github.com/OffchainLabs/prysm/v6/testing/util"
)

func TestReportEpochMetrics_BadHeadState(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://github.com/OffchainLabs/prysm/pull/15414/files#r2151969948. SetValidators(nil) is not an issue because the mvslice can handle it.

s, err := util.NewBeaconState()
require.NoError(t, err)
h, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, h.SetValidators(nil))
err = reportEpochMetrics(t.Context(), s, h)
require.ErrorContains(t, "failed to initialize precompute: state has nil validator slice", err)
}

func TestReportEpochMetrics_BadAttestation(t *testing.T) {
s, err := util.NewBeaconState()
require.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/core/altair/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func Test_BaseReward(t *testing.T) {
valIdx: 2,
st: genState(1),
want: 0,
errString: "validator index 2 does not exist",
errString: "index 2 out of bounds",
},
{
name: "active balance is 32eth",
Expand Down Expand Up @@ -89,7 +89,7 @@ func Test_BaseRewardWithTotalBalance(t *testing.T) {
valIdx: 2,
activeBalance: 1,
want: 0,
errString: "validator index 2 does not exist",
errString: "index 2 out of bounds",
},
{
name: "active balance is 1",
Expand Down
3 changes: 3 additions & 0 deletions beacon-chain/core/blocks/block_operations_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func TestFuzzEth1DataHasEnoughSupport_10000(t *testing.T) {
require.NoError(t, err)
_, err = Eth1DataHasEnoughSupport(s, eth1data)
_ = err
fuzz.FreeMemory(i)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this one and the ones below in #15395

}

}
Expand Down Expand Up @@ -319,6 +320,7 @@ func TestFuzzverifyDeposit_10000(t *testing.T) {
require.NoError(t, err)
err = VerifyDeposit(s, deposit)
_ = err
fuzz.FreeMemory(i)
}
}

Expand Down Expand Up @@ -382,5 +384,6 @@ func TestFuzzVerifyExit_10000(t *testing.T) {
_ = err
err = VerifyExitAndSignature(val, s, ve)
_ = err
fuzz.FreeMemory(i)
}
}
8 changes: 4 additions & 4 deletions beacon-chain/core/helpers/sync_committee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestIsCurrentEpochSyncCommittee_DoesNotExist(t *testing.T) {
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))

ok, err := helpers.IsCurrentPeriodSyncCommittee(state, 12390192)
require.ErrorContains(t, "validator index 12390192 does not exist", err)
require.ErrorContains(t, "index 12390192 out of bounds", err)
require.Equal(t, false, ok)
}

Expand Down Expand Up @@ -187,7 +187,7 @@ func TestIsNextEpochSyncCommittee_DoesNotExist(t *testing.T) {
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))

ok, err := helpers.IsNextPeriodSyncCommittee(state, 120391029)
require.ErrorContains(t, "validator index 120391029 does not exist", err)
require.ErrorContains(t, "index 120391029 out of bounds", err)
require.Equal(t, false, ok)
}

Expand Down Expand Up @@ -287,7 +287,7 @@ func TestCurrentEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) {
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))

index, err := helpers.CurrentPeriodSyncSubcommitteeIndices(state, 129301923)
require.ErrorContains(t, "validator index 129301923 does not exist", err)
require.ErrorContains(t, "index 129301923 out of bounds", err)
require.DeepEqual(t, []primitives.CommitteeIndex(nil), index)
}

Expand Down Expand Up @@ -374,7 +374,7 @@ func TestNextEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) {
require.NoError(t, state.SetNextSyncCommittee(syncCommittee))

index, err := helpers.NextPeriodSyncSubcommitteeIndices(state, 21093019)
require.ErrorContains(t, "validator index 21093019 does not exist", err)
require.ErrorContains(t, "index 21093019 out of bounds", err)
require.DeepEqual(t, []primitives.CommitteeIndex(nil), index)
}

Expand Down
11 changes: 0 additions & 11 deletions beacon-chain/core/helpers/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,17 +561,6 @@ func TestActiveValidatorIndices(t *testing.T) {
},
want: []primitives.ValidatorIndex{0, 2, 3},
},*/
{
name: "impossible_zero_validators", // Regression test for issue #13051
Copy link
Contributor Author

@rkapka rkapka Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is impossible to test. Validators are a multi-value slice and a non-nil value is created in InitializeFromProto even if you have a nil value in the proto state. I added a test in the mvslice package that shows initializing the mvslice with a nil value is also not a problem as it will work in the same way as initializing with an empty slice.

args: args{
state: &ethpb.BeaconState{
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Validators: make([]*ethpb.Validator, 0),
},
epoch: 10,
},
wantedErr: "state has nil validator slice",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/rpc/eth/beacon/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ go_library(
"//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",
"//consensus-types/validator:go_default_library",
"//container/multi-value-slice:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//monitoring/tracing/trace:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/rpc/eth/beacon/handlers_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared"
"github.com/OffchainLabs/prysm/v6/config/features"
"github.com/OffchainLabs/prysm/v6/config/params"
consensus_types "github.com/OffchainLabs/prysm/v6/consensus-types"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
mvslice "github.com/OffchainLabs/prysm/v6/container/multi-value-slice"
"github.com/OffchainLabs/prysm/v6/crypto/bls"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
"github.com/OffchainLabs/prysm/v6/network/httputil"
Expand Down Expand Up @@ -501,7 +501,7 @@ func (s *Server) SubmitVoluntaryExit(w http.ResponseWriter, r *http.Request) {
}
val, err := headState.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
if err != nil {
if errors.Is(err, consensus_types.ErrOutOfBounds) {
if errors.Is(err, mvslice.ErrOutOfBounds) {
httputil.HandleError(w, "Could not get validator: "+err.Error(), http.StatusBadRequest)
return
}
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/rpc/eth/events/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
st := tc.getState()
v := &eth.Validator{ExitEpoch: math.MaxUint64, EffectiveBalance: params.BeaconConfig().MinActivationBalance, WithdrawalCredentials: make([]byte, 32)}
require.NoError(t, st.SetValidators([]*eth.Validator{v}))
require.NoError(t, st.SetBalances([]uint64{0}))
currentSlot := primitives.Slot(0)
// to avoid slot processing
require.NoError(t, st.SetSlot(currentSlot+1))
Expand Down
2 changes: 1 addition & 1 deletion beacon-chain/rpc/eth/validator/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ go_library(
"//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/primitives:go_default_library",
"//consensus-types/validator:go_default_library",
"//container/multi-value-slice:go_default_library",
"//crypto/bls/common:go_default_library",
"//encoding/bytesutil:go_default_library",
"//monitoring/tracing/trace:go_default_library",
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/rpc/eth/validator/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import (
"github.com/OffchainLabs/prysm/v6/config/features"
fieldparams "github.com/OffchainLabs/prysm/v6/config/fieldparams"
"github.com/OffchainLabs/prysm/v6/config/params"
consensus_types "github.com/OffchainLabs/prysm/v6/consensus-types"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
validator2 "github.com/OffchainLabs/prysm/v6/consensus-types/validator"
mvslice "github.com/OffchainLabs/prysm/v6/container/multi-value-slice"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
"github.com/OffchainLabs/prysm/v6/network/httputil"
Expand Down Expand Up @@ -570,7 +570,7 @@ func (s *Server) SubmitBeaconCommitteeSubscription(w http.ResponseWriter, r *htt
subscriptions[i] = consensusItem
val, err := st.ValidatorAtIndexReadOnly(consensusItem.ValidatorIndex)
if err != nil {
if errors.Is(err, consensus_types.ErrOutOfBounds) {
if errors.Is(err, mvslice.ErrOutOfBounds) {
httputil.HandleError(w, "Could not get validator: "+err.Error(), http.StatusBadRequest)
return
}
Expand Down
1 change: 0 additions & 1 deletion beacon-chain/state/fieldtrie/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ go_test(
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//beacon-chain/state/state-native/types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
Expand Down
62 changes: 12 additions & 50 deletions beacon-chain/state/fieldtrie/field_trie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
customtypes "github.com/OffchainLabs/prysm/v6/beacon-chain/state/state-native/custom-types"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/state-native/types"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state/stateutil"
"github.com/OffchainLabs/prysm/v6/config/features"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
mvslice "github.com/OffchainLabs/prysm/v6/container/multi-value-slice"
Expand All @@ -21,32 +20,19 @@ func TestFieldTrie_NewTrie(t *testing.T) {
t.Run("native state", func(t *testing.T) {
runNewTrie(t)
})
t.Run("native state with multivalue slice", func(t *testing.T) {
cfg := &features.Flags{}
cfg.EnableExperimentalState = true
reset := features.InitWithReset(cfg)
runNewTrie(t)

reset()
})
}

func runNewTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 40)
roots := newState.BlockRoots()
var elements interface{}
blockRoots := make([][32]byte, len(roots))
for i, r := range roots {
blockRoots[i] = [32]byte(r)
}
elements = customtypes.BlockRoots(blockRoots)

if features.Get().EnableExperimentalState {
mvRoots := buildTestCompositeSlice[[32]byte](blockRoots)
elements = mvslice.MultiValueSliceComposite[[32]byte]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvRoots,
}
mvRoots := buildTestCompositeSlice[[32]byte](blockRoots)
elements := mvslice.MultiValueSliceComposite[[32]byte]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvRoots,
}

trie, err := NewFieldTrie(types.BlockRoots, types.BasicArray, elements, uint64(params.BeaconConfig().SlotsPerHistoricalRoot))
Expand All @@ -69,27 +55,15 @@ func TestFieldTrie_RecomputeTrie(t *testing.T) {
t.Run("native state", func(t *testing.T) {
runRecomputeTrie(t)
})
t.Run("native state with multivalue slice", func(t *testing.T) {
cfg := &features.Flags{}
cfg.EnableExperimentalState = true
reset := features.InitWithReset(cfg)
runRecomputeTrie(t)

reset()
})
}

func runRecomputeTrie(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)

var elements interface{}
elements = newState.Validators()
if features.Get().EnableExperimentalState {
mvRoots := buildTestCompositeSlice[*ethpb.Validator](newState.Validators())
elements = mvslice.MultiValueSliceComposite[*ethpb.Validator]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvRoots,
}
mvRoots := buildTestCompositeSlice[*ethpb.Validator](newState.Validators())
elements := mvslice.MultiValueSliceComposite[*ethpb.Validator]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvRoots,
}

trie, err := NewFieldTrie(types.Validators, types.CompositeArray, elements, params.BeaconConfig().ValidatorRegistryLimit)
Expand Down Expand Up @@ -125,26 +99,14 @@ func TestFieldTrie_RecomputeTrie_CompressedArray(t *testing.T) {
t.Run("native state", func(t *testing.T) {
runRecomputeTrie_CompressedArray(t)
})
t.Run("native state with multivalue slice", func(t *testing.T) {
cfg := &features.Flags{}
cfg.EnableExperimentalState = true
reset := features.InitWithReset(cfg)
runRecomputeTrie_CompressedArray(t)

reset()
})
}

func runRecomputeTrie_CompressedArray(t *testing.T) {
newState, _ := util.DeterministicGenesisState(t, 32)
var elements interface{}
elements = newState.Balances()
if features.Get().EnableExperimentalState {
mvBals := buildTestCompositeSlice(newState.Balances())
elements = mvslice.MultiValueSliceComposite[uint64]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvBals,
}
mvBals := buildTestCompositeSlice(newState.Balances())
elements := mvslice.MultiValueSliceComposite[uint64]{
Identifiable: mockIdentifier{},
MultiValueSlice: mvBals,
}

trie, err := NewFieldTrie(types.Balances, types.CompressedArray, elements, stateutil.ValidatorLimitForBalancesChunks())
Expand Down
3 changes: 0 additions & 3 deletions beacon-chain/state/state-native/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,8 @@ go_library(
"//beacon-chain/state/state-native/custom-types:go_default_library",
"//beacon-chain/state/state-native/types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//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",
Expand Down Expand Up @@ -135,7 +133,6 @@ go_test(
"//beacon-chain/state/state-native/types:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//beacon-chain/state/testing:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
Expand Down
4 changes: 1 addition & 3 deletions beacon-chain/state/state-native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ Add the new getter and setter to `/beacon-chain/state/interfaces.go`.
- Update `spec_parameters.go`.
- Update `state_trie.go`:
- Add a `[version]Fields` variable that contains all fields of the new state version.
- Add a `[version]SharedFieldRefCount` constant that represents the number of fields whose references are shared between states.
- Add an `experimentalState[Version]SharedFieldCountRef` constant that represents the number of **non multi-value slice** fields whose references are shared
between states.
- Add a `[version]SharedFieldRefCount` constant that represents the number of fields whose references are shared between states. Multi-value slice references are not shared in this way so don't include them.
- Add the following functions: `InitializeFromProto[Version]()`, `InitializeFromProtoUnsafe[Version]()`.
- Update the following functions: `Copy()`, `initializeMerkleLayers()`, `RecordStateMetrics()` (applies only to multi-value slice fields), `rootSelector()`,
`finalizerCleanup()` (applies only to multi-value slice fields).
Expand Down
Loading
Loading