diff --git a/beacon-chain/blockchain/metrics_test.go b/beacon-chain/blockchain/metrics_test.go index 1bd6606c8918..dbce0b181e52 100644 --- a/beacon-chain/blockchain/metrics_test.go +++ b/beacon-chain/blockchain/metrics_test.go @@ -16,7 +16,7 @@ func TestReportEpochMetrics_BadHeadState(t *testing.T) { require.NoError(t, err) require.NoError(t, h.SetValidators(nil)) err = reportEpochMetrics(context.Background(), s, h) - require.ErrorContains(t, "failed to initialize precompute: nil validators in state", err) + require.ErrorContains(t, "failed to initialize precompute: state has nil validator slice", err) } func TestReportEpochMetrics_BadAttestation(t *testing.T) { diff --git a/beacon-chain/core/altair/reward_test.go b/beacon-chain/core/altair/reward_test.go index 98f24a4271fa..1dcdcb2a20a7 100644 --- a/beacon-chain/core/altair/reward_test.go +++ b/beacon-chain/core/altair/reward_test.go @@ -31,7 +31,7 @@ func Test_BaseReward(t *testing.T) { valIdx: 2, st: genState(1), want: 0, - errString: "index 2 out of range", + errString: "validator index 2 does not exist", }, { name: "active balance is 32eth", @@ -89,7 +89,7 @@ func Test_BaseRewardWithTotalBalance(t *testing.T) { valIdx: 2, activeBalance: 1, want: 0, - errString: "index 2 out of range", + errString: "validator index 2 does not exist", }, { name: "active balance is 1", diff --git a/beacon-chain/core/blocks/randao.go b/beacon-chain/core/blocks/randao.go index a162a2e07234..7e8f98d989cb 100644 --- a/beacon-chain/core/blocks/randao.go +++ b/beacon-chain/core/blocks/randao.go @@ -82,7 +82,7 @@ func ProcessRandaoNoVerify( for i, x := range blockRandaoReveal { latestMixSlice[i] ^= x } - if err := beaconState.UpdateRandaoMixesAtIndex(uint64(currentEpoch%latestMixesLength), latestMixSlice); err != nil { + if err := beaconState.UpdateRandaoMixesAtIndex(uint64(currentEpoch%latestMixesLength), [32]byte(latestMixSlice)); err != nil { return nil, err } return beaconState, nil diff --git a/beacon-chain/core/blocks/withdrawals_test.go b/beacon-chain/core/blocks/withdrawals_test.go index 417604fafad3..0a36ca68aed9 100644 --- a/beacon-chain/core/blocks/withdrawals_test.go +++ b/beacon-chain/core/blocks/withdrawals_test.go @@ -152,7 +152,7 @@ func TestProcessBLSToExecutionChange(t *testing.T) { } _, err = blocks.ProcessBLSToExecutionChange(st, signed) - require.ErrorContains(t, "out of range", err) + require.ErrorContains(t, "out of bounds", err) }) t.Run("signature does not verify", func(t *testing.T) { diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 80229fede3b0..f2ab91fbe4d7 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -349,7 +349,7 @@ func ProcessRandaoMixesReset(state state.BeaconState) (state.BeaconState, error) if err != nil { return nil, err } - if err := state.UpdateRandaoMixesAtIndex(uint64(nextEpoch%randaoMixLength), mix); err != nil { + if err := state.UpdateRandaoMixesAtIndex(uint64(nextEpoch%randaoMixLength), [32]byte(mix)); err != nil { return nil, err } diff --git a/beacon-chain/core/helpers/sync_committee_test.go b/beacon-chain/core/helpers/sync_committee_test.go index 287e1eba1c85..6bcbc1558601 100644 --- a/beacon-chain/core/helpers/sync_committee_test.go +++ b/beacon-chain/core/helpers/sync_committee_test.go @@ -99,7 +99,7 @@ func TestIsCurrentEpochSyncCommittee_DoesNotExist(t *testing.T) { require.NoError(t, state.SetNextSyncCommittee(syncCommittee)) ok, err := IsCurrentPeriodSyncCommittee(state, 12390192) - require.ErrorContains(t, "index 12390192 out of range", err) + require.ErrorContains(t, "validator index 12390192 does not exist", err) require.Equal(t, false, ok) } @@ -182,7 +182,7 @@ func TestIsNextEpochSyncCommittee_DoesNotExist(t *testing.T) { require.NoError(t, state.SetNextSyncCommittee(syncCommittee)) ok, err := IsNextPeriodSyncCommittee(state, 120391029) - require.ErrorContains(t, "index 120391029 out of range", err) + require.ErrorContains(t, "validator index 120391029 does not exist", err) require.Equal(t, false, ok) } @@ -282,7 +282,7 @@ func TestCurrentEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) { require.NoError(t, state.SetNextSyncCommittee(syncCommittee)) index, err := CurrentPeriodSyncSubcommitteeIndices(state, 129301923) - require.ErrorContains(t, "index 129301923 out of range", err) + require.ErrorContains(t, "validator index 129301923 does not exist", err) require.DeepEqual(t, []primitives.CommitteeIndex(nil), index) } @@ -367,7 +367,7 @@ func TestNextEpochSyncSubcommitteeIndices_DoesNotExist(t *testing.T) { require.NoError(t, state.SetNextSyncCommittee(syncCommittee)) index, err := NextPeriodSyncSubcommitteeIndices(state, 21093019) - require.ErrorContains(t, "index 21093019 out of range", err) + require.ErrorContains(t, "validator index 21093019 does not exist", err) require.DeepEqual(t, []primitives.CommitteeIndex(nil), index) } diff --git a/beacon-chain/core/transition/state.go b/beacon-chain/core/transition/state.go index 9ada3dc37c48..f397a2bc3130 100644 --- a/beacon-chain/core/transition/state.go +++ b/beacon-chain/core/transition/state.go @@ -9,6 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/container/trie" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" @@ -221,6 +222,18 @@ func OptimizedGenesisBeaconState(genesisTime uint64, preState state.BeaconState, // EmptyGenesisState returns an empty beacon state object. func EmptyGenesisState() (state.BeaconState, error) { + blockRoots := make([][]byte, fieldparams.BlockRootsLength) + for i := range blockRoots { + blockRoots[i] = make([]byte, fieldparams.RootLength) + } + stateRoots := make([][]byte, fieldparams.StateRootsLength) + for i := range stateRoots { + stateRoots[i] = make([]byte, fieldparams.RootLength) + } + mixes := make([][]byte, fieldparams.RandaoMixesLength) + for i := range mixes { + mixes[i] = make([]byte, fieldparams.RootLength) + } st := ðpb.BeaconState{ // Misc fields. Slot: 0, @@ -229,6 +242,9 @@ func EmptyGenesisState() (state.BeaconState, error) { CurrentVersion: params.BeaconConfig().GenesisForkVersion, Epoch: 0, }, + BlockRoots: blockRoots, + StateRoots: stateRoots, + RandaoMixes: mixes, // Validator registry fields. Validators: []*ethpb.Validator{}, Balances: []uint64{}, diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool.go b/beacon-chain/rpc/eth/beacon/handlers_pool.go index 9d3f52757e86..7259f7479c80 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool.go @@ -2,6 +2,7 @@ package beacon import ( "encoding/json" + "errors" "fmt" "io" "net/http" @@ -15,7 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" - state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/crypto/bls" http2 "github.com/prysmaticlabs/prysm/v4/network/http" @@ -228,8 +229,8 @@ func (s *Server) SubmitVoluntaryExit(w http.ResponseWriter, r *http.Request) { } val, err := headState.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex) if err != nil { - if outOfRangeErr, ok := err.(*state_native.ValidatorIndexOutOfRangeError); ok { - http2.HandleError(w, "Could not get exiting validator: "+outOfRangeErr.Error(), http.StatusBadRequest) + if errors.Is(err, consensus_types.ErrOutOfBounds) { + http2.HandleError(w, "Could not get validator: "+err.Error(), http.StatusBadRequest) return } http2.HandleError(w, "Could not get validator: "+err.Error(), http.StatusInternalServerError) diff --git a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go index fa8ee470581e..96096dddbfcf 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_pool_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_pool_test.go @@ -485,7 +485,7 @@ func TestSubmitVoluntaryExit(t *testing.T) { e := &http2.DefaultErrorJson{} require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e)) assert.Equal(t, http.StatusBadRequest, e.Code) - assert.Equal(t, true, strings.Contains(e.Message, "Could not get exiting validator")) + assert.Equal(t, true, strings.Contains(e.Message, "Could not get validator")) }) } diff --git a/beacon-chain/rpc/eth/beacon/state_test.go b/beacon-chain/rpc/eth/beacon/state_test.go index 2a1fca6e6838..3c261fab252d 100644 --- a/beacon-chain/rpc/eth/beacon/state_test.go +++ b/beacon-chain/rpc/eth/beacon/state_test.go @@ -108,8 +108,8 @@ func TestGetStateRoot(t *testing.T) { } func TestGetRandao(t *testing.T) { - mixCurrent := bytesutil.PadTo([]byte("current"), 32) - mixOld := bytesutil.PadTo([]byte("old"), 32) + mixCurrent := bytesutil.ToBytes32([]byte("current")) + mixOld := bytesutil.ToBytes32([]byte("old")) epochCurrent := primitives.Epoch(100000) epochOld := 100000 - params.BeaconConfig().EpochsPerHistoricalVector + 1 @@ -125,7 +125,7 @@ func TestGetRandao(t *testing.T) { headSt, err := util.NewBeaconState() require.NoError(t, err) require.NoError(t, headSt.SetSlot(params.BeaconConfig().SlotsPerEpoch)) - headRandao := bytesutil.PadTo([]byte("head"), 32) + headRandao := bytesutil.ToBytes32([]byte("head")) require.NoError(t, headSt.UpdateRandaoMixesAtIndex(uint64(headEpoch), headRandao)) db := dbTest.SetupDB(t) @@ -143,17 +143,17 @@ func TestGetRandao(t *testing.T) { t.Run("no epoch requested", func(t *testing.T) { resp, err := server.GetRandao(ctx, ð2.RandaoRequest{StateId: []byte("head")}) require.NoError(t, err) - assert.DeepEqual(t, mixCurrent, resp.Data.Randao) + assert.DeepEqual(t, mixCurrent[:], resp.Data.Randao) }) t.Run("current epoch requested", func(t *testing.T) { resp, err := server.GetRandao(ctx, ð2.RandaoRequest{StateId: []byte("head"), Epoch: &epochCurrent}) require.NoError(t, err) - assert.DeepEqual(t, mixCurrent, resp.Data.Randao) + assert.DeepEqual(t, mixCurrent[:], resp.Data.Randao) }) t.Run("old epoch requested", func(t *testing.T) { resp, err := server.GetRandao(ctx, ð2.RandaoRequest{StateId: []byte("head"), Epoch: &epochOld}) require.NoError(t, err) - assert.DeepEqual(t, mixOld, resp.Data.Randao) + assert.DeepEqual(t, mixOld[:], resp.Data.Randao) }) t.Run("head state below `EpochsPerHistoricalVector`", func(t *testing.T) { server.Stater = &testutil.MockStater{ @@ -161,7 +161,7 @@ func TestGetRandao(t *testing.T) { } resp, err := server.GetRandao(ctx, ð2.RandaoRequest{StateId: []byte("head")}) require.NoError(t, err) - assert.DeepEqual(t, headRandao, resp.Data.Randao) + assert.DeepEqual(t, headRandao[:], resp.Data.Randao) }) t.Run("epoch too old", func(t *testing.T) { epochTooOld := primitives.Epoch(100000 - st.RandaoMixesLength()) diff --git a/beacon-chain/rpc/eth/validator/BUILD.bazel b/beacon-chain/rpc/eth/validator/BUILD.bazel index f9dff1253f9c..d79f652d7e35 100644 --- a/beacon-chain/rpc/eth/validator/BUILD.bazel +++ b/beacon-chain/rpc/eth/validator/BUILD.bazel @@ -29,10 +29,10 @@ go_library( "//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/lookup:go_default_library", "//beacon-chain/state:go_default_library", - "//beacon-chain/state/state-native:go_default_library", "//beacon-chain/sync:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", + "//consensus-types:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", "//encoding/bytesutil:go_default_library", diff --git a/beacon-chain/rpc/eth/validator/handlers.go b/beacon-chain/rpc/eth/validator/handlers.go index 79d4d2043ee1..ae9fb281c3a6 100644 --- a/beacon-chain/rpc/eth/validator/handlers.go +++ b/beacon-chain/rpc/eth/validator/handlers.go @@ -24,9 +24,9 @@ import ( rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" - state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" "github.com/prysmaticlabs/prysm/v4/config/params" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" validator2 "github.com/prysmaticlabs/prysm/v4/consensus-types/validator" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" @@ -339,8 +339,8 @@ func (s *Server) SubmitBeaconCommitteeSubscription(w http.ResponseWriter, r *htt subscriptions[i] = consensusItem val, err := st.ValidatorAtIndexReadOnly(consensusItem.ValidatorIndex) if err != nil { - if outOfRangeErr, ok := err.(*state_native.ValidatorIndexOutOfRangeError); ok { - http2.HandleError(w, "Could not get validator: "+outOfRangeErr.Error(), http.StatusBadRequest) + if errors.Is(err, consensus_types.ErrOutOfBounds) { + http2.HandleError(w, "Could not get validator: "+err.Error(), http.StatusBadRequest) return } http2.HandleError(w, "Could not get validator: "+err.Error(), http.StatusInternalServerError) diff --git a/beacon-chain/state/fieldtrie/BUILD.bazel b/beacon-chain/state/fieldtrie/BUILD.bazel index b4f2aba446ce..bd025d3699df 100644 --- a/beacon-chain/state/fieldtrie/BUILD.bazel +++ b/beacon-chain/state/fieldtrie/BUILD.bazel @@ -7,12 +7,11 @@ go_library( "field_trie_helpers.go", ], importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/fieldtrie", - visibility = ["//beacon-chain:__subpackages__"], + visibility = ["//visibility:public"], deps = [ "//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", - "//encoding/bytesutil:go_default_library", "//math:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "@com_github_pkg_errors//:go_default_library", diff --git a/beacon-chain/state/fieldtrie/field_trie.go b/beacon-chain/state/fieldtrie/field_trie.go index 0ff909f9aa5b..512fa3b3b982 100644 --- a/beacon-chain/state/fieldtrie/field_trie.go +++ b/beacon-chain/state/fieldtrie/field_trie.go @@ -31,11 +31,11 @@ type FieldTrie struct { // NewFieldTrie is the constructor for the field trie data structure. It creates the corresponding // trie according to the given parameters. Depending on whether the field is a basic/composite array // which is either fixed/variable length, it will appropriately determine the trie. -func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) (*FieldTrie, error) { +func NewFieldTrie(field types.FieldIndex, fieldInfo types.DataType, elements interface{}, length uint64) (*FieldTrie, error) { if elements == nil { return &FieldTrie{ field: field, - dataType: dataType, + dataType: fieldInfo, reference: stateutil.NewRef(1), RWMutex: new(sync.RWMutex), length: length, @@ -48,10 +48,10 @@ func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements inte return nil, err } - if err := validateElements(field, dataType, elements, length); err != nil { + if err := validateElements(field, fieldInfo, elements, length); err != nil { return nil, err } - switch dataType { + switch fieldInfo { case types.BasicArray: fl, err := stateutil.ReturnTrieLayer(fieldRoots, length) if err != nil { @@ -60,7 +60,7 @@ func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements inte return &FieldTrie{ fieldLayers: fl, field: field, - dataType: dataType, + dataType: fieldInfo, reference: stateutil.NewRef(1), RWMutex: new(sync.RWMutex), length: length, @@ -70,14 +70,14 @@ func NewFieldTrie(field types.FieldIndex, dataType types.DataType, elements inte return &FieldTrie{ fieldLayers: stateutil.ReturnTrieLayerVariable(fieldRoots, length), field: field, - dataType: dataType, + dataType: fieldInfo, reference: stateutil.NewRef(1), RWMutex: new(sync.RWMutex), length: length, numOfElems: reflect.Indirect(reflect.ValueOf(elements)).Len(), }, nil default: - return nil, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(dataType).Name()) + return nil, errors.Errorf("unrecognized data type in field map: %v", reflect.TypeOf(fieldInfo).Name()) } } diff --git a/beacon-chain/state/fieldtrie/field_trie_helpers.go b/beacon-chain/state/fieldtrie/field_trie_helpers.go index 39b4d4c244af..b43de40ebe25 100644 --- a/beacon-chain/state/fieldtrie/field_trie_helpers.go +++ b/beacon-chain/state/fieldtrie/field_trie_helpers.go @@ -9,7 +9,6 @@ import ( customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" - "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" pmath "github.com/prysmaticlabs/prysm/v4/math" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) @@ -46,8 +45,8 @@ func (f *FieldTrie) validateIndices(idxs []uint64) error { return nil } -func validateElements(field types.FieldIndex, dataType types.DataType, elements interface{}, length uint64) error { - if dataType == types.CompressedArray { +func validateElements(field types.FieldIndex, fieldInfo types.DataType, elements interface{}, length uint64) error { + if fieldInfo == types.CompressedArray { comLength, err := field.ElemsInChunk() if err != nil { return err @@ -65,11 +64,11 @@ func validateElements(field types.FieldIndex, dataType types.DataType, elements func fieldConverters(field types.FieldIndex, indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { switch field { case types.BlockRoots: - return convertBlockRoots(indices, elements, convertAll) + return convert32ByteArrays[customtypes.BlockRoots](indices, elements, convertAll) case types.StateRoots: - return convertStateRoots(indices, elements, convertAll) + return convert32ByteArrays[customtypes.StateRoots](indices, elements, convertAll) case types.RandaoMixes: - return convertRandaoMixes(indices, elements, convertAll) + return convert32ByteArrays[customtypes.RandaoMixes](indices, elements, convertAll) case types.Eth1DataVotes: return convertEth1DataVotes(indices, elements, convertAll) case types.Validators: @@ -83,37 +82,13 @@ func fieldConverters(field types.FieldIndex, indices []uint64, elements interfac } } -func convertBlockRoots(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { - switch val := elements.(type) { - case [][]byte: - return handleByteArrays(val, indices, convertAll) - case *customtypes.BlockRoots: - return handle32ByteArrays(val[:], indices, convertAll) - default: - return nil, errors.Errorf("Incorrect type used for block roots") - } -} - -func convertStateRoots(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { - switch val := elements.(type) { - case [][]byte: - return handleByteArrays(val, indices, convertAll) - case *customtypes.StateRoots: - return handle32ByteArrays(val[:], indices, convertAll) - default: - return nil, errors.Errorf("Incorrect type used for state roots") - } -} - -func convertRandaoMixes(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { - switch val := elements.(type) { - case [][]byte: - return handleByteArrays(val, indices, convertAll) - case *customtypes.RandaoMixes: - return handle32ByteArrays(val[:], indices, convertAll) - default: - return nil, errors.Errorf("Incorrect type used for randao mixes") +func convert32ByteArrays[T ~[][32]byte](indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { + val, ok := elements.(T) + if !ok { + var t T + return nil, errors.Errorf("Wanted type of %T but got %T", t, elements) } + return handle32ByteArrays(val, indices, convertAll) } func convertEth1DataVotes(indices []uint64, elements interface{}, convertAll bool) ([][32]byte, error) { @@ -148,34 +123,6 @@ func convertBalances(indices []uint64, elements interface{}, convertAll bool) ([ return handleBalanceSlice(val, indices, convertAll) } -// handleByteArrays computes and returns byte arrays in a slice of root format. -func handleByteArrays(val [][]byte, indices []uint64, convertAll bool) ([][32]byte, error) { - length := len(indices) - if convertAll { - length = len(val) - } - roots := make([][32]byte, 0, length) - rootCreator := func(input []byte) { - newRoot := bytesutil.ToBytes32(input) - roots = append(roots, newRoot) - } - if convertAll { - for i := range val { - rootCreator(val[i]) - } - return roots, nil - } - if len(val) > 0 { - for _, idx := range indices { - if idx > uint64(len(val))-1 { - return nil, fmt.Errorf("index %d greater than number of byte arrays %d", idx, len(val)) - } - rootCreator(val[idx]) - } - } - return roots, nil -} - // handle32ByteArrays computes and returns 32 byte arrays in a slice of root format. func handle32ByteArrays(val [][32]byte, indices []uint64, convertAll bool) ([][32]byte, error) { length := len(indices) diff --git a/beacon-chain/state/fieldtrie/field_trie_test.go b/beacon-chain/state/fieldtrie/field_trie_test.go index 7887984ad161..57bfd6d1f786 100644 --- a/beacon-chain/state/fieldtrie/field_trie_test.go +++ b/beacon-chain/state/fieldtrie/field_trie_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/fieldtrie" + customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" "github.com/prysmaticlabs/prysm/v4/config/params" @@ -16,11 +17,15 @@ import ( func TestFieldTrie_NewTrie(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 40) + roots := newState.BlockRoots() + blockRoots := make([][32]byte, len(roots)) + for i, r := range roots { + blockRoots[i] = [32]byte(r) + } - // 5 represents the enum value of state roots - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(5), types.BasicArray, newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot)) + trie, err := fieldtrie.NewFieldTrie(types.BlockRoots, types.BasicArray, customtypes.BlockRoots(blockRoots), uint64(params.BeaconConfig().SlotsPerHistoricalRoot)) require.NoError(t, err) - root, err := stateutil.RootsArrayHashTreeRoot(newState.StateRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot)) + root, err := stateutil.RootsArrayHashTreeRoot(newState.BlockRoots(), uint64(params.BeaconConfig().SlotsPerHistoricalRoot)) require.NoError(t, err) newRoot, err := trie.TrieRoot() require.NoError(t, err) @@ -28,7 +33,7 @@ func TestFieldTrie_NewTrie(t *testing.T) { } func TestFieldTrie_NewTrie_NilElements(t *testing.T) { - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(5), types.BasicArray, nil, 8234) + trie, err := fieldtrie.NewFieldTrie(types.BlockRoots, types.BasicArray, nil, 8234) require.NoError(t, err) _, err = trie.TrieRoot() require.ErrorIs(t, err, fieldtrie.ErrEmptyFieldTrie) @@ -36,8 +41,7 @@ func TestFieldTrie_NewTrie_NilElements(t *testing.T) { func TestFieldTrie_RecomputeTrie(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 32) - // 10 represents the enum value of validators - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(11), types.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit) + trie, err := fieldtrie.NewFieldTrie(types.Validators, types.CompositeArray, newState.Validators(), params.BeaconConfig().ValidatorRegistryLimit) require.NoError(t, err) oldroot, err := trie.TrieRoot() @@ -68,7 +72,7 @@ func TestFieldTrie_RecomputeTrie(t *testing.T) { func TestFieldTrie_RecomputeTrie_CompressedArray(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 32) - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(12), types.CompressedArray, newState.Balances(), stateutil.ValidatorLimitForBalancesChunks()) + trie, err := fieldtrie.NewFieldTrie(types.Balances, types.CompressedArray, newState.Balances(), stateutil.ValidatorLimitForBalancesChunks()) require.NoError(t, err) require.Equal(t, trie.Length(), stateutil.ValidatorLimitForBalancesChunks()) changedIdx := []uint64{4, 8} @@ -85,14 +89,19 @@ func TestFieldTrie_RecomputeTrie_CompressedArray(t *testing.T) { func TestNewFieldTrie_UnknownType(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 32) - _, err := fieldtrie.NewFieldTrie(types.FieldIndex(12), 4, newState.Balances(), 32) + _, err := fieldtrie.NewFieldTrie(types.Balances, 4, newState.Balances(), 32) require.ErrorContains(t, "unrecognized data type", err) } func TestFieldTrie_CopyTrieImmutable(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 32) - // 12 represents the enum value of randao mixes. - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(13), types.BasicArray, newState.RandaoMixes(), uint64(params.BeaconConfig().EpochsPerHistoricalVector)) + mixes := newState.RandaoMixes() + randaoMixes := make([][32]byte, len(mixes)) + for i, r := range mixes { + randaoMixes[i] = [32]byte(r) + } + + trie, err := fieldtrie.NewFieldTrie(types.RandaoMixes, types.BasicArray, customtypes.RandaoMixes(randaoMixes), uint64(params.BeaconConfig().EpochsPerHistoricalVector)) require.NoError(t, err) newTrie := trie.CopyTrie() @@ -100,10 +109,15 @@ func TestFieldTrie_CopyTrieImmutable(t *testing.T) { changedIdx := []uint64{2, 29} changedVals := [][32]byte{{'A', 'B'}, {'C', 'D'}} - require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[0], changedVals[0][:])) - require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[1], changedVals[1][:])) + require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[0], changedVals[0])) + require.NoError(t, newState.UpdateRandaoMixesAtIndex(changedIdx[1], changedVals[1])) - root, err := trie.RecomputeTrie(changedIdx, newState.RandaoMixes()) + mixes = newState.RandaoMixes() + randaoMixes = make([][32]byte, len(mixes)) + for i, r := range mixes { + randaoMixes[i] = [32]byte(r) + } + root, err := trie.RecomputeTrie(changedIdx, customtypes.RandaoMixes(randaoMixes)) require.NoError(t, err) newRoot, err := newTrie.TrieRoot() require.NoError(t, err) @@ -113,7 +127,7 @@ func TestFieldTrie_CopyTrieImmutable(t *testing.T) { } func TestFieldTrie_CopyAndTransferEmpty(t *testing.T) { - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(13), types.BasicArray, nil, uint64(params.BeaconConfig().EpochsPerHistoricalVector)) + trie, err := fieldtrie.NewFieldTrie(types.RandaoMixes, types.BasicArray, nil, uint64(params.BeaconConfig().EpochsPerHistoricalVector)) require.NoError(t, err) require.DeepEqual(t, trie, trie.CopyTrie()) @@ -123,7 +137,7 @@ func TestFieldTrie_CopyAndTransferEmpty(t *testing.T) { func TestFieldTrie_TransferTrie(t *testing.T) { newState, _ := util.DeterministicGenesisState(t, 32) maxLength := (params.BeaconConfig().ValidatorRegistryLimit*8 + 31) / 32 - trie, err := fieldtrie.NewFieldTrie(types.FieldIndex(12), types.CompressedArray, newState.Balances(), maxLength) + trie, err := fieldtrie.NewFieldTrie(types.Balances, types.CompressedArray, newState.Balances(), maxLength) require.NoError(t, err) oldRoot, err := trie.TrieRoot() require.NoError(t, err) diff --git a/beacon-chain/state/fieldtrie/helpers_test.go b/beacon-chain/state/fieldtrie/helpers_test.go index 224643025d35..950308369d13 100644 --- a/beacon-chain/state/fieldtrie/helpers_test.go +++ b/beacon-chain/state/fieldtrie/helpers_test.go @@ -103,22 +103,12 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) { errMsg string expectedLength int }{ - { - name: "BlockRoots [][]bytes", - args: &args{ - field: types.FieldIndex(5), - indices: []uint64{}, - elements: [][]byte{[]byte("dfsadfsadf")}, - convertAll: true, - }, - wantHex: []string{"0x6466736164667361646600000000000000000000000000000000000000000000"}, - }, { name: "BlockRoots customtypes.BlockRoots", args: &args{ field: types.FieldIndex(5), indices: []uint64{}, - elements: &customtypes.BlockRoots{}, + elements: customtypes.BlockRoots{}, convertAll: true, }, wantHex: []string{"0x0000000000000000000000000000000000000000000000000000000000000000"}, @@ -133,34 +123,14 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) { convertAll: true, }, wantHex: nil, - errMsg: "Incorrect type used for block roots", - }, - { - name: "BlockRoots [][]bytes", - args: &args{ - field: types.FieldIndex(5), - indices: []uint64{}, - elements: [][]byte{[]byte("dfsadfsadf")}, - convertAll: true, - }, - wantHex: []string{"0x6466736164667361646600000000000000000000000000000000000000000000"}, - }, - { - name: "StateRoots [][]bytes", - args: &args{ - field: types.FieldIndex(6), - indices: []uint64{}, - elements: [][]byte{[]byte("dfsadfsadf")}, - convertAll: true, - }, - wantHex: []string{"0x6466736164667361646600000000000000000000000000000000000000000000"}, + errMsg: "Wanted type of customtypes.BlockRoots", }, { name: "StateRoots customtypes.StateRoots", args: &args{ field: types.FieldIndex(6), indices: []uint64{}, - elements: &customtypes.StateRoots{}, + elements: customtypes.StateRoots{}, convertAll: true, }, wantHex: []string{"0x0000000000000000000000000000000000000000000000000000000000000000"}, @@ -175,45 +145,25 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) { convertAll: true, }, wantHex: nil, - errMsg: "Incorrect type used for state roots", - }, - { - name: "StateRoots [][]bytes convert all false", - args: &args{ - field: types.FieldIndex(6), - indices: []uint64{}, - elements: [][]byte{[]byte("dfsadfsadf")}, - convertAll: false, - }, - wantHex: []string{"0x6466736164667361646600000000000000000000000000000000000000000000"}, + errMsg: "Wanted type of customtypes.StateRoots", }, { name: "StateRoots customtypes.StateRoots convert all false", args: &args{ field: types.FieldIndex(6), indices: []uint64{}, - elements: &customtypes.StateRoots{}, + elements: customtypes.StateRoots{}, convertAll: false, }, wantHex: []string{"0x0000000000000000000000000000000000000000000000000000000000000000"}, expectedLength: 8192, }, - { - name: "RandaoMixes [][]bytes", - args: &args{ - field: types.FieldIndex(13), - indices: []uint64{}, - elements: [][]byte{[]byte("dfsadfsadf")}, - convertAll: true, - }, - wantHex: []string{"0x6466736164667361646600000000000000000000000000000000000000000000"}, - }, { name: "RandaoMixes customtypes.RandaoMixes", args: &args{ field: types.FieldIndex(13), indices: []uint64{}, - elements: &customtypes.RandaoMixes{}, + elements: customtypes.RandaoMixes{}, convertAll: true, }, wantHex: []string{"0x0000000000000000000000000000000000000000000000000000000000000000"}, @@ -228,7 +178,7 @@ func TestFieldTrie_NativeState_fieldConvertersNative(t *testing.T) { convertAll: true, }, wantHex: nil, - errMsg: "Incorrect type used for randao mixes", + errMsg: "Wanted type of customtypes.RandaoMixes", }, { name: "Eth1DataVotes type not found", diff --git a/beacon-chain/state/interfaces.go b/beacon-chain/state/interfaces.go index 33bdd77eb1f6..b5aeecf1f49a 100644 --- a/beacon-chain/state/interfaces.go +++ b/beacon-chain/state/interfaces.go @@ -237,7 +237,7 @@ type WriteOnlyBalances interface { // WriteOnlyRandaoMixes defines a struct which only has write access to randao mixes methods. type WriteOnlyRandaoMixes interface { SetRandaoMixes(val [][]byte) error - UpdateRandaoMixesAtIndex(idx uint64, val []byte) error + UpdateRandaoMixesAtIndex(idx uint64, val [32]byte) error } // WriteOnlyCheckpoint defines a struct which only has write access to check point methods. diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index 6949ffb4a207..ad433a5045cb 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "getters_validator.go", "getters_withdrawal.go", "hasher.go", + "multi_value_slices.go", "proofs.go", "readonly_validator.go", "setters_attestation.go", @@ -49,11 +50,14 @@ 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", + "//container/multi-value-slice:go_default_library", "//container/slice:go_default_library", "//crypto/hash:go_default_library", "//encoding/bytesutil:go_default_library", @@ -64,6 +68,8 @@ go_library( "//runtime/version:go_default_library", "//time/slots:go_default_library", "@com_github_pkg_errors//:go_default_library", + "@com_github_prometheus_client_golang//prometheus:go_default_library", + "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", "@com_github_prysmaticlabs_fastssz//:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@io_opencensus_go//trace:go_default_library", @@ -82,6 +88,7 @@ go_test( "getters_validator_test.go", "getters_withdrawal_test.go", "hasher_test.go", + "mvslice_fuzz_test.go", "proofs_test.go", "readonly_validator_test.go", "references_test.go", @@ -96,6 +103,7 @@ go_test( "state_trie_test.go", "types_test.go", ], + data = glob(["testdata/**"]), embed = [":go_default_library"], deps = [ "//beacon-chain/core/transition:go_default_library", @@ -103,6 +111,7 @@ 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", diff --git a/beacon-chain/state/state-native/beacon_state_mainnet.go b/beacon-chain/state/state-native/beacon_state_mainnet.go index 6495bc5f1584..54974a3889da 100644 --- a/beacon-chain/state/state-native/beacon_state_mainnet.go +++ b/beacon-chain/state/state-native/beacon_state_mainnet.go @@ -11,6 +11,7 @@ import ( customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" @@ -25,16 +26,21 @@ type BeaconState struct { slot primitives.Slot fork *ethpb.Fork latestBlockHeader *ethpb.BeaconBlockHeader - blockRoots *customtypes.BlockRoots - stateRoots *customtypes.StateRoots + blockRoots customtypes.BlockRoots + blockRootsMultiValue *MultiValueBlockRoots + stateRoots customtypes.StateRoots + stateRootsMultiValue *MultiValueStateRoots historicalRoots customtypes.HistoricalRoots historicalSummaries []*ethpb.HistoricalSummary eth1Data *ethpb.Eth1Data eth1DataVotes []*ethpb.Eth1Data eth1DepositIndex uint64 validators []*ethpb.Validator + validatorsMultiValue *MultiValueValidators balances []uint64 - randaoMixes *customtypes.RandaoMixes + balancesMultiValue *MultiValueBalances + randaoMixes customtypes.RandaoMixes + randaoMixesMultiValue *MultiValueRandaoMixes slashings []uint64 previousEpochAttestations []*ethpb.PendingAttestation currentEpochAttestations []*ethpb.PendingAttestation @@ -45,6 +51,7 @@ type BeaconState struct { currentJustifiedCheckpoint *ethpb.Checkpoint finalizedCheckpoint *ethpb.Checkpoint inactivityScores []uint64 + inactivityScoresMultiValue *MultiValueInactivityScores currentSyncCommittee *ethpb.SyncCommittee nextSyncCommittee *ethpb.SyncCommittee latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader @@ -53,6 +60,7 @@ type BeaconState struct { nextWithdrawalIndex uint64 nextWithdrawalValidatorIndex primitives.ValidatorIndex + id uint64 lock sync.RWMutex dirtyFields map[types.FieldIndex]bool dirtyIndices map[types.FieldIndex][]uint64 @@ -70,8 +78,8 @@ type beaconStateMarshalable struct { Slot primitives.Slot `json:"slot" yaml:"slot"` Fork *ethpb.Fork `json:"fork" yaml:"fork"` LatestBlockHeader *ethpb.BeaconBlockHeader `json:"latest_block_header" yaml:"latest_block_header"` - BlockRoots *customtypes.BlockRoots `json:"block_roots" yaml:"block_roots"` - StateRoots *customtypes.StateRoots `json:"state_roots" yaml:"state_roots"` + BlockRoots customtypes.BlockRoots `json:"block_roots" yaml:"block_roots"` + StateRoots customtypes.StateRoots `json:"state_roots" yaml:"state_roots"` HistoricalRoots customtypes.HistoricalRoots `json:"historical_roots" yaml:"historical_roots"` HistoricalSummaries []*ethpb.HistoricalSummary `json:"historical_summaries" yaml:"historical_summaries"` Eth1Data *ethpb.Eth1Data `json:"eth_1_data" yaml:"eth_1_data"` @@ -79,7 +87,7 @@ type beaconStateMarshalable struct { Eth1DepositIndex uint64 `json:"eth_1_deposit_index" yaml:"eth_1_deposit_index"` Validators []*ethpb.Validator `json:"validators" yaml:"validators"` Balances []uint64 `json:"balances" yaml:"balances"` - RandaoMixes *customtypes.RandaoMixes `json:"randao_mixes" yaml:"randao_mixes"` + RandaoMixes customtypes.RandaoMixes `json:"randao_mixes" yaml:"randao_mixes"` Slashings []uint64 `json:"slashings" yaml:"slashings"` PreviousEpochAttestations []*ethpb.PendingAttestation `json:"previous_epoch_attestations" yaml:"previous_epoch_attestations"` CurrentEpochAttestations []*ethpb.PendingAttestation `json:"current_epoch_attestations" yaml:"current_epoch_attestations"` @@ -99,6 +107,29 @@ type beaconStateMarshalable struct { } func (b *BeaconState) MarshalJSON() ([]byte, error) { + var bRoots customtypes.BlockRoots + var sRoots customtypes.StateRoots + var mixes customtypes.RandaoMixes + var balances []uint64 + var inactivityScores []uint64 + var vals []*ethpb.Validator + + if features.Get().EnableExperimentalState { + bRoots = b.blockRootsMultiValue.Value(b) + sRoots = b.stateRootsMultiValue.Value(b) + mixes = b.randaoMixesMultiValue.Value(b) + balances = b.balancesMultiValue.Value(b) + inactivityScores = b.inactivityScoresMultiValue.Value(b) + vals = b.validatorsMultiValue.Value(b) + } else { + bRoots = b.blockRoots + sRoots = b.stateRoots + mixes = b.randaoMixes + balances = b.balances + inactivityScores = b.inactivityScores + vals = b.validators + } + marshalable := &beaconStateMarshalable{ Version: b.version, GenesisTime: b.genesisTime, @@ -106,16 +137,16 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots, - StateRoots: b.stateRoots, + BlockRoots: bRoots, + StateRoots: sRoots, HistoricalRoots: b.historicalRoots, HistoricalSummaries: b.historicalSummaries, Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes, + Validators: vals, + Balances: balances, + RandaoMixes: mixes, Slashings: b.slashings, PreviousEpochAttestations: b.previousEpochAttestations, CurrentEpochAttestations: b.currentEpochAttestations, @@ -125,7 +156,7 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, FinalizedCheckpoint: b.finalizedCheckpoint, - InactivityScores: b.inactivityScores, + InactivityScores: inactivityScores, CurrentSyncCommittee: b.currentSyncCommittee, NextSyncCommittee: b.nextSyncCommittee, LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader, diff --git a/beacon-chain/state/state-native/beacon_state_minimal.go b/beacon-chain/state/state-native/beacon_state_minimal.go index 885e39d218a9..c2cc99285078 100644 --- a/beacon-chain/state/state-native/beacon_state_minimal.go +++ b/beacon-chain/state/state-native/beacon_state_minimal.go @@ -11,6 +11,7 @@ import ( customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" @@ -25,16 +26,21 @@ type BeaconState struct { slot primitives.Slot fork *ethpb.Fork latestBlockHeader *ethpb.BeaconBlockHeader - blockRoots *customtypes.BlockRoots - stateRoots *customtypes.StateRoots + blockRoots customtypes.BlockRoots + blockRootsMultiValue *MultiValueBlockRoots + stateRoots customtypes.StateRoots + stateRootsMultiValue *MultiValueStateRoots historicalRoots customtypes.HistoricalRoots historicalSummaries []*ethpb.HistoricalSummary eth1Data *ethpb.Eth1Data eth1DataVotes []*ethpb.Eth1Data eth1DepositIndex uint64 validators []*ethpb.Validator + validatorsMultiValue *MultiValueValidators balances []uint64 - randaoMixes *customtypes.RandaoMixes + balancesMultiValue *MultiValueBalances + randaoMixes customtypes.RandaoMixes + randaoMixesMultiValue *MultiValueRandaoMixes slashings []uint64 previousEpochAttestations []*ethpb.PendingAttestation currentEpochAttestations []*ethpb.PendingAttestation @@ -45,6 +51,7 @@ type BeaconState struct { currentJustifiedCheckpoint *ethpb.Checkpoint finalizedCheckpoint *ethpb.Checkpoint inactivityScores []uint64 + inactivityScoresMultiValue *MultiValueInactivityScores currentSyncCommittee *ethpb.SyncCommittee nextSyncCommittee *ethpb.SyncCommittee latestExecutionPayloadHeader *enginev1.ExecutionPayloadHeader @@ -53,6 +60,7 @@ type BeaconState struct { nextWithdrawalIndex uint64 nextWithdrawalValidatorIndex primitives.ValidatorIndex + id uint64 lock sync.RWMutex dirtyFields map[types.FieldIndex]bool dirtyIndices map[types.FieldIndex][]uint64 @@ -70,8 +78,8 @@ type beaconStateMarshalable struct { Slot primitives.Slot `json:"slot" yaml:"slot"` Fork *ethpb.Fork `json:"fork" yaml:"fork"` LatestBlockHeader *ethpb.BeaconBlockHeader `json:"latest_block_header" yaml:"latest_block_header"` - BlockRoots *customtypes.BlockRoots `json:"block_roots" yaml:"block_roots"` - StateRoots *customtypes.StateRoots `json:"state_roots" yaml:"state_roots"` + BlockRoots customtypes.BlockRoots `json:"block_roots" yaml:"block_roots"` + StateRoots customtypes.StateRoots `json:"state_roots" yaml:"state_roots"` HistoricalRoots customtypes.HistoricalRoots `json:"historical_roots" yaml:"historical_roots"` HistoricalSummaries []*ethpb.HistoricalSummary `json:"historical_summaries" yaml:"historical_summaries"` Eth1Data *ethpb.Eth1Data `json:"eth_1_data" yaml:"eth_1_data"` @@ -79,7 +87,7 @@ type beaconStateMarshalable struct { Eth1DepositIndex uint64 `json:"eth_1_deposit_index" yaml:"eth_1_deposit_index"` Validators []*ethpb.Validator `json:"validators" yaml:"validators"` Balances []uint64 `json:"balances" yaml:"balances"` - RandaoMixes *customtypes.RandaoMixes `json:"randao_mixes" yaml:"randao_mixes"` + RandaoMixes customtypes.RandaoMixes `json:"randao_mixes" yaml:"randao_mixes"` Slashings []uint64 `json:"slashings" yaml:"slashings"` PreviousEpochAttestations []*ethpb.PendingAttestation `json:"previous_epoch_attestations" yaml:"previous_epoch_attestations"` CurrentEpochAttestations []*ethpb.PendingAttestation `json:"current_epoch_attestations" yaml:"current_epoch_attestations"` @@ -99,6 +107,29 @@ type beaconStateMarshalable struct { } func (b *BeaconState) MarshalJSON() ([]byte, error) { + var bRoots customtypes.BlockRoots + var sRoots customtypes.StateRoots + var mixes customtypes.RandaoMixes + var balances []uint64 + var inactivityScores []uint64 + var vals []*ethpb.Validator + + if features.Get().EnableExperimentalState { + bRoots = b.blockRootsMultiValue.Value(b) + sRoots = b.stateRootsMultiValue.Value(b) + mixes = b.randaoMixesMultiValue.Value(b) + balances = b.balancesMultiValue.Value(b) + inactivityScores = b.inactivityScoresMultiValue.Value(b) + vals = b.validatorsMultiValue.Value(b) + } else { + bRoots = b.blockRoots + sRoots = b.stateRoots + mixes = b.randaoMixes + balances = b.balances + inactivityScores = b.inactivityScores + vals = b.validators + } + marshalable := &beaconStateMarshalable{ Version: b.version, GenesisTime: b.genesisTime, @@ -106,16 +137,16 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots, - StateRoots: b.stateRoots, + BlockRoots: bRoots, + StateRoots: sRoots, HistoricalRoots: b.historicalRoots, HistoricalSummaries: b.historicalSummaries, Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes, + Validators: vals, + Balances: balances, + RandaoMixes: mixes, Slashings: b.slashings, PreviousEpochAttestations: b.previousEpochAttestations, CurrentEpochAttestations: b.currentEpochAttestations, @@ -125,7 +156,7 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, FinalizedCheckpoint: b.finalizedCheckpoint, - InactivityScores: b.inactivityScores, + InactivityScores: inactivityScores, CurrentSyncCommittee: b.currentSyncCommittee, NextSyncCommittee: b.nextSyncCommittee, LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader, diff --git a/beacon-chain/state/state-native/custom-types/block_roots.go b/beacon-chain/state/state-native/custom-types/block_roots.go index 34d81d78aec7..5e7355014556 100644 --- a/beacon-chain/state/state-native/custom-types/block_roots.go +++ b/beacon-chain/state/state-native/custom-types/block_roots.go @@ -7,12 +7,12 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" ) -var _ fssz.HashRoot = (BlockRoots)([fieldparams.BlockRootsLength][32]byte{}) +var _ fssz.HashRoot = (BlockRoots)([][32]byte{}) var _ fssz.Marshaler = (*BlockRoots)(nil) var _ fssz.Unmarshaler = (*BlockRoots)(nil) // BlockRoots represents block roots of the beacon state. -type BlockRoots [fieldparams.BlockRootsLength][32]byte +type BlockRoots [][32]byte // HashTreeRoot returns calculated hash root. func (r BlockRoots) HashTreeRoot() ([32]byte, error) { @@ -35,7 +35,7 @@ func (r *BlockRoots) UnmarshalSSZ(buf []byte) error { return fmt.Errorf("expected buffer of length %d received %d", r.SizeSSZ(), len(buf)) } - var roots BlockRoots + roots := BlockRoots(make([][32]byte, fieldparams.BlockRootsLength)) for i := range roots { copy(roots[i][:], buf[i*32:(i+1)*32]) } @@ -44,7 +44,7 @@ func (r *BlockRoots) UnmarshalSSZ(buf []byte) error { } // MarshalSSZTo marshals BlockRoots with the provided byte slice. -func (r *BlockRoots) MarshalSSZTo(dst []byte) ([]byte, error) { +func (r BlockRoots) MarshalSSZTo(dst []byte) ([]byte, error) { marshalled, err := r.MarshalSSZ() if err != nil { return nil, err @@ -53,7 +53,7 @@ func (r *BlockRoots) MarshalSSZTo(dst []byte) ([]byte, error) { } // MarshalSSZ marshals BlockRoots into a serialized object. -func (r *BlockRoots) MarshalSSZ() ([]byte, error) { +func (r BlockRoots) MarshalSSZ() ([]byte, error) { marshalled := make([]byte, fieldparams.BlockRootsLength*32) for i, r32 := range r { for j, rr := range r32 { @@ -64,12 +64,13 @@ func (r *BlockRoots) MarshalSSZ() ([]byte, error) { } // SizeSSZ returns the size of the serialized object. -func (_ *BlockRoots) SizeSSZ() int { +func (_ BlockRoots) SizeSSZ() int { return fieldparams.BlockRootsLength * 32 } // Slice converts a customtypes.BlockRoots object into a 2D byte slice. -func (r *BlockRoots) Slice() [][]byte { +// Each item in the slice is a copy of the original item. +func (r BlockRoots) Slice() [][]byte { if r == nil { return nil } diff --git a/beacon-chain/state/state-native/custom-types/block_roots_test.go b/beacon-chain/state/state-native/custom-types/block_roots_test.go index 9320f14aca1e..5b1da6d4e797 100644 --- a/beacon-chain/state/state-native/custom-types/block_roots_test.go +++ b/beacon-chain/state/state-native/custom-types/block_roots_test.go @@ -8,14 +8,6 @@ import ( "github.com/prysmaticlabs/prysm/v4/testing/assert" ) -func TestBlockRoots_Casting(t *testing.T) { - var b [fieldparams.BlockRootsLength][32]byte - d := BlockRoots(b) - if !reflect.DeepEqual([fieldparams.BlockRootsLength][32]byte(d), b) { - t.Errorf("Unequal: %v = %v", d, b) - } -} - func TestBlockRoots_UnmarshalSSZ(t *testing.T) { t.Run("Ok", func(t *testing.T) { d := BlockRoots{} @@ -70,7 +62,7 @@ func TestBlockRoots_MarshalSSZTo(t *testing.T) { } func TestBlockRoots_MarshalSSZ(t *testing.T) { - d := BlockRoots{} + d := BlockRoots(make([][32]byte, fieldparams.BlockRootsLength)) d[0] = [32]byte{'f', 'o', 'o'} d[1] = [32]byte{'b', 'a', 'r'} b, err := d.MarshalSSZ() @@ -94,7 +86,7 @@ func TestBlockRoots_SizeSSZ(t *testing.T) { func TestBlockRoots_Slice(t *testing.T) { a, b, c := [32]byte{'a'}, [32]byte{'b'}, [32]byte{'c'} - roots := BlockRoots{} + roots := BlockRoots(make([][32]byte, fieldparams.BlockRootsLength)) roots[1] = a roots[10] = b roots[100] = c diff --git a/beacon-chain/state/state-native/custom-types/historical_roots.go b/beacon-chain/state/state-native/custom-types/historical_roots.go index 92157dfc18b3..9f5d81a8f420 100644 --- a/beacon-chain/state/state-native/custom-types/historical_roots.go +++ b/beacon-chain/state/state-native/custom-types/historical_roots.go @@ -43,7 +43,7 @@ func (r *HistoricalRoots) UnmarshalSSZ(buf []byte) error { } // MarshalSSZTo marshals HistoricalRoots with the provided byte slice. -func (r *HistoricalRoots) MarshalSSZTo(dst []byte) ([]byte, error) { +func (r HistoricalRoots) MarshalSSZTo(dst []byte) ([]byte, error) { marshalled, err := r.MarshalSSZ() if err != nil { return nil, err @@ -52,9 +52,9 @@ func (r *HistoricalRoots) MarshalSSZTo(dst []byte) ([]byte, error) { } // MarshalSSZ marshals HistoricalRoots into a serialized object. -func (r *HistoricalRoots) MarshalSSZ() ([]byte, error) { - marshalled := make([]byte, len(*r)*32) - for i, r32 := range *r { +func (r HistoricalRoots) MarshalSSZ() ([]byte, error) { + marshalled := make([]byte, len(r)*32) + for i, r32 := range r { for j, rr := range r32 { marshalled[i*32+j] = rr } @@ -63,17 +63,17 @@ func (r *HistoricalRoots) MarshalSSZ() ([]byte, error) { } // SizeSSZ returns the size of the serialized object. -func (r *HistoricalRoots) SizeSSZ() int { - return len(*r) * 32 +func (r HistoricalRoots) SizeSSZ() int { + return len(r) * 32 } // Slice converts a customtypes.HistoricalRoots object into a 2D byte slice. -func (r *HistoricalRoots) Slice() [][]byte { +func (r HistoricalRoots) Slice() [][]byte { if r == nil { return nil } - hRoots := make([][]byte, len(*r)) - for i, root := range *r { + hRoots := make([][]byte, len(r)) + for i, root := range r { tmp := root hRoots[i] = tmp[:] } diff --git a/beacon-chain/state/state-native/custom-types/randao_mixes.go b/beacon-chain/state/state-native/custom-types/randao_mixes.go index 324b0839299c..794b6e08d605 100644 --- a/beacon-chain/state/state-native/custom-types/randao_mixes.go +++ b/beacon-chain/state/state-native/custom-types/randao_mixes.go @@ -7,12 +7,12 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" ) -var _ fssz.HashRoot = (RandaoMixes)([fieldparams.RandaoMixesLength][32]byte{}) +var _ fssz.HashRoot = (RandaoMixes)([][32]byte{}) var _ fssz.Marshaler = (*RandaoMixes)(nil) var _ fssz.Unmarshaler = (*RandaoMixes)(nil) // RandaoMixes represents RANDAO mixes of the beacon state. -type RandaoMixes [fieldparams.RandaoMixesLength][32]byte +type RandaoMixes [][32]byte // HashTreeRoot returns calculated hash root. func (r RandaoMixes) HashTreeRoot() ([32]byte, error) { @@ -35,7 +35,7 @@ func (r *RandaoMixes) UnmarshalSSZ(buf []byte) error { return fmt.Errorf("expected buffer of length %d received %d", r.SizeSSZ(), len(buf)) } - var roots RandaoMixes + roots := RandaoMixes(make([][32]byte, fieldparams.RandaoMixesLength)) for i := range roots { copy(roots[i][:], buf[i*32:(i+1)*32]) } @@ -44,7 +44,7 @@ func (r *RandaoMixes) UnmarshalSSZ(buf []byte) error { } // MarshalSSZTo marshals RandaoMixes with the provided byte slice. -func (r *RandaoMixes) MarshalSSZTo(dst []byte) ([]byte, error) { +func (r RandaoMixes) MarshalSSZTo(dst []byte) ([]byte, error) { marshalled, err := r.MarshalSSZ() if err != nil { return nil, err @@ -53,7 +53,7 @@ func (r *RandaoMixes) MarshalSSZTo(dst []byte) ([]byte, error) { } // MarshalSSZ marshals RandaoMixes into a serialized object. -func (r *RandaoMixes) MarshalSSZ() ([]byte, error) { +func (r RandaoMixes) MarshalSSZ() ([]byte, error) { marshalled := make([]byte, fieldparams.RandaoMixesLength*32) for i, r32 := range r { for j, rr := range r32 { @@ -64,12 +64,13 @@ func (r *RandaoMixes) MarshalSSZ() ([]byte, error) { } // SizeSSZ returns the size of the serialized object. -func (_ *RandaoMixes) SizeSSZ() int { +func (_ RandaoMixes) SizeSSZ() int { return fieldparams.RandaoMixesLength * 32 } // Slice converts a customtypes.RandaoMixes object into a 2D byte slice. -func (r *RandaoMixes) Slice() [][]byte { +// Each item in the slice is a copy of the original item. +func (r RandaoMixes) Slice() [][]byte { if r == nil { return nil } diff --git a/beacon-chain/state/state-native/custom-types/randao_mixes_test.go b/beacon-chain/state/state-native/custom-types/randao_mixes_test.go index 73732e3f936a..3680a8d4e0a2 100644 --- a/beacon-chain/state/state-native/custom-types/randao_mixes_test.go +++ b/beacon-chain/state/state-native/custom-types/randao_mixes_test.go @@ -8,14 +8,6 @@ import ( "github.com/prysmaticlabs/prysm/v4/testing/assert" ) -func TestRandaoMixes_Casting(t *testing.T) { - var b [fieldparams.RandaoMixesLength][32]byte - d := RandaoMixes(b) - if !reflect.DeepEqual([fieldparams.RandaoMixesLength][32]byte(d), b) { - t.Errorf("Unequal: %v = %v", d, b) - } -} - func TestRandaoMixes_UnmarshalSSZ(t *testing.T) { t.Run("Ok", func(t *testing.T) { d := RandaoMixes{} @@ -70,7 +62,7 @@ func TestRandaoMixes_MarshalSSZTo(t *testing.T) { } func TestRandaoMixes_MarshalSSZ(t *testing.T) { - d := RandaoMixes{} + d := RandaoMixes(make([][32]byte, fieldparams.RandaoMixesLength)) d[0] = [32]byte{'f', 'o', 'o'} d[1] = [32]byte{'b', 'a', 'r'} b, err := d.MarshalSSZ() @@ -94,7 +86,7 @@ func TestRandaoMixes_SizeSSZ(t *testing.T) { func TestRandaoMixes_Slice(t *testing.T) { a, b, c := [32]byte{'a'}, [32]byte{'b'}, [32]byte{'c'} - roots := RandaoMixes{} + roots := RandaoMixes(make([][32]byte, fieldparams.RandaoMixesLength)) roots[1] = a roots[10] = b roots[100] = c diff --git a/beacon-chain/state/state-native/custom-types/state_roots.go b/beacon-chain/state/state-native/custom-types/state_roots.go index 134b271f6c3e..8be1141fd7ad 100644 --- a/beacon-chain/state/state-native/custom-types/state_roots.go +++ b/beacon-chain/state/state-native/custom-types/state_roots.go @@ -7,12 +7,12 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" ) -var _ fssz.HashRoot = (StateRoots)([fieldparams.StateRootsLength][32]byte{}) +var _ fssz.HashRoot = (StateRoots)([][32]byte{}) var _ fssz.Marshaler = (*StateRoots)(nil) var _ fssz.Unmarshaler = (*StateRoots)(nil) // StateRoots represents block roots of the beacon state. -type StateRoots [fieldparams.StateRootsLength][32]byte +type StateRoots [][32]byte // HashTreeRoot returns calculated hash root. func (r StateRoots) HashTreeRoot() ([32]byte, error) { @@ -35,7 +35,7 @@ func (r *StateRoots) UnmarshalSSZ(buf []byte) error { return fmt.Errorf("expected buffer of length %d received %d", r.SizeSSZ(), len(buf)) } - var roots StateRoots + roots := StateRoots(make([][32]byte, fieldparams.StateRootsLength)) for i := range roots { copy(roots[i][:], buf[i*32:(i+1)*32]) } @@ -44,7 +44,7 @@ func (r *StateRoots) UnmarshalSSZ(buf []byte) error { } // MarshalSSZTo marshals StateRoots with the provided byte slice. -func (r *StateRoots) MarshalSSZTo(dst []byte) ([]byte, error) { +func (r StateRoots) MarshalSSZTo(dst []byte) ([]byte, error) { marshalled, err := r.MarshalSSZ() if err != nil { return nil, err @@ -53,7 +53,7 @@ func (r *StateRoots) MarshalSSZTo(dst []byte) ([]byte, error) { } // MarshalSSZ marshals StateRoots into a serialized object. -func (r *StateRoots) MarshalSSZ() ([]byte, error) { +func (r StateRoots) MarshalSSZ() ([]byte, error) { marshalled := make([]byte, fieldparams.StateRootsLength*32) for i, r32 := range r { for j, rr := range r32 { @@ -64,12 +64,13 @@ func (r *StateRoots) MarshalSSZ() ([]byte, error) { } // SizeSSZ returns the size of the serialized object. -func (_ *StateRoots) SizeSSZ() int { +func (_ StateRoots) SizeSSZ() int { return fieldparams.StateRootsLength * 32 } // Slice converts a customtypes.StateRoots object into a 2D byte slice. -func (r *StateRoots) Slice() [][]byte { +// Each item in the slice is a copy of the original item. +func (r StateRoots) Slice() [][]byte { if r == nil { return nil } diff --git a/beacon-chain/state/state-native/custom-types/state_roots_test.go b/beacon-chain/state/state-native/custom-types/state_roots_test.go index 51d7f582c7bd..9b150bd9c8d3 100644 --- a/beacon-chain/state/state-native/custom-types/state_roots_test.go +++ b/beacon-chain/state/state-native/custom-types/state_roots_test.go @@ -8,14 +8,6 @@ import ( "github.com/prysmaticlabs/prysm/v4/testing/assert" ) -func TestStateRoots_Casting(t *testing.T) { - var b [fieldparams.StateRootsLength][32]byte - d := StateRoots(b) - if !reflect.DeepEqual([fieldparams.StateRootsLength][32]byte(d), b) { - t.Errorf("Unequal: %v = %v", d, b) - } -} - func TestStateRoots_UnmarshalSSZ(t *testing.T) { t.Run("Ok", func(t *testing.T) { d := StateRoots{} @@ -70,7 +62,7 @@ func TestStateRoots_MarshalSSZTo(t *testing.T) { } func TestStateRoots_MarshalSSZ(t *testing.T) { - d := StateRoots{} + d := StateRoots(make([][32]byte, fieldparams.StateRootsLength)) d[0] = [32]byte{'f', 'o', 'o'} d[1] = [32]byte{'b', 'a', 'r'} b, err := d.MarshalSSZ() @@ -94,7 +86,7 @@ func TestStateRoots_SizeSSZ(t *testing.T) { func TestStateRoots_Slice(t *testing.T) { a, b, c := [32]byte{'a'}, [32]byte{'b'}, [32]byte{'c'} - roots := StateRoots{} + roots := StateRoots(make([][32]byte, fieldparams.StateRootsLength)) roots[1] = a roots[10] = b roots[100] = c diff --git a/beacon-chain/state/state-native/getters_block.go b/beacon-chain/state/state-native/getters_block.go index 5d1c6388c175..aaf39b6ed7e2 100644 --- a/beacon-chain/state/state-native/getters_block.go +++ b/beacon-chain/state/state-native/getters_block.go @@ -1,8 +1,10 @@ package state_native import ( - "fmt" - + "github.com/pkg/errors" + customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" + "github.com/prysmaticlabs/prysm/v4/config/features" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) @@ -45,26 +47,46 @@ func (b *BeaconState) latestBlockHeaderVal() *ethpb.BeaconBlockHeader { // BlockRoots kept track of in the beacon state. func (b *BeaconState) BlockRoots() [][]byte { - if b.blockRoots == nil { - return nil - } - b.lock.RLock() defer b.lock.RUnlock() - return b.blockRoots.Slice() + roots := b.blockRootsVal() + if roots == nil { + return nil + } + return roots.Slice() +} + +func (b *BeaconState) blockRootsVal() customtypes.BlockRoots { + if features.Get().EnableExperimentalState { + if b.blockRootsMultiValue == nil { + return nil + } + return b.blockRootsMultiValue.Value(b) + } + return b.blockRoots } // BlockRootAtIndex retrieves a specific block root based on an // input index value. func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) { - if b.blockRoots == nil { - return []byte{}, nil - } - b.lock.RLock() defer b.lock.RUnlock() + if features.Get().EnableExperimentalState { + if b.blockRootsMultiValue == nil { + return []byte{}, nil + } + r, err := b.blockRootsMultiValue.At(b, idx) + if err != nil { + return nil, err + } + return r[:], nil + } + + if b.blockRoots == nil { + return []byte{}, nil + } r, err := b.blockRootAtIndex(idx) if err != nil { return nil, err @@ -77,7 +99,7 @@ func (b *BeaconState) BlockRootAtIndex(idx uint64) ([]byte, error) { // This assumes that a lock is already held on BeaconState. func (b *BeaconState) blockRootAtIndex(idx uint64) ([32]byte, error) { if uint64(len(b.blockRoots)) <= idx { - return [32]byte{}, fmt.Errorf("index %d out of range", idx) + return [32]byte{}, errors.Wrapf(consensus_types.ErrOutOfBounds, "block root index %d does not exist", idx) } return b.blockRoots[idx], nil } diff --git a/beacon-chain/state/state-native/getters_misc.go b/beacon-chain/state/state-native/getters_misc.go index 5ae1d68b61d1..8315cc471b66 100644 --- a/beacon-chain/state/state-native/getters_misc.go +++ b/beacon-chain/state/state-native/getters_misc.go @@ -6,6 +6,11 @@ import ( "github.com/prysmaticlabs/prysm/v4/runtime/version" ) +// Id is the identifier of the beacon state. +func (b *BeaconState) Id() uint64 { + return b.id +} + // GenesisTime of the beacon state as a uint64. func (b *BeaconState) GenesisTime() uint64 { b.lock.RLock() @@ -79,16 +84,6 @@ func (b *BeaconState) HistoricalRoots() ([][]byte, error) { return b.historicalRoots.Slice(), nil } -// balancesLength returns the length of the balances slice. -// This assumes that a lock is already held on BeaconState. -func (b *BeaconState) balancesLength() int { - if b.balances == nil { - return 0 - } - - return len(b.balances) -} - // HistoricalSummaries of the beacon state. func (b *BeaconState) HistoricalSummaries() ([]*ethpb.HistoricalSummary, error) { if b.version < version.Capella { diff --git a/beacon-chain/state/state-native/getters_participation.go b/beacon-chain/state/state-native/getters_participation.go index 54ee45527f21..353ae7b69fad 100644 --- a/beacon-chain/state/state-native/getters_participation.go +++ b/beacon-chain/state/state-native/getters_participation.go @@ -3,6 +3,7 @@ package state_native import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" "github.com/prysmaticlabs/prysm/v4/runtime/version" ) @@ -56,7 +57,11 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er return 0, 0, 0, ErrNilParticipation } - return stateutil.UnrealizedCheckpointBalances(cp, pp, b.validators, currentEpoch) + if features.Get().EnableExperimentalState { + return stateutil.UnrealizedCheckpointBalances(cp, pp, b.validatorsVal(), currentEpoch) + } else { + return stateutil.UnrealizedCheckpointBalances(cp, pp, b.validators, currentEpoch) + } } // currentEpochParticipationVal corresponding to participation bits on the beacon chain. diff --git a/beacon-chain/state/state-native/getters_randao.go b/beacon-chain/state/state-native/getters_randao.go index 9f6de01de048..5a60db047301 100644 --- a/beacon-chain/state/state-native/getters_randao.go +++ b/beacon-chain/state/state-native/getters_randao.go @@ -1,31 +1,54 @@ package state_native import ( - "fmt" + "github.com/pkg/errors" + customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" + "github.com/prysmaticlabs/prysm/v4/config/features" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ) // RandaoMixes of block proposers on the beacon chain. func (b *BeaconState) RandaoMixes() [][]byte { - if b.randaoMixes == nil { - return nil - } - b.lock.RLock() defer b.lock.RUnlock() - return b.randaoMixes.Slice() + mixes := b.randaoMixesVal() + if mixes == nil { + return nil + } + return mixes.Slice() +} + +func (b *BeaconState) randaoMixesVal() customtypes.RandaoMixes { + if features.Get().EnableExperimentalState { + if b.randaoMixesMultiValue == nil { + return nil + } + return b.randaoMixesMultiValue.Value(b) + } + return b.randaoMixes } // RandaoMixAtIndex retrieves a specific block root based on an // input index value. func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) { - if b.randaoMixes == nil { - return nil, nil - } - b.lock.RLock() defer b.lock.RUnlock() + if features.Get().EnableExperimentalState { + if b.randaoMixesMultiValue == nil { + return nil, nil + } + r, err := b.randaoMixesMultiValue.At(b, idx) + if err != nil { + return nil, err + } + return r[:], nil + } + + if b.randaoMixes == nil { + return nil, nil + } m, err := b.randaoMixAtIndex(idx) if err != nil { return nil, err @@ -38,7 +61,7 @@ func (b *BeaconState) RandaoMixAtIndex(idx uint64) ([]byte, error) { // This assumes that a lock is already held on BeaconState. func (b *BeaconState) randaoMixAtIndex(idx uint64) ([32]byte, error) { if uint64(len(b.randaoMixes)) <= idx { - return [32]byte{}, fmt.Errorf("index %d out of range", idx) + return [32]byte{}, errors.Wrapf(consensus_types.ErrOutOfBounds, "randao mix index %d does not exist", idx) } return b.randaoMixes[idx], nil @@ -46,22 +69,17 @@ func (b *BeaconState) randaoMixAtIndex(idx uint64) ([32]byte, error) { // RandaoMixesLength returns the length of the randao mixes slice. func (b *BeaconState) RandaoMixesLength() int { - if b.randaoMixes == nil { - return 0 - } - b.lock.RLock() defer b.lock.RUnlock() - return b.randaoMixesLength() -} - -// randaoMixesLength returns the length of the randao mixes slice. -// This assumes that a lock is already held on BeaconState. -func (b *BeaconState) randaoMixesLength() int { + if features.Get().EnableExperimentalState { + if b.randaoMixesMultiValue == nil { + return 0 + } + return b.randaoMixesMultiValue.Len(b) + } if b.randaoMixes == nil { return 0 } - return len(b.randaoMixes) } diff --git a/beacon-chain/state/state-native/getters_state.go b/beacon-chain/state/state-native/getters_state.go index 7e0019e656b9..1e83c594c5c9 100644 --- a/beacon-chain/state/state-native/getters_state.go +++ b/beacon-chain/state/state-native/getters_state.go @@ -1,9 +1,10 @@ package state_native import ( - "fmt" - "github.com/pkg/errors" + customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" + "github.com/prysmaticlabs/prysm/v4/config/features" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/runtime/version" ) @@ -16,6 +17,18 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { } gvrCopy := b.genesisValidatorsRoot + br := b.blockRootsVal().Slice() + sr := b.stateRootsVal().Slice() + rm := b.randaoMixesVal().Slice() + var vals []*ethpb.Validator + var bals []uint64 + if features.Get().EnableExperimentalState { + vals = b.validatorsVal() + bals = b.balancesVal() + } else { + vals = b.validators + bals = b.balances + } switch b.version { case version.Phase0: @@ -25,15 +38,15 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes.Slice(), + Validators: vals, + Balances: bals, + RandaoMixes: rm, Slashings: b.slashings, PreviousEpochAttestations: b.previousEpochAttestations, CurrentEpochAttestations: b.currentEpochAttestations, @@ -49,15 +62,15 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes.Slice(), + Validators: vals, + Balances: bals, + RandaoMixes: rm, Slashings: b.slashings, PreviousEpochParticipation: b.previousEpochParticipation, CurrentEpochParticipation: b.currentEpochParticipation, @@ -65,7 +78,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, FinalizedCheckpoint: b.finalizedCheckpoint, - InactivityScores: b.inactivityScores, + InactivityScores: b.inactivityScoresVal(), CurrentSyncCommittee: b.currentSyncCommittee, NextSyncCommittee: b.nextSyncCommittee, } @@ -76,15 +89,15 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes.Slice(), + Validators: vals, + Balances: bals, + RandaoMixes: rm, Slashings: b.slashings, PreviousEpochParticipation: b.previousEpochParticipation, CurrentEpochParticipation: b.currentEpochParticipation, @@ -92,7 +105,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, FinalizedCheckpoint: b.finalizedCheckpoint, - InactivityScores: b.inactivityScores, + InactivityScores: b.inactivityScoresVal(), CurrentSyncCommittee: b.currentSyncCommittee, NextSyncCommittee: b.nextSyncCommittee, LatestExecutionPayloadHeader: b.latestExecutionPayloadHeader, @@ -104,15 +117,15 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes.Slice(), + Validators: vals, + Balances: bals, + RandaoMixes: rm, Slashings: b.slashings, PreviousEpochParticipation: b.previousEpochParticipation, CurrentEpochParticipation: b.currentEpochParticipation, @@ -120,7 +133,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, FinalizedCheckpoint: b.finalizedCheckpoint, - InactivityScores: b.inactivityScores, + InactivityScores: b.inactivityScoresVal(), CurrentSyncCommittee: b.currentSyncCommittee, NextSyncCommittee: b.nextSyncCommittee, LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderCapella, @@ -135,15 +148,15 @@ func (b *BeaconState) ToProtoUnsafe() interface{} { Slot: b.slot, Fork: b.fork, LatestBlockHeader: b.latestBlockHeader, - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1Data, Eth1DataVotes: b.eth1DataVotes, Eth1DepositIndex: b.eth1DepositIndex, - Validators: b.validators, - Balances: b.balances, - RandaoMixes: b.randaoMixes.Slice(), + Validators: vals, + Balances: bals, + RandaoMixes: rm, Slashings: b.slashings, PreviousEpochParticipation: b.previousEpochParticipation, CurrentEpochParticipation: b.currentEpochParticipation, @@ -174,6 +187,14 @@ func (b *BeaconState) ToProto() interface{} { defer b.lock.RUnlock() gvrCopy := b.genesisValidatorsRoot + br := b.blockRootsVal().Slice() + sr := b.stateRootsVal().Slice() + rm := b.randaoMixesVal().Slice() + + var inactivityScores []uint64 + if b.version > version.Phase0 { + inactivityScores = b.inactivityScoresVal() + } switch b.version { case version.Phase0: @@ -183,15 +204,15 @@ func (b *BeaconState) ToProto() interface{} { Slot: b.slot, Fork: b.forkVal(), LatestBlockHeader: b.latestBlockHeaderVal(), - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1DataVal(), Eth1DataVotes: b.eth1DataVotesVal(), Eth1DepositIndex: b.eth1DepositIndex, Validators: b.validatorsVal(), Balances: b.balancesVal(), - RandaoMixes: b.randaoMixes.Slice(), + RandaoMixes: rm, Slashings: b.slashingsVal(), PreviousEpochAttestations: b.previousEpochAttestationsVal(), CurrentEpochAttestations: b.currentEpochAttestationsVal(), @@ -207,15 +228,15 @@ func (b *BeaconState) ToProto() interface{} { Slot: b.slot, Fork: b.forkVal(), LatestBlockHeader: b.latestBlockHeaderVal(), - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1DataVal(), Eth1DataVotes: b.eth1DataVotesVal(), Eth1DepositIndex: b.eth1DepositIndex, Validators: b.validatorsVal(), Balances: b.balancesVal(), - RandaoMixes: b.randaoMixes.Slice(), + RandaoMixes: rm, Slashings: b.slashingsVal(), PreviousEpochParticipation: b.previousEpochParticipationVal(), CurrentEpochParticipation: b.currentEpochParticipationVal(), @@ -223,7 +244,7 @@ func (b *BeaconState) ToProto() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(), CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(), FinalizedCheckpoint: b.finalizedCheckpointVal(), - InactivityScores: b.inactivityScoresVal(), + InactivityScores: inactivityScores, CurrentSyncCommittee: b.currentSyncCommitteeVal(), NextSyncCommittee: b.nextSyncCommitteeVal(), } @@ -234,15 +255,15 @@ func (b *BeaconState) ToProto() interface{} { Slot: b.slot, Fork: b.forkVal(), LatestBlockHeader: b.latestBlockHeaderVal(), - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1DataVal(), Eth1DataVotes: b.eth1DataVotesVal(), Eth1DepositIndex: b.eth1DepositIndex, Validators: b.validatorsVal(), Balances: b.balancesVal(), - RandaoMixes: b.randaoMixes.Slice(), + RandaoMixes: rm, Slashings: b.slashingsVal(), PreviousEpochParticipation: b.previousEpochParticipationVal(), CurrentEpochParticipation: b.currentEpochParticipationVal(), @@ -250,7 +271,7 @@ func (b *BeaconState) ToProto() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(), CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(), FinalizedCheckpoint: b.finalizedCheckpointVal(), - InactivityScores: b.inactivityScoresVal(), + InactivityScores: inactivityScores, CurrentSyncCommittee: b.currentSyncCommitteeVal(), NextSyncCommittee: b.nextSyncCommitteeVal(), LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderVal(), @@ -262,15 +283,15 @@ func (b *BeaconState) ToProto() interface{} { Slot: b.slot, Fork: b.forkVal(), LatestBlockHeader: b.latestBlockHeaderVal(), - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1DataVal(), Eth1DataVotes: b.eth1DataVotesVal(), Eth1DepositIndex: b.eth1DepositIndex, Validators: b.validatorsVal(), Balances: b.balancesVal(), - RandaoMixes: b.randaoMixes.Slice(), + RandaoMixes: rm, Slashings: b.slashingsVal(), PreviousEpochParticipation: b.previousEpochParticipationVal(), CurrentEpochParticipation: b.currentEpochParticipationVal(), @@ -278,7 +299,7 @@ func (b *BeaconState) ToProto() interface{} { PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(), CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(), FinalizedCheckpoint: b.finalizedCheckpointVal(), - InactivityScores: b.inactivityScoresVal(), + InactivityScores: inactivityScores, CurrentSyncCommittee: b.currentSyncCommitteeVal(), NextSyncCommittee: b.nextSyncCommitteeVal(), LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderCapellaVal(), @@ -293,15 +314,15 @@ func (b *BeaconState) ToProto() interface{} { Slot: b.slot, Fork: b.forkVal(), LatestBlockHeader: b.latestBlockHeaderVal(), - BlockRoots: b.blockRoots.Slice(), - StateRoots: b.stateRoots.Slice(), + BlockRoots: br, + StateRoots: sr, HistoricalRoots: b.historicalRoots.Slice(), Eth1Data: b.eth1DataVal(), Eth1DataVotes: b.eth1DataVotesVal(), Eth1DepositIndex: b.eth1DepositIndex, Validators: b.validatorsVal(), Balances: b.balancesVal(), - RandaoMixes: b.randaoMixes.Slice(), + RandaoMixes: rm, Slashings: b.slashingsVal(), PreviousEpochParticipation: b.previousEpochParticipationVal(), CurrentEpochParticipation: b.currentEpochParticipationVal(), @@ -324,26 +345,46 @@ func (b *BeaconState) ToProto() interface{} { // StateRoots kept track of in the beacon state. func (b *BeaconState) StateRoots() [][]byte { - if b.stateRoots == nil { - return nil - } - b.lock.RLock() defer b.lock.RUnlock() - return b.stateRoots.Slice() + roots := b.stateRootsVal() + if roots == nil { + return nil + } + return roots.Slice() +} + +func (b *BeaconState) stateRootsVal() customtypes.StateRoots { + if features.Get().EnableExperimentalState { + if b.stateRootsMultiValue == nil { + return nil + } + return b.stateRootsMultiValue.Value(b) + } + return b.stateRoots } // StateRootAtIndex retrieves a specific state root based on an // input index value. func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) { - if b.stateRoots == nil { - return nil, nil - } - b.lock.RLock() defer b.lock.RUnlock() + if features.Get().EnableExperimentalState { + if b.stateRootsMultiValue == nil { + return nil, nil + } + r, err := b.stateRootsMultiValue.At(b, idx) + if err != nil { + return nil, err + } + return r[:], nil + } + + if b.stateRoots == nil { + return nil, nil + } r, err := b.stateRootAtIndex(idx) if err != nil { return nil, err @@ -354,9 +395,11 @@ func (b *BeaconState) StateRootAtIndex(idx uint64) ([]byte, error) { // stateRootAtIndex retrieves a specific state root based on an // input index value. // This assumes that a lock is already held on BeaconState. +// +// WARNING: This function does not work with the multi-value slice feature. func (b *BeaconState) stateRootAtIndex(idx uint64) ([32]byte, error) { if uint64(len(b.stateRoots)) <= idx { - return [32]byte{}, fmt.Errorf("index %d out of range", idx) + return [32]byte{}, errors.Wrapf(consensus_types.ErrOutOfBounds, "state root index %d does not exist", idx) } return b.stateRoots[idx], nil } diff --git a/beacon-chain/state/state-native/getters_validator.go b/beacon-chain/state/state-native/getters_validator.go index 9e777b0bb572..2e36becdaa37 100644 --- a/beacon-chain/state/state-native/getters_validator.go +++ b/beacon-chain/state/state-native/getters_validator.go @@ -1,57 +1,42 @@ package state_native import ( - "fmt" - "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state" + "github.com/prysmaticlabs/prysm/v4/config/features" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/runtime/version" ) -// ValidatorIndexOutOfRangeError represents an error scenario where a validator does not exist -// at a given index in the validator's array. -type ValidatorIndexOutOfRangeError struct { - message string -} - -// NewValidatorIndexOutOfRangeError creates a new error instance. -func NewValidatorIndexOutOfRangeError(index primitives.ValidatorIndex) ValidatorIndexOutOfRangeError { - return ValidatorIndexOutOfRangeError{ - message: fmt.Sprintf("index %d out of range", index), - } -} - -// Error returns the underlying error message. -func (e *ValidatorIndexOutOfRangeError) Error() string { - return e.message -} - // Validators participating in consensus on the beacon chain. func (b *BeaconState) Validators() []*ethpb.Validator { - if b.validators == nil { - return nil - } - b.lock.RLock() defer b.lock.RUnlock() return b.validatorsVal() } -// validatorsVal participating in consensus on the beacon chain. -// This assumes that a lock is already held on BeaconState. func (b *BeaconState) validatorsVal() []*ethpb.Validator { - if b.validators == nil { - return nil + var v []*ethpb.Validator + if features.Get().EnableExperimentalState { + if b.validatorsMultiValue == nil { + return nil + } + v = b.validatorsMultiValue.Value(b) + } else { + if b.validators == nil { + return nil + } + v = b.validators } - res := make([]*ethpb.Validator, len(b.validators)) + res := make([]*ethpb.Validator, len(v)) for i := 0; i < len(res); i++ { - val := b.validators[i] + val := v[i] if val == nil { continue } @@ -80,19 +65,42 @@ func (b *BeaconState) validatorsReferences() []*ethpb.Validator { return res } +func (b *BeaconState) validatorsLen() int { + if features.Get().EnableExperimentalState { + if b.validatorsMultiValue == nil { + return 0 + } + return b.validatorsMultiValue.Len(b) + } + return len(b.validators) +} + // ValidatorAtIndex is the validator at the provided index. func (b *BeaconState) ValidatorAtIndex(idx primitives.ValidatorIndex) (*ethpb.Validator, error) { + b.lock.RLock() + defer b.lock.RUnlock() + + return b.validatorAtIndex(idx) +} + +func (b *BeaconState) validatorAtIndex(idx primitives.ValidatorIndex) (*ethpb.Validator, error) { + if features.Get().EnableExperimentalState { + if b.validatorsMultiValue == nil { + return ðpb.Validator{}, nil + } + v, err := b.validatorsMultiValue.At(b, uint64(idx)) + if err != nil { + return nil, err + } + return ethpb.CopyValidator(v), nil + } + if b.validators == nil { return ðpb.Validator{}, nil } if uint64(len(b.validators)) <= uint64(idx) { - e := NewValidatorIndexOutOfRangeError(idx) - return nil, &e + return nil, errors.Wrapf(consensus_types.ErrOutOfBounds, "validator index %d does not exist", idx) } - - b.lock.RLock() - defer b.lock.RUnlock() - val := b.validators[idx] return ethpb.CopyValidator(val), nil } @@ -100,18 +108,28 @@ func (b *BeaconState) ValidatorAtIndex(idx primitives.ValidatorIndex) (*ethpb.Va // ValidatorAtIndexReadOnly is the validator at the provided index. This method // doesn't clone the validator. func (b *BeaconState) ValidatorAtIndexReadOnly(idx primitives.ValidatorIndex) (state.ReadOnlyValidator, error) { + b.lock.RLock() + defer b.lock.RUnlock() + + if features.Get().EnableExperimentalState { + if b.validatorsMultiValue == nil { + return nil, state.ErrNilValidatorsInState + } + v, err := b.validatorsMultiValue.At(b, uint64(idx)) + if err != nil { + return nil, err + } + return NewValidator(v) + } + if b.validators == nil { return nil, state.ErrNilValidatorsInState } if uint64(len(b.validators)) <= uint64(idx) { - e := NewValidatorIndexOutOfRangeError(idx) - return nil, &e + return nil, errors.Wrapf(consensus_types.ErrOutOfBounds, "validator index %d does not exist", idx) } - - b.lock.RLock() - defer b.lock.RUnlock() - - return NewValidator(b.validators[idx]) + val := b.validators[idx] + return NewValidator(val) } // ValidatorIndexByPubkey returns a given validator by its 48-byte public key. @@ -121,7 +139,13 @@ func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]by } b.lock.RLock() defer b.lock.RUnlock() - numOfVals := len(b.validators) + + var numOfVals int + if features.Get().EnableExperimentalState { + numOfVals = b.validatorsMultiValue.Len(b) + } else { + numOfVals = len(b.validators) + } idx, ok := b.valMapHandler.Get(key) if ok && primitives.ValidatorIndex(numOfVals) <= idx { @@ -133,16 +157,27 @@ func (b *BeaconState) ValidatorIndexByPubkey(key [fieldparams.BLSPubkeyLength]by // PubkeyAtIndex returns the pubkey at the given // validator index. func (b *BeaconState) PubkeyAtIndex(idx primitives.ValidatorIndex) [fieldparams.BLSPubkeyLength]byte { - if uint64(idx) >= uint64(len(b.validators)) { - return [fieldparams.BLSPubkeyLength]byte{} - } b.lock.RLock() defer b.lock.RUnlock() - if b.validators[idx] == nil { + var v *ethpb.Validator + if features.Get().EnableExperimentalState { + var err error + v, err = b.validatorsMultiValue.At(b, uint64(idx)) + if err != nil { + return [fieldparams.BLSPubkeyLength]byte{} + } + } else { + if uint64(idx) >= uint64(len(b.validators)) { + return [fieldparams.BLSPubkeyLength]byte{} + } + v = b.validators[idx] + } + + if v == nil { return [fieldparams.BLSPubkeyLength]byte{} } - return bytesutil.ToBytes48(b.validators[idx].PublicKey) + return bytesutil.ToBytes48(v.PublicKey) } // NumValidators returns the size of the validator registry. @@ -150,51 +185,78 @@ func (b *BeaconState) NumValidators() int { b.lock.RLock() defer b.lock.RUnlock() - return len(b.validators) + return b.validatorsLen() } // 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. func (b *BeaconState) ReadFromEveryValidator(f func(idx int, val state.ReadOnlyValidator) error) error { + b.lock.RLock() + defer b.lock.RUnlock() + + if features.Get().EnableExperimentalState { + return b.readFromEveryValidatorMVSlice(f) + } + if b.validators == nil { - return errors.New("nil validators in state") + return state.ErrNilValidatorsInState } - b.lock.RLock() + validators := b.validators - b.lock.RUnlock() for i, v := range validators { v, err := NewValidator(v) if err != nil { return err } - if err := f(i, v); err != nil { + if err = f(i, v); err != nil { return err } } return nil } -// Balances of validators participating in consensus on the beacon chain. -func (b *BeaconState) Balances() []uint64 { - if b.balances == nil { - return nil +// WARNING: This function works only for the multi-value slice feature. +func (b *BeaconState) readFromEveryValidatorMVSlice(f func(idx int, val state.ReadOnlyValidator) error) error { + if b.validatorsMultiValue == nil { + return state.ErrNilValidatorsInState + } + l := b.validatorsMultiValue.Len(b) + for i := 0; i < l; i++ { + v, err := b.validatorsMultiValue.At(b, uint64(i)) + if err != nil { + return err + } + rov, err := NewValidator(v) + if err != nil { + return err + } + if err = f(i, rov); err != nil { + return err + } } + return nil +} +// Balances of validators participating in consensus on the beacon chain. +func (b *BeaconState) Balances() []uint64 { b.lock.RLock() defer b.lock.RUnlock() return b.balancesVal() } -// balancesVal of validators participating in consensus on the beacon chain. -// This assumes that a lock is already held on BeaconState. func (b *BeaconState) balancesVal() []uint64 { + if features.Get().EnableExperimentalState { + if b.balancesMultiValue == nil { + return nil + } + return b.balancesMultiValue.Value(b) + } if b.balances == nil { return nil } - res := make([]uint64, len(b.balances)) copy(res, b.balances) return res @@ -202,29 +264,40 @@ func (b *BeaconState) balancesVal() []uint64 { // BalanceAtIndex of validator with the provided index. func (b *BeaconState) BalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error) { - if b.balances == nil { - return 0, nil - } - b.lock.RLock() defer b.lock.RUnlock() + return b.balanceAtIndex(idx) +} + +func (b *BeaconState) balanceAtIndex(idx primitives.ValidatorIndex) (uint64, error) { + if features.Get().EnableExperimentalState { + if b.balancesMultiValue == nil { + return 0, nil + } + return b.balancesMultiValue.At(b, uint64(idx)) + } + if b.balances == nil { + return 0, nil + } if uint64(len(b.balances)) <= uint64(idx) { - return 0, fmt.Errorf("index of %d does not exist", idx) + return 0, errors.Wrapf(consensus_types.ErrOutOfBounds, "balance index %d does not exist", idx) } return b.balances[idx], nil } // BalancesLength returns the length of the balances slice. func (b *BeaconState) BalancesLength() int { - if b.balances == nil { - return 0 - } - b.lock.RLock() defer b.lock.RUnlock() - return b.balancesLength() + if features.Get().EnableExperimentalState { + if b.balancesMultiValue == nil { + return 0 + } + return b.balancesMultiValue.Len(b) + } + return len(b.balances) } // Slashings of validators on the beacon chain. @@ -257,23 +330,22 @@ func (b *BeaconState) InactivityScores() ([]uint64, error) { return nil, errNotSupported("InactivityScores", b.version) } - if b.inactivityScores == nil { - return nil, nil - } - b.lock.RLock() defer b.lock.RUnlock() return b.inactivityScoresVal(), nil } -// inactivityScoresVal of validators participating in consensus on the beacon chain. -// This assumes that a lock is already held on BeaconState. func (b *BeaconState) inactivityScoresVal() []uint64 { + if features.Get().EnableExperimentalState { + if b.inactivityScoresMultiValue == nil { + return nil + } + return b.inactivityScoresMultiValue.Value(b) + } if b.inactivityScores == nil { return nil } - res := make([]uint64, len(b.inactivityScores)) copy(res, b.inactivityScores) return res diff --git a/beacon-chain/state/state-native/getters_validator_test.go b/beacon-chain/state/state-native/getters_validator_test.go index c566bc08eb12..e494ad4811f3 100644 --- a/beacon-chain/state/state-native/getters_validator_test.go +++ b/beacon-chain/state/state-native/getters_validator_test.go @@ -52,11 +52,6 @@ func TestBeaconState_ValidatorAtIndexReadOnly_HandlesNilSlice_Deneb(t *testing.T }) } -func TestValidatorIndexOutOfRangeError(t *testing.T) { - err := statenative.NewValidatorIndexOutOfRangeError(1) - require.Equal(t, err.Error(), "index 1 out of range") -} - func TestValidatorIndexes(t *testing.T) { dState, _ := util.DeterministicGenesisState(t, 10) byteValue := dState.PubkeyAtIndex(1) diff --git a/beacon-chain/state/state-native/getters_withdrawal.go b/beacon-chain/state/state-native/getters_withdrawal.go index 5bbed241973d..21ee4b7a8e8d 100644 --- a/beacon-chain/state/state-native/getters_withdrawal.go +++ b/beacon-chain/state/state-native/getters_withdrawal.go @@ -1,6 +1,7 @@ package state_native import ( + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" @@ -54,10 +55,17 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) { withdrawalIndex := b.nextWithdrawalIndex epoch := slots.ToEpoch(b.slot) - bound := mathutil.Min(uint64(len(b.validators)), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) + validatorsLen := b.validatorsLen() + bound := mathutil.Min(uint64(validatorsLen), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) for i := uint64(0); i < bound; i++ { - val := b.validators[validatorIndex] - balance := b.balances[validatorIndex] + val, err := b.validatorAtIndex(validatorIndex) + if err != nil { + return nil, 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) + } if balance > 0 && isFullyWithdrawableValidator(val, epoch) { withdrawals = append(withdrawals, &enginev1.Withdrawal{ Index: withdrawalIndex, @@ -79,7 +87,7 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) { break } validatorIndex += 1 - if uint64(validatorIndex) == uint64(len(b.validators)) { + if uint64(validatorIndex) == uint64(validatorsLen) { validatorIndex = 0 } } diff --git a/beacon-chain/state/state-native/hasher.go b/beacon-chain/state/state-native/hasher.go index 21b833d0f117..07c6c777830d 100644 --- a/beacon-chain/state/state-native/hasher.go +++ b/beacon-chain/state/state-native/hasher.go @@ -65,22 +65,14 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b fieldRoots[types.LatestBlockHeader.RealPosition()] = headerHashTreeRoot[:] // BlockRoots array root. - bRoots := make([][]byte, len(state.blockRoots)) - for i := range bRoots { - bRoots[i] = state.blockRoots[i][:] - } - blockRootsRoot, err := stateutil.ArraysRoot(bRoots, fieldparams.BlockRootsLength) + blockRootsRoot, err := stateutil.ArraysRoot(state.blockRootsVal().Slice(), fieldparams.BlockRootsLength) if err != nil { return nil, errors.Wrap(err, "could not compute block roots merkleization") } fieldRoots[types.BlockRoots.RealPosition()] = blockRootsRoot[:] // StateRoots array root. - sRoots := make([][]byte, len(state.stateRoots)) - for i := range sRoots { - sRoots[i] = state.stateRoots[i][:] - } - stateRootsRoot, err := stateutil.ArraysRoot(sRoots, fieldparams.StateRootsLength) + stateRootsRoot, err := stateutil.ArraysRoot(state.stateRootsVal().Slice(), fieldparams.StateRootsLength) if err != nil { return nil, errors.Wrap(err, "could not compute state roots merkleization") } @@ -118,25 +110,21 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b fieldRoots[types.Eth1DepositIndex.RealPosition()] = eth1DepositBuf[:] // Validators slice root. - validatorsRoot, err := stateutil.ValidatorRegistryRoot(state.validators) + validatorsRoot, err := stateutil.ValidatorRegistryRoot(state.validatorsVal()) if err != nil { return nil, errors.Wrap(err, "could not compute validator registry merkleization") } fieldRoots[types.Validators.RealPosition()] = validatorsRoot[:] // Balances slice root. - balancesRoot, err := stateutil.Uint64ListRootWithRegistryLimit(state.balances) + balancesRoot, err := stateutil.Uint64ListRootWithRegistryLimit(state.balancesVal()) if err != nil { return nil, errors.Wrap(err, "could not compute validator balances merkleization") } fieldRoots[types.Balances.RealPosition()] = balancesRoot[:] // RandaoMixes array root. - mixes := make([][]byte, len(state.randaoMixes)) - for i := range mixes { - mixes[i] = state.randaoMixes[i][:] - } - randaoRootsRoot, err := stateutil.ArraysRoot(mixes, fieldparams.RandaoMixesLength) + randaoRootsRoot, err := stateutil.ArraysRoot(state.randaoMixesVal().Slice(), fieldparams.RandaoMixesLength) if err != nil { return nil, errors.Wrap(err, "could not compute randao roots merkleization") } @@ -208,7 +196,7 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b if state.version >= version.Altair { // Inactivity scores root. - inactivityScoresRoot, err := stateutil.Uint64ListRootWithRegistryLimit(state.inactivityScores) + inactivityScoresRoot, err := stateutil.Uint64ListRootWithRegistryLimit(state.inactivityScoresVal()) if err != nil { return nil, errors.Wrap(err, "could not compute inactivityScoreRoot") } diff --git a/beacon-chain/state/state-native/multi_value_slices.go b/beacon-chain/state/state-native/multi_value_slices.go new file mode 100644 index 000000000000..97ecdf33aab1 --- /dev/null +++ b/beacon-chain/state/state-native/multi_value_slices.go @@ -0,0 +1,145 @@ +package state_native + +import ( + "runtime" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + multi_value_slice "github.com/prysmaticlabs/prysm/v4/container/multi-value-slice" + "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" +) + +var ( + multiValueRandaoMixesCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_randao_mixes_count", + }) + multiValueBlockRootsCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_block_roots_count", + }) + multiValueStateRootsCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_state_roots_count", + }) + multiValueBalancesCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_balances_count", + }) + multiValueValidatorsCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_validators_count", + }) + multiValueInactivityScoresCountGauge = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "multi_value_inactivity_scores_count", + }) +) + +// MultiValueRandaoMixes is a multi-value slice of randao mixes. +type MultiValueRandaoMixes = multi_value_slice.Slice[[32]byte, *BeaconState] + +// NewMultiValueRandaoMixes creates a new slice whose shared items will be populated with copies of input values. +func NewMultiValueRandaoMixes(mixes [][]byte) *MultiValueRandaoMixes { + items := make([][32]byte, fieldparams.RandaoMixesLength) + for i, v := range mixes { + items[i] = [32]byte(bytesutil.PadTo(v, 32)) + } + mv := &MultiValueRandaoMixes{} + mv.Init(items) + multiValueRandaoMixesCountGauge.Inc() + runtime.SetFinalizer(mv, randaoMixesFinalizer) + return mv +} + +// MultiValueBlockRoots is a multi-value slice of block roots. +type MultiValueBlockRoots = multi_value_slice.Slice[[32]byte, *BeaconState] + +// NewMultiValueBlockRoots creates a new slice whose shared items will be populated with copies of input values. +func NewMultiValueBlockRoots(roots [][]byte) *MultiValueBlockRoots { + items := make([][32]byte, fieldparams.BlockRootsLength) + for i, v := range roots { + items[i] = [32]byte(bytesutil.PadTo(v, 32)) + } + mv := &MultiValueBlockRoots{} + mv.Init(items) + multiValueBlockRootsCountGauge.Inc() + runtime.SetFinalizer(mv, blockRootsFinalizer) + return mv +} + +// MultiValueStateRoots is a multi-value slice of state roots. +type MultiValueStateRoots = multi_value_slice.Slice[[32]byte, *BeaconState] + +// NewMultiValueStateRoots creates a new slice whose shared items will be populated with copies of input values. +func NewMultiValueStateRoots(roots [][]byte) *MultiValueStateRoots { + items := make([][32]byte, fieldparams.StateRootsLength) + for i, v := range roots { + items[i] = [32]byte(bytesutil.PadTo(v, 32)) + } + mv := &MultiValueStateRoots{} + mv.Init(items) + multiValueStateRootsCountGauge.Inc() + runtime.SetFinalizer(mv, stateRootsFinalizer) + return mv +} + +// MultiValueBalances is a multi-value slice of balances. +type MultiValueBalances = multi_value_slice.Slice[uint64, *BeaconState] + +// NewMultiValueBalances creates a new slice whose shared items will be populated with copies of input values. +func NewMultiValueBalances(balances []uint64) *MultiValueBalances { + items := make([]uint64, len(balances)) + copy(items, balances) + mv := &MultiValueBalances{} + mv.Init(items) + multiValueBalancesCountGauge.Inc() + runtime.SetFinalizer(mv, balancesFinalizer) + return mv +} + +// MultiValueInactivityScores is a multi-value slice of inactivity scores. +type MultiValueInactivityScores = multi_value_slice.Slice[uint64, *BeaconState] + +// NewMultiValueInactivityScores creates a new slice whose shared items will be populated with copies of input values. +func NewMultiValueInactivityScores(scores []uint64) *MultiValueInactivityScores { + items := make([]uint64, len(scores)) + copy(items, scores) + mv := &MultiValueInactivityScores{} + mv.Init(items) + multiValueInactivityScoresCountGauge.Inc() + runtime.SetFinalizer(mv, inactivityScoresFinalizer) + return mv +} + +// MultiValueValidators is a multi-value slice of validator references. +type MultiValueValidators = multi_value_slice.Slice[*ethpb.Validator, *BeaconState] + +// NewMultiValueValidators creates a new slice whose shared items will be populated with input values. +func NewMultiValueValidators(vals []*ethpb.Validator) *MultiValueValidators { + mv := &MultiValueValidators{} + mv.Init(vals) + multiValueValidatorsCountGauge.Inc() + runtime.SetFinalizer(mv, validatorsFinalizer) + return mv +} + +func randaoMixesFinalizer(m *MultiValueRandaoMixes) { + multiValueRandaoMixesCountGauge.Dec() +} + +func blockRootsFinalizer(m *MultiValueBlockRoots) { + multiValueBlockRootsCountGauge.Dec() +} + +func stateRootsFinalizer(m *MultiValueStateRoots) { + multiValueStateRootsCountGauge.Dec() +} + +func balancesFinalizer(m *MultiValueBalances) { + multiValueBalancesCountGauge.Dec() +} + +func validatorsFinalizer(m *MultiValueValidators) { + multiValueValidatorsCountGauge.Dec() +} + +func inactivityScoresFinalizer(m *MultiValueInactivityScores) { + multiValueInactivityScoresCountGauge.Dec() +} diff --git a/beacon-chain/state/state-native/mvslice_fuzz_test.go b/beacon-chain/state/state-native/mvslice_fuzz_test.go new file mode 100644 index 000000000000..6ca9dab416df --- /dev/null +++ b/beacon-chain/state/state-native/mvslice_fuzz_test.go @@ -0,0 +1,71 @@ +package state_native + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v4/config/features" + "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" + ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v4/testing/require" +) + +func FuzzMultiValueBalances(f *testing.F) { + resetFn := features.InitWithReset(&features.Flags{ + EnableExperimentalState: true, + }) + defer resetFn() + + bals := make([]uint64, 65536) + firstState, err := InitializeFromProtoPhase0(ðpb.BeaconState{Balances: bals}) + require.NoError(f, err) + + f.Fuzz(func(t *testing.T, index uint16, value uint64) { + secondState := firstState + // there's a 25% chance we will copy the state + copyState := index%4 == 0 + if copyState { + secondState = firstState.Copy() + } + if index%2 == 0 { + // update existing balance + + oldValue, err := firstState.BalanceAtIndex(primitives.ValidatorIndex(index)) + require.NoError(t, err) + + require.NoError(t, secondState.UpdateBalancesAtIndex(primitives.ValidatorIndex(index), value)) + + firstValue, err := firstState.BalanceAtIndex(primitives.ValidatorIndex(index)) + require.NoError(t, err) + secondValue, err := secondState.BalanceAtIndex(primitives.ValidatorIndex(index)) + require.NoError(t, err) + if copyState { + require.Equal(t, oldValue, firstValue) + require.Equal(t, value, secondValue) + } else { + require.Equal(t, value, firstValue) + require.Equal(t, value, secondValue) + } + } else { + // append new balance + + firstLength := firstState.BalancesLength() + + require.NoError(t, secondState.AppendBalance(value)) + + if copyState { + require.Equal(t, firstLength, secondState.BalancesLength()) + v, err := firstState.BalanceAtIndex(primitives.ValidatorIndex(firstLength - 1)) + require.NoError(t, err) + require.Equal(t, value, v) + v, err = secondState.BalanceAtIndex(primitives.ValidatorIndex(secondState.BalancesLength() - 1)) + require.NoError(t, err) + require.Equal(t, value, v) + } else { + require.Equal(t, firstLength+1, secondState.BalancesLength()) + v, err := secondState.BalanceAtIndex(primitives.ValidatorIndex(secondState.BalancesLength() - 1)) + require.NoError(t, err) + require.Equal(t, value, v) + } + } + }) +} diff --git a/beacon-chain/state/state-native/references_test.go b/beacon-chain/state/state-native/references_test.go index a3c61485ad7f..8cacb08f57a2 100644 --- a/beacon-chain/state/state-native/references_test.go +++ b/beacon-chain/state/state-native/references_test.go @@ -38,7 +38,7 @@ func TestStateReferenceSharing_Finalizer_Phase0(t *testing.T) { b, ok := copied.(*BeaconState) require.Equal(t, true, ok) assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes") - require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar"))) + require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar")))) if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 { t.Error("Expected 1 shared reference to RANDAO mix for both a and b") } @@ -67,7 +67,7 @@ func TestStateReferenceSharing_Finalizer_Altair(t *testing.T) { b, ok := copied.(*BeaconState) require.Equal(t, true, ok) assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes") - require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar"))) + require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar")))) if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 { t.Error("Expected 1 shared reference to RANDAO mix for both a and b") } @@ -96,7 +96,7 @@ func TestStateReferenceSharing_Finalizer_Bellatrix(t *testing.T) { b, ok := copied.(*BeaconState) require.Equal(t, true, ok) assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes") - require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar"))) + require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar")))) if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 { t.Error("Expected 1 shared reference to RANDAO mix for both a and b") } @@ -125,7 +125,7 @@ func TestStateReferenceSharing_Finalizer_Capella(t *testing.T) { b, ok := copied.(*BeaconState) require.Equal(t, true, ok) assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes") - require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar"))) + require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar")))) if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 { t.Error("Expected 1 shared reference to RANDAO mix for both a and b") } @@ -154,7 +154,7 @@ func TestStateReferenceSharing_Finalizer_Deneb(t *testing.T) { b, ok := copied.(*BeaconState) require.Equal(t, true, ok) assert.Equal(t, uint(2), b.sharedFieldReferences[types.RandaoMixes].Refs(), "Expected 2 shared references to RANDAO mixes") - require.NoError(t, b.UpdateRandaoMixesAtIndex(0, []byte("bar"))) + require.NoError(t, b.UpdateRandaoMixesAtIndex(0, bytesutil.ToBytes32([]byte("bar")))) if b.sharedFieldReferences[types.RandaoMixes].Refs() != 1 || a.sharedFieldReferences[types.RandaoMixes].Refs() != 1 { t.Error("Expected 1 shared reference to RANDAO mix for both a and b") } @@ -482,7 +482,7 @@ func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Phase0(t *testing.T) { assertValFound(t, mixesB, val1[:]) // Mutator should only affect calling state: a. - require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2[:])) + require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2)) // Assert no shared state mutation occurred only on state a (copy on write). assertValFound(t, a.RandaoMixes(), val2[:]) @@ -526,7 +526,7 @@ func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Altair(t *testing.T) { assertValFound(t, mixesB, val1[:]) // Mutator should only affect calling state: a. - require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2[:])) + require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2)) // Assert no shared state mutation occurred only on state a (copy on write). assertValFound(t, a.RandaoMixes(), val2[:]) @@ -570,7 +570,7 @@ func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Bellatrix(t *testing.T) { assertValFound(t, mixesB, val1[:]) // Mutator should only affect calling state: a. - require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2[:])) + require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2)) // Assert no shared state mutation occurred only on state a (copy on write). assertValFound(t, a.RandaoMixes(), val2[:]) @@ -614,7 +614,7 @@ func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Capella(t *testing.T) { assertValFound(t, mixesB, val1[:]) // Mutator should only affect calling state: a. - require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2[:])) + require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2)) // Assert no shared state mutation occurred only on state a (copy on write). assertValFound(t, a.RandaoMixes(), val2[:]) @@ -658,7 +658,7 @@ func TestStateReferenceCopy_NoUnexpectedRandaoMutation_Deneb(t *testing.T) { assertValFound(t, mixesB, val1[:]) // Mutator should only affect calling state: a. - require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2[:])) + require.NoError(t, a.UpdateRandaoMixesAtIndex(0, val2)) // Assert no shared state mutation occurred only on state a (copy on write). assertValFound(t, a.RandaoMixes(), val2[:]) diff --git a/beacon-chain/state/state-native/setters_block.go b/beacon-chain/state/state-native/setters_block.go index fca9a71732c5..22f43be3bce0 100644 --- a/beacon-chain/state/state-native/setters_block.go +++ b/beacon-chain/state/state-native/setters_block.go @@ -1,12 +1,12 @@ package state_native import ( - "fmt" - - customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) @@ -26,15 +26,22 @@ func (b *BeaconState) SetBlockRoots(val [][]byte) error { b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.BlockRoots].MinusRef() - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.blockRootsMultiValue != nil { + b.blockRootsMultiValue.Detach(b) + } + b.blockRootsMultiValue = NewMultiValueBlockRoots(val) + } else { + b.sharedFieldReferences[types.BlockRoots].MinusRef() + b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - var rootsArr [fieldparams.BlockRootsLength][32]byte - for i := 0; i < len(rootsArr); i++ { - copy(rootsArr[i][:], val[i]) + rootsArr := make([][32]byte, fieldparams.BlockRootsLength) + for i := 0; i < len(rootsArr); i++ { + copy(rootsArr[i][:], val[i]) + } + b.blockRoots = rootsArr } - roots := customtypes.BlockRoots(rootsArr) - b.blockRoots = &roots + b.markFieldAsDirty(types.BlockRoots) b.rebuildTrie[types.BlockRoots] = true return nil @@ -43,24 +50,29 @@ func (b *BeaconState) SetBlockRoots(val [][]byte) error { // UpdateBlockRootAtIndex for the beacon state. Updates the block root // at a specific index to a new value. func (b *BeaconState) UpdateBlockRootAtIndex(idx uint64, blockRoot [32]byte) error { - if uint64(len(b.blockRoots)) <= idx { - return fmt.Errorf("invalid index provided %d", idx) - } b.lock.Lock() defer b.lock.Unlock() - r := b.blockRoots - if ref := b.sharedFieldReferences[types.BlockRoots]; ref.Refs() > 1 { - // Copy elements in underlying array by reference. - roots := *b.blockRoots - rootsCopy := roots - r = &rootsCopy - ref.MinusRef() - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - } + if features.Get().EnableExperimentalState { + if err := b.blockRootsMultiValue.UpdateAt(b, idx, blockRoot); err != nil { + return errors.Wrap(err, "could not update block roots") + } + } else { + if uint64(len(b.blockRoots)) <= idx { + return errors.Wrapf(consensus_types.ErrOutOfBounds, "block root index %d does not exist", idx) + } - r[idx] = blockRoot - b.blockRoots = r + r := b.blockRoots + if ref := b.sharedFieldReferences[types.BlockRoots]; ref.Refs() > 1 { + // Copy elements in underlying array by reference. + r = make([][32]byte, len(b.blockRoots)) + copy(r, b.blockRoots) + ref.MinusRef() + b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) + } + r[idx] = blockRoot + b.blockRoots = r + } b.markFieldAsDirty(types.BlockRoots) b.addDirtyIndices(types.BlockRoots, []uint64{idx}) diff --git a/beacon-chain/state/state-native/setters_randao.go b/beacon-chain/state/state-native/setters_randao.go index 9f023d8f8c56..02aab89ef1c8 100644 --- a/beacon-chain/state/state-native/setters_randao.go +++ b/beacon-chain/state/state-native/setters_randao.go @@ -2,11 +2,11 @@ package state_native import ( "github.com/pkg/errors" - customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" - "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ) // SetRandaoMixes for the beacon state. Updates the entire @@ -15,15 +15,22 @@ func (b *BeaconState) SetRandaoMixes(val [][]byte) error { b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.RandaoMixes].MinusRef() - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.randaoMixesMultiValue != nil { + b.randaoMixesMultiValue.Detach(b) + } + b.randaoMixesMultiValue = NewMultiValueRandaoMixes(val) + } else { + b.sharedFieldReferences[types.RandaoMixes].MinusRef() + b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) - var mixesArr [fieldparams.RandaoMixesLength][32]byte - for i := 0; i < len(mixesArr); i++ { - copy(mixesArr[i][:], val[i]) + rootsArr := make([][32]byte, fieldparams.RandaoMixesLength) + for i := 0; i < len(rootsArr); i++ { + copy(rootsArr[i][:], val[i]) + } + b.randaoMixes = rootsArr } - mixes := customtypes.RandaoMixes(mixesArr) - b.randaoMixes = &mixes + b.markFieldAsDirty(types.RandaoMixes) b.rebuildTrie[types.RandaoMixes] = true return nil @@ -31,27 +38,36 @@ func (b *BeaconState) SetRandaoMixes(val [][]byte) error { // UpdateRandaoMixesAtIndex for the beacon state. Updates the randao mixes // at a specific index to a new value. -func (b *BeaconState) UpdateRandaoMixesAtIndex(idx uint64, val []byte) error { - if uint64(len(b.randaoMixes)) <= idx { - return errors.Errorf("invalid index provided %d", idx) +func (b *BeaconState) UpdateRandaoMixesAtIndex(idx uint64, val [32]byte) error { + if features.Get().EnableExperimentalState { + if err := b.randaoMixesMultiValue.UpdateAt(b, idx, val); err != nil { + return errors.Wrap(err, "could not update randao mixes") + } + } else { + if uint64(len(b.randaoMixes)) <= idx { + return errors.Wrapf(consensus_types.ErrOutOfBounds, "randao mix index %d does not exist", idx) + } + + b.lock.Lock() + + m := b.randaoMixes + if ref := b.sharedFieldReferences[types.RandaoMixes]; ref.Refs() > 1 { + // Copy elements in underlying array by reference. + m = make([][32]byte, len(b.randaoMixes)) + copy(m, b.randaoMixes) + ref.MinusRef() + b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) + } + m[idx] = val + b.randaoMixes = m + + b.lock.Unlock() } + b.lock.Lock() defer b.lock.Unlock() - mixes := b.randaoMixes - if refs := b.sharedFieldReferences[types.RandaoMixes].Refs(); refs > 1 { - // Copy elements in underlying array by reference. - m := *b.randaoMixes - mCopy := m - mixes = &mCopy - b.sharedFieldReferences[types.RandaoMixes].MinusRef() - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) - } - - mixes[idx] = bytesutil.ToBytes32(val) - b.randaoMixes = mixes b.markFieldAsDirty(types.RandaoMixes) b.addDirtyIndices(types.RandaoMixes, []uint64{idx}) - return nil } diff --git a/beacon-chain/state/state-native/setters_state.go b/beacon-chain/state/state-native/setters_state.go index 7df12abdd7d6..c7551c186385 100644 --- a/beacon-chain/state/state-native/setters_state.go +++ b/beacon-chain/state/state-native/setters_state.go @@ -2,10 +2,11 @@ package state_native import ( "github.com/pkg/errors" - customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ) // SetStateRoots for the beacon state. Updates the state roots @@ -14,15 +15,22 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error { b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.StateRoots].MinusRef() - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.stateRootsMultiValue != nil { + b.stateRootsMultiValue.Detach(b) + } + b.stateRootsMultiValue = NewMultiValueStateRoots(val) + } else { + b.sharedFieldReferences[types.StateRoots].MinusRef() + b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) - var rootsArr [fieldparams.StateRootsLength][32]byte - for i := 0; i < len(rootsArr); i++ { - copy(rootsArr[i][:], val[i]) + rootsArr := make([][32]byte, fieldparams.StateRootsLength) + for i := 0; i < len(rootsArr); i++ { + copy(rootsArr[i][:], val[i]) + } + b.stateRoots = rootsArr } - roots := customtypes.StateRoots(rootsArr) - b.stateRoots = &roots + b.markFieldAsDirty(types.StateRoots) b.rebuildTrie[types.StateRoots] = true return nil @@ -31,29 +39,33 @@ func (b *BeaconState) SetStateRoots(val [][]byte) error { // UpdateStateRootAtIndex for the beacon state. Updates the state root // at a specific index to a new value. func (b *BeaconState) UpdateStateRootAtIndex(idx uint64, stateRoot [32]byte) error { - b.lock.RLock() - if uint64(len(b.stateRoots)) <= idx { - b.lock.RUnlock() - return errors.Errorf("invalid index provided %d", idx) - } - b.lock.RUnlock() + if features.Get().EnableExperimentalState { + if err := b.stateRootsMultiValue.UpdateAt(b, idx, stateRoot); err != nil { + return errors.Wrap(err, "could not update state roots") + } + } else { + if uint64(len(b.stateRoots)) <= idx { + return errors.Wrapf(consensus_types.ErrOutOfBounds, "state root index %d does not exist", idx) + } - b.lock.Lock() - defer b.lock.Unlock() + b.lock.Lock() - // Check if we hold the only reference to the shared state roots slice. - r := b.stateRoots - if ref := b.sharedFieldReferences[types.StateRoots]; ref.Refs() > 1 { - // Copy elements in underlying array by reference. - roots := *b.stateRoots - rootsCopy := roots - r = &rootsCopy - ref.MinusRef() - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) + r := b.stateRoots + if ref := b.sharedFieldReferences[types.StateRoots]; ref.Refs() > 1 { + // Copy elements in underlying array by reference. + r = make([][32]byte, len(b.stateRoots)) + copy(r, b.stateRoots) + ref.MinusRef() + b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) + } + r[idx] = stateRoot + b.stateRoots = r + + b.lock.Unlock() } - r[idx] = stateRoot - b.stateRoots = r + b.lock.Lock() + defer b.lock.Unlock() b.markFieldAsDirty(types.StateRoots) b.addDirtyIndices(types.StateRoots, []uint64{idx}) diff --git a/beacon-chain/state/state-native/setters_validator.go b/beacon-chain/state/state-native/setters_validator.go index abeb64dec0e0..d3c72df2ffda 100644 --- a/beacon-chain/state/state-native/setters_validator.go +++ b/beacon-chain/state/state-native/setters_validator.go @@ -4,6 +4,8 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" @@ -16,69 +18,119 @@ func (b *BeaconState) SetValidators(val []*ethpb.Validator) error { b.lock.Lock() defer b.lock.Unlock() - b.validators = val - b.sharedFieldReferences[types.Validators].MinusRef() - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.validatorsMultiValue != nil { + b.validatorsMultiValue.Detach(b) + } + b.validatorsMultiValue = NewMultiValueValidators(val) + } else { + b.validators = val + b.sharedFieldReferences[types.Validators].MinusRef() + b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) + } + b.markFieldAsDirty(types.Validators) b.rebuildTrie[types.Validators] = true - b.valMapHandler = stateutil.NewValMapHandler(b.validators) + b.valMapHandler = stateutil.NewValMapHandler(val) return nil } // ApplyToEveryValidator applies the provided callback function to each validator in the // validator registry. func (b *BeaconState) ApplyToEveryValidator(f func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error)) error { - b.lock.Lock() - v := b.validators - if ref := b.sharedFieldReferences[types.Validators]; ref.Refs() > 1 { - v = b.validatorsReferences() - ref.MinusRef() - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - } - b.lock.Unlock() var changedVals []uint64 - for i, val := range v { - changed, newVal, err := f(i, val) - if err != nil { - return err + if features.Get().EnableExperimentalState { + b.lock.Lock() + + l := b.validatorsMultiValue.Len(b) + for i := 0; i < l; i++ { + v, err := b.validatorsMultiValue.At(b, uint64(i)) + if err != nil { + b.lock.Unlock() + return err + } + changed, newVal, err := f(i, v) + if err != nil { + b.lock.Unlock() + return err + } + if changed { + changedVals = append(changedVals, uint64(i)) + if err = b.validatorsMultiValue.UpdateAt(b, uint64(i), newVal); err != nil { + b.lock.Unlock() + return errors.Wrapf(err, "could not update validator at index %d", i) + } + } } - if changed { - changedVals = append(changedVals, uint64(i)) - v[i] = newVal + + b.lock.Unlock() + } else { + b.lock.Lock() + + v := b.validators + if ref := b.sharedFieldReferences[types.Validators]; ref.Refs() > 1 { + v = b.validatorsReferences() + ref.MinusRef() + b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) } + + b.lock.Unlock() + + for i, val := range v { + changed, newVal, err := f(i, val) + if err != nil { + return err + } + if changed { + changedVals = append(changedVals, uint64(i)) + v[i] = newVal + } + } + + b.lock.Lock() + b.validators = v + b.lock.Unlock() } b.lock.Lock() defer b.lock.Unlock() - b.validators = v b.markFieldAsDirty(types.Validators) b.addDirtyIndices(types.Validators, changedVals) - return nil } // UpdateValidatorAtIndex for the beacon state. Updates the validator // at a specific index to a new value. func (b *BeaconState) UpdateValidatorAtIndex(idx primitives.ValidatorIndex, val *ethpb.Validator) error { - if uint64(len(b.validators)) <= uint64(idx) { - return errors.Errorf("invalid index provided %d", idx) + if features.Get().EnableExperimentalState { + if err := b.validatorsMultiValue.UpdateAt(b, uint64(idx), val); err != nil { + return errors.Wrap(err, "could not update validator") + } + } else { + if uint64(len(b.validators)) <= uint64(idx) { + return errors.Wrapf(consensus_types.ErrOutOfBounds, "validator index %d does not exist", idx) + } + + b.lock.Lock() + + v := b.validators + if ref := b.sharedFieldReferences[types.Validators]; ref.Refs() > 1 { + v = b.validatorsReferences() + ref.MinusRef() + b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) + } + v[idx] = val + b.validators = v + + b.lock.Unlock() } + b.lock.Lock() defer b.lock.Unlock() - v := b.validators - if ref := b.sharedFieldReferences[types.Validators]; ref.Refs() > 1 { - v = b.validatorsReferences() - ref.MinusRef() - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - } - - v[idx] = val - b.validators = v b.markFieldAsDirty(types.Validators) b.addDirtyIndices(types.Validators, []uint64{uint64(idx)}) - return nil } @@ -88,10 +140,17 @@ func (b *BeaconState) SetBalances(val []uint64) error { b.lock.Lock() defer b.lock.Unlock() - b.sharedFieldReferences[types.Balances].MinusRef() - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.balancesMultiValue != nil { + b.balancesMultiValue.Detach(b) + } + b.balancesMultiValue = NewMultiValueBalances(val) + } else { + b.sharedFieldReferences[types.Balances].MinusRef() + b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) + b.balances = val + } - b.balances = val b.markFieldAsDirty(types.Balances) b.rebuildTrie[types.Balances] = true return nil @@ -100,21 +159,32 @@ func (b *BeaconState) SetBalances(val []uint64) error { // UpdateBalancesAtIndex for the beacon state. This method updates the balance // at a specific index to a new value. func (b *BeaconState) UpdateBalancesAtIndex(idx primitives.ValidatorIndex, val uint64) error { - if uint64(len(b.balances)) <= uint64(idx) { - return errors.Errorf("invalid index provided %d", idx) + if features.Get().EnableExperimentalState { + if err := b.balancesMultiValue.UpdateAt(b, uint64(idx), val); err != nil { + return errors.Wrap(err, "could not update balances") + } + } else { + if uint64(len(b.balances)) <= uint64(idx) { + return errors.Wrapf(consensus_types.ErrOutOfBounds, "balance index %d does not exist", idx) + } + + b.lock.Lock() + + bals := b.balances + if b.sharedFieldReferences[types.Balances].Refs() > 1 { + bals = b.balancesVal() + b.sharedFieldReferences[types.Balances].MinusRef() + b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) + } + bals[idx] = val + b.balances = bals + + b.lock.Unlock() } + b.lock.Lock() defer b.lock.Unlock() - bals := b.balances - if b.sharedFieldReferences[types.Balances].Refs() > 1 { - bals = b.balancesVal() - b.sharedFieldReferences[types.Balances].MinusRef() - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - } - - bals[idx] = val - b.balances = bals b.markFieldAsDirty(types.Balances) b.addDirtyIndices(types.Balances, []uint64{uint64(idx)}) return nil @@ -161,22 +231,30 @@ func (b *BeaconState) UpdateSlashingsAtIndex(idx, val uint64) error { // AppendValidator for the beacon state. Appends the new value // to the end of list. func (b *BeaconState) AppendValidator(val *ethpb.Validator) error { - b.lock.Lock() - defer b.lock.Unlock() + var valIdx primitives.ValidatorIndex + if features.Get().EnableExperimentalState { + b.validatorsMultiValue.Append(b, val) + valIdx = primitives.ValidatorIndex(b.validatorsMultiValue.Len(b) - 1) + } else { + b.lock.Lock() + + vals := b.validators + if b.sharedFieldReferences[types.Validators].Refs() > 1 { + vals = b.validatorsReferences() + b.sharedFieldReferences[types.Validators].MinusRef() + b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) + } - vals := b.validators - if b.sharedFieldReferences[types.Validators].Refs() > 1 { - vals = b.validatorsReferences() - b.sharedFieldReferences[types.Validators].MinusRef() - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) + b.validators = append(vals, val) + valIdx = primitives.ValidatorIndex(len(b.validators) - 1) + + b.lock.Unlock() } - // append validator to slice - b.validators = append(vals, val) - valIdx := primitives.ValidatorIndex(len(b.validators) - 1) + b.lock.Lock() + defer b.lock.Unlock() b.valMapHandler.Set(bytesutil.ToBytes48(val.PublicKey), valIdx) - b.markFieldAsDirty(types.Validators) b.addDirtyIndices(types.Validators, []uint64{uint64(valIdx)}) return nil @@ -185,42 +263,61 @@ func (b *BeaconState) AppendValidator(val *ethpb.Validator) error { // AppendBalance for the beacon state. Appends the new value // to the end of list. func (b *BeaconState) AppendBalance(bal uint64) error { - b.lock.Lock() - defer b.lock.Unlock() + var balIdx uint64 + if features.Get().EnableExperimentalState { + b.balancesMultiValue.Append(b, bal) + balIdx = uint64(b.balancesMultiValue.Len(b) - 1) + } else { + b.lock.Lock() + + bals := b.balances + if b.sharedFieldReferences[types.Balances].Refs() > 1 { + bals = make([]uint64, 0, len(b.balances)+1) + bals = append(bals, b.balances...) + b.sharedFieldReferences[types.Balances].MinusRef() + b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) + } - bals := b.balances - if b.sharedFieldReferences[types.Balances].Refs() > 1 { - bals = make([]uint64, 0, len(b.balances)+1) - bals = append(bals, b.balances...) - b.sharedFieldReferences[types.Balances].MinusRef() - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) + b.balances = append(bals, bal) + balIdx = uint64(len(b.balances) - 1) + + b.lock.Unlock() } - b.balances = append(bals, bal) - balIdx := len(b.balances) - 1 + b.lock.Lock() + defer b.lock.Unlock() + b.markFieldAsDirty(types.Balances) - b.addDirtyIndices(types.Balances, []uint64{uint64(balIdx)}) + b.addDirtyIndices(types.Balances, []uint64{balIdx}) return nil } // AppendInactivityScore for the beacon state. func (b *BeaconState) AppendInactivityScore(s uint64) error { - b.lock.Lock() - defer b.lock.Unlock() - if b.version == version.Phase0 { return errNotSupported("AppendInactivityScore", b.version) } - scores := b.inactivityScores - if b.sharedFieldReferences[types.InactivityScores].Refs() > 1 { - scores = make([]uint64, 0, len(b.inactivityScores)+1) - scores = append(scores, b.inactivityScores...) - b.sharedFieldReferences[types.InactivityScores].MinusRef() - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + b.inactivityScoresMultiValue.Append(b, s) + } else { + b.lock.Lock() + + scores := b.inactivityScores + if b.sharedFieldReferences[types.InactivityScores].Refs() > 1 { + scores = make([]uint64, 0, len(b.inactivityScores)+1) + scores = append(scores, b.inactivityScores...) + b.sharedFieldReferences[types.InactivityScores].MinusRef() + b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) + } + b.inactivityScores = append(scores, s) + + b.lock.Unlock() } - b.inactivityScores = append(scores, s) + b.lock.Lock() + defer b.lock.Unlock() + b.markFieldAsDirty(types.InactivityScores) return nil } @@ -235,10 +332,17 @@ func (b *BeaconState) SetInactivityScores(val []uint64) error { return errNotSupported("SetInactivityScores", b.version) } - b.sharedFieldReferences[types.InactivityScores].MinusRef() - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) + if features.Get().EnableExperimentalState { + if b.inactivityScoresMultiValue != nil { + b.inactivityScoresMultiValue.Detach(b) + } + b.inactivityScoresMultiValue = NewMultiValueInactivityScores(val) + } else { + b.sharedFieldReferences[types.InactivityScores].MinusRef() + b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) + b.inactivityScores = val + } - b.inactivityScores = val b.markFieldAsDirty(types.InactivityScores) return nil } diff --git a/beacon-chain/state/state-native/state_trie.go b/beacon-chain/state/state-native/state_trie.go index 2a89fba10025..0c0a714303f1 100644 --- a/beacon-chain/state/state-native/state_trie.go +++ b/beacon-chain/state/state-native/state_trie.go @@ -11,6 +11,7 @@ import ( customtypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/custom-types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stateutil" + "github.com/prysmaticlabs/prysm/v4/config/features" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/container/slice" @@ -92,11 +93,16 @@ var denebFields = append( ) const ( - phase0SharedFieldRefCount = 10 - altairSharedFieldRefCount = 11 - bellatrixSharedFieldRefCount = 12 - capellaSharedFieldRefCount = 14 - denebSharedFieldRefCount = 14 + phase0SharedFieldRefCount = 10 + altairSharedFieldRefCount = 11 + bellatrixSharedFieldRefCount = 12 + capellaSharedFieldRefCount = 14 + denebSharedFieldRefCount = 14 + experimentalStatePhase0SharedFieldRefCount = 5 + experimentalStateAltairSharedFieldRefCount = 5 + experimentalStateBellatrixSharedFieldRefCount = 6 + experimentalStateCapellaSharedFieldRefCount = 8 + experimentalStateDenebSharedFieldRefCount = 8 ) // InitializeFromProtoPhase0 the beacon state from a protobuf representation. @@ -131,22 +137,10 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, return nil, errors.New("received nil state") } - var bRoots customtypes.BlockRoots - for i, r := range st.BlockRoots { - copy(bRoots[i][:], r) - } - var sRoots customtypes.StateRoots - for i, r := range st.StateRoots { - copy(sRoots[i][:], r) - } hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) for i, r := range st.HistoricalRoots { copy(hRoots[i][:], r) } - var mixes customtypes.RandaoMixes - for i, m := range st.RandaoMixes { - copy(mixes[i][:], m) - } fieldCount := params.BeaconConfig().BeaconStateFieldCount b := &BeaconState{ @@ -156,15 +150,10 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, slot: st.Slot, fork: st.Fork, latestBlockHeader: st.LatestBlockHeader, - blockRoots: &bRoots, - stateRoots: &sRoots, historicalRoots: hRoots, eth1Data: st.Eth1Data, eth1DataVotes: st.Eth1DataVotes, eth1DepositIndex: st.Eth1DepositIndex, - validators: st.Validators, - balances: st.Balances, - randaoMixes: &mixes, slashings: st.Slashings, previousEpochAttestations: st.PreviousEpochAttestations, currentEpochAttestations: st.CurrentEpochAttestations, @@ -173,12 +162,45 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, finalizedCheckpoint: st.FinalizedCheckpoint, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), + id: types.Enumerator.Inc(), + + 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.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStatePhase0SharedFieldRefCount) + } 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.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount) } for _, f := range phase0Fields { @@ -193,16 +215,18 @@ func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, } // Initialize field reference tracking for shared data. - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) b.sharedFieldReferences[types.PreviousEpochAttestations] = stateutil.NewRef(1) b.sharedFieldReferences[types.CurrentEpochAttestations] = stateutil.NewRef(1) + 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) + } state.StateCount.Inc() // Finalizer runs when dst is being destroyed in garbage collection. @@ -217,22 +241,10 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS return nil, errors.New("received nil state") } - var bRoots customtypes.BlockRoots - for i, r := range st.BlockRoots { - bRoots[i] = bytesutil.ToBytes32(r) - } - var sRoots customtypes.StateRoots - for i, r := range st.StateRoots { - sRoots[i] = bytesutil.ToBytes32(r) - } hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) for i, r := range st.HistoricalRoots { hRoots[i] = bytesutil.ToBytes32(r) } - var mixes customtypes.RandaoMixes - for i, m := range st.RandaoMixes { - mixes[i] = bytesutil.ToBytes32(m) - } fieldCount := params.BeaconConfig().BeaconStateAltairFieldCount b := &BeaconState{ @@ -242,15 +254,10 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS slot: st.Slot, fork: st.Fork, latestBlockHeader: st.LatestBlockHeader, - blockRoots: &bRoots, - stateRoots: &sRoots, historicalRoots: hRoots, eth1Data: st.Eth1Data, eth1DataVotes: st.Eth1DataVotes, eth1DepositIndex: st.Eth1DepositIndex, - validators: st.Validators, - balances: st.Balances, - randaoMixes: &mixes, slashings: st.Slashings, previousEpochParticipation: st.PreviousEpochParticipation, currentEpochParticipation: st.CurrentEpochParticipation, @@ -258,16 +265,50 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint, currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, finalizedCheckpoint: st.FinalizedCheckpoint, - inactivityScores: st.InactivityScores, currentSyncCommittee: st.CurrentSyncCommittee, nextSyncCommittee: st.NextSyncCommittee, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, altairSharedFieldRefCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), + id: types.Enumerator.Inc(), + + 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, experimentalStateAltairSharedFieldRefCount) + } 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, altairSharedFieldRefCount) } for _, f := range altairFields { @@ -282,17 +323,19 @@ func InitializeFromProtoUnsafeAltair(st *ethpb.BeaconStateAltair) (state.BeaconS } // Initialize field reference tracking for shared data. - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) // New in Altair. b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) // New in Altair. - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) // New in Altair. + 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.StateCount.Inc() // Finalizer runs when dst is being destroyed in garbage collection. @@ -307,22 +350,10 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B return nil, errors.New("received nil state") } - var bRoots customtypes.BlockRoots - for i, r := range st.BlockRoots { - bRoots[i] = bytesutil.ToBytes32(r) - } - var sRoots customtypes.StateRoots - for i, r := range st.StateRoots { - sRoots[i] = bytesutil.ToBytes32(r) - } hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) for i, r := range st.HistoricalRoots { hRoots[i] = bytesutil.ToBytes32(r) } - var mixes customtypes.RandaoMixes - for i, m := range st.RandaoMixes { - mixes[i] = bytesutil.ToBytes32(m) - } fieldCount := params.BeaconConfig().BeaconStateBellatrixFieldCount b := &BeaconState{ @@ -332,15 +363,10 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B slot: st.Slot, fork: st.Fork, latestBlockHeader: st.LatestBlockHeader, - blockRoots: &bRoots, - stateRoots: &sRoots, historicalRoots: hRoots, eth1Data: st.Eth1Data, eth1DataVotes: st.Eth1DataVotes, eth1DepositIndex: st.Eth1DepositIndex, - validators: st.Validators, - balances: st.Balances, - randaoMixes: &mixes, slashings: st.Slashings, previousEpochParticipation: st.PreviousEpochParticipation, currentEpochParticipation: st.CurrentEpochParticipation, @@ -348,17 +374,51 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint, currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, finalizedCheckpoint: st.FinalizedCheckpoint, - inactivityScores: st.InactivityScores, currentSyncCommittee: st.CurrentSyncCommittee, nextSyncCommittee: st.NextSyncCommittee, latestExecutionPayloadHeader: st.LatestExecutionPayloadHeader, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), + id: types.Enumerator.Inc(), + + 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, experimentalStateBellatrixSharedFieldRefCount) + } 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, bellatrixSharedFieldRefCount) } for _, f := range bellatrixFields { @@ -373,18 +433,20 @@ func InitializeFromProtoUnsafeBellatrix(st *ethpb.BeaconStateBellatrix) (state.B } // Initialize field reference tracking for shared data. - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) b.sharedFieldReferences[types.LatestExecutionPayloadHeader] = stateutil.NewRef(1) // New in Bellatrix. + 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.StateCount.Inc() // Finalizer runs when dst is being destroyed in garbage collection. @@ -399,22 +461,10 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco return nil, errors.New("received nil state") } - var bRoots customtypes.BlockRoots - for i, r := range st.BlockRoots { - bRoots[i] = bytesutil.ToBytes32(r) - } - var sRoots customtypes.StateRoots - for i, r := range st.StateRoots { - sRoots[i] = bytesutil.ToBytes32(r) - } hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) for i, r := range st.HistoricalRoots { hRoots[i] = bytesutil.ToBytes32(r) } - var mixes customtypes.RandaoMixes - for i, m := range st.RandaoMixes { - mixes[i] = bytesutil.ToBytes32(m) - } fieldCount := params.BeaconConfig().BeaconStateCapellaFieldCount b := &BeaconState{ @@ -424,15 +474,10 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco slot: st.Slot, fork: st.Fork, latestBlockHeader: st.LatestBlockHeader, - blockRoots: &bRoots, - stateRoots: &sRoots, historicalRoots: hRoots, eth1Data: st.Eth1Data, eth1DataVotes: st.Eth1DataVotes, eth1DepositIndex: st.Eth1DepositIndex, - validators: st.Validators, - balances: st.Balances, - randaoMixes: &mixes, slashings: st.Slashings, previousEpochParticipation: st.PreviousEpochParticipation, currentEpochParticipation: st.CurrentEpochParticipation, @@ -440,7 +485,6 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint, currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, finalizedCheckpoint: st.FinalizedCheckpoint, - inactivityScores: st.InactivityScores, currentSyncCommittee: st.CurrentSyncCommittee, nextSyncCommittee: st.NextSyncCommittee, latestExecutionPayloadHeaderCapella: st.LatestExecutionPayloadHeader, @@ -448,12 +492,47 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex, historicalSummaries: st.HistoricalSummaries, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), + id: types.Enumerator.Inc(), + + 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, experimentalStateCapellaSharedFieldRefCount) + } 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, capellaSharedFieldRefCount) } for _, f := range capellaFields { @@ -468,19 +547,21 @@ func InitializeFromProtoUnsafeCapella(st *ethpb.BeaconStateCapella) (state.Beaco } // Initialize field reference tracking for shared data. - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) b.sharedFieldReferences[types.LatestExecutionPayloadHeaderCapella] = stateutil.NewRef(1) // New in Capella. b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella. + 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.StateCount.Inc() // Finalizer runs when dst is being destroyed in garbage collection. @@ -495,22 +576,10 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta return nil, errors.New("received nil state") } - var bRoots customtypes.BlockRoots - for i, r := range st.BlockRoots { - bRoots[i] = bytesutil.ToBytes32(r) - } - var sRoots customtypes.StateRoots - for i, r := range st.StateRoots { - sRoots[i] = bytesutil.ToBytes32(r) - } hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) for i, r := range st.HistoricalRoots { hRoots[i] = bytesutil.ToBytes32(r) } - var mixes customtypes.RandaoMixes - for i, m := range st.RandaoMixes { - mixes[i] = bytesutil.ToBytes32(m) - } fieldCount := params.BeaconConfig().BeaconStateDenebFieldCount b := &BeaconState{ @@ -520,15 +589,10 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta slot: st.Slot, fork: st.Fork, latestBlockHeader: st.LatestBlockHeader, - blockRoots: &bRoots, - stateRoots: &sRoots, historicalRoots: hRoots, eth1Data: st.Eth1Data, eth1DataVotes: st.Eth1DataVotes, eth1DepositIndex: st.Eth1DepositIndex, - validators: st.Validators, - balances: st.Balances, - randaoMixes: &mixes, slashings: st.Slashings, previousEpochParticipation: st.PreviousEpochParticipation, currentEpochParticipation: st.CurrentEpochParticipation, @@ -536,7 +600,6 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint, currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, finalizedCheckpoint: st.FinalizedCheckpoint, - inactivityScores: st.InactivityScores, currentSyncCommittee: st.CurrentSyncCommittee, nextSyncCommittee: st.NextSyncCommittee, latestExecutionPayloadHeaderDeneb: st.LatestExecutionPayloadHeader, @@ -544,12 +607,45 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex, historicalSummaries: st.HistoricalSummaries, - dirtyFields: make(map[types.FieldIndex]bool, fieldCount), - dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), - stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount), - sharedFieldReferences: make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount), - rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), - valMapHandler: stateutil.NewValMapHandler(st.Validators), + 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, experimentalStateDenebSharedFieldRefCount) + } 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, denebSharedFieldRefCount) } for _, f := range denebFields { @@ -564,19 +660,21 @@ func InitializeFromProtoUnsafeDeneb(st *ethpb.BeaconStateDeneb) (state.BeaconSta } // Initialize field reference tracking for shared data. - b.sharedFieldReferences[types.BlockRoots] = stateutil.NewRef(1) - b.sharedFieldReferences[types.StateRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Validators] = stateutil.NewRef(1) - b.sharedFieldReferences[types.Balances] = stateutil.NewRef(1) - b.sharedFieldReferences[types.RandaoMixes] = stateutil.NewRef(1) b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) - b.sharedFieldReferences[types.InactivityScores] = stateutil.NewRef(1) b.sharedFieldReferences[types.LatestExecutionPayloadHeaderDeneb] = stateutil.NewRef(1) // New in Deneb. b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) // New in Capella. + 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.StateCount.Inc() // Finalizer runs when dst is being destroyed in garbage collection. @@ -615,8 +713,11 @@ func (b *BeaconState) Copy() state.BeaconState { // Large arrays, infrequently changed, constant size. blockRoots: b.blockRoots, + blockRootsMultiValue: b.blockRootsMultiValue, stateRoots: b.stateRoots, + stateRootsMultiValue: b.stateRootsMultiValue, randaoMixes: b.randaoMixes, + randaoMixesMultiValue: b.randaoMixesMultiValue, previousEpochAttestations: b.previousEpochAttestations, currentEpochAttestations: b.currentEpochAttestations, eth1DataVotes: b.eth1DataVotes, @@ -624,12 +725,15 @@ func (b *BeaconState) Copy() state.BeaconState { // Large arrays, increases over time. balances: b.balances, + balancesMultiValue: b.balancesMultiValue, historicalRoots: b.historicalRoots, historicalSummaries: b.historicalSummaries, validators: b.validators, + validatorsMultiValue: b.validatorsMultiValue, previousEpochParticipation: b.previousEpochParticipation, currentEpochParticipation: b.currentEpochParticipation, inactivityScores: b.inactivityScores, + inactivityScoresMultiValue: b.inactivityScoresMultiValue, // Everything else, too small to be concerned about, constant size. genesisValidatorsRoot: b.genesisValidatorsRoot, @@ -646,6 +750,8 @@ func (b *BeaconState) Copy() state.BeaconState { latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapellaVal(), latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDenebVal(), + id: types.Enumerator.Inc(), + dirtyFields: make(map[types.FieldIndex]bool, fieldCount), dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount), rebuildTrie: make(map[types.FieldIndex]bool, fieldCount), @@ -655,17 +761,43 @@ func (b *BeaconState) Copy() state.BeaconState { valMapHandler: b.valMapHandler, } - switch b.version { - case version.Phase0: - dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount) - case version.Altair: - dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, altairSharedFieldRefCount) - case version.Bellatrix: - dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount) - case version.Capella: - dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount) - case version.Deneb: - dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, denebSharedFieldRefCount) + if features.Get().EnableExperimentalState { + b.blockRootsMultiValue.Copy(b, dst) + b.stateRootsMultiValue.Copy(b, dst) + b.randaoMixesMultiValue.Copy(b, dst) + b.balancesMultiValue.Copy(b, dst) + if b.version > version.Phase0 { + b.inactivityScoresMultiValue.Copy(b, dst) + } + b.validatorsMultiValue.Copy(b, dst) + } + + if features.Get().EnableExperimentalState { + switch b.version { + case version.Phase0: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStatePhase0SharedFieldRefCount) + case version.Altair: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateAltairSharedFieldRefCount) + case version.Bellatrix: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateBellatrixSharedFieldRefCount) + case version.Capella: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateCapellaSharedFieldRefCount) + case version.Deneb: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, experimentalStateDenebSharedFieldRefCount) + } + } else { + switch b.version { + case version.Phase0: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, phase0SharedFieldRefCount) + case version.Altair: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, altairSharedFieldRefCount) + case version.Bellatrix: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, bellatrixSharedFieldRefCount) + case version.Capella: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, capellaSharedFieldRefCount) + case version.Deneb: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, denebSharedFieldRefCount) + } } for field, ref := range b.sharedFieldReferences { @@ -824,25 +956,9 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) case types.LatestBlockHeader: return stateutil.BlockHeaderRoot(b.latestBlockHeader) case types.BlockRoots: - if b.rebuildTrie[field] { - err := b.resetFieldTrie(field, b.blockRoots, fieldparams.BlockRootsLength) - if err != nil { - return [32]byte{}, err - } - delete(b.rebuildTrie, field) - return b.stateFieldLeaves[field].TrieRoot() - } - return b.recomputeFieldTrie(field, b.blockRoots) + return b.blockRootsRootSelector(field) case types.StateRoots: - if b.rebuildTrie[field] { - err := b.resetFieldTrie(field, b.stateRoots, fieldparams.StateRootsLength) - if err != nil { - return [32]byte{}, err - } - delete(b.rebuildTrie, field) - return b.stateFieldLeaves[field].TrieRoot() - } - return b.recomputeFieldTrie(field, b.stateRoots) + return b.stateRootsRootSelector(field) case types.HistoricalRoots: hRoots := make([][]byte, len(b.historicalRoots)) for i := range hRoots { @@ -866,35 +982,11 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) } return b.recomputeFieldTrie(field, b.eth1DataVotes) case types.Validators: - if b.rebuildTrie[field] { - err := b.resetFieldTrie(field, b.validators, fieldparams.ValidatorRegistryLimit) - if err != nil { - return [32]byte{}, err - } - delete(b.rebuildTrie, field) - return b.stateFieldLeaves[field].TrieRoot() - } - return b.recomputeFieldTrie(11, b.validators) + return b.validatorsRootSelector(field) case types.Balances: - if b.rebuildTrie[field] { - err := b.resetFieldTrie(field, b.balances, stateutil.ValidatorLimitForBalancesChunks()) - if err != nil { - return [32]byte{}, err - } - delete(b.rebuildTrie, field) - return b.stateFieldLeaves[field].TrieRoot() - } - return b.recomputeFieldTrie(12, b.balances) + return b.balancesRootSelector(field) case types.RandaoMixes: - if b.rebuildTrie[field] { - err := b.resetFieldTrie(field, b.randaoMixes, fieldparams.RandaoMixesLength) - if err != nil { - return [32]byte{}, err - } - delete(b.rebuildTrie, field) - return b.stateFieldLeaves[field].TrieRoot() - } - return b.recomputeFieldTrie(13, b.randaoMixes) + return b.randaoMixesRootSelector(field) case types.Slashings: return ssz.SlashingsRoot(b.slashings) case types.PreviousEpochAttestations: @@ -938,7 +1030,11 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) case types.FinalizedCheckpoint: return ssz.CheckpointRoot(b.finalizedCheckpoint) case types.InactivityScores: - return stateutil.Uint64ListRootWithRegistryLimit(b.inactivityScores) + if features.Get().EnableExperimentalState { + return stateutil.Uint64ListRootWithRegistryLimit(b.inactivityScoresMultiValue.Value(b)) + } else { + return stateutil.Uint64ListRootWithRegistryLimit(b.inactivityScores) + } case types.CurrentSyncCommittee: return stateutil.SyncCommitteeRoot(b.currentSyncCommittee) case types.NextSyncCommittee: @@ -1054,5 +1150,142 @@ func finalizerCleanup(b *BeaconState) { for i := range b.stateFieldLeaves { delete(b.stateFieldLeaves, i) } + + if features.Get().EnableExperimentalState { + if b.blockRootsMultiValue != nil { + b.blockRootsMultiValue.Detach(b) + } + if b.stateRootsMultiValue != nil { + b.stateRootsMultiValue.Detach(b) + } + if b.randaoMixesMultiValue != nil { + b.randaoMixesMultiValue.Detach(b) + } + if b.balancesMultiValue != nil { + b.balancesMultiValue.Detach(b) + } + if b.inactivityScoresMultiValue != nil { + b.inactivityScoresMultiValue.Detach(b) + } + if b.validatorsMultiValue != nil { + b.validatorsMultiValue.Detach(b) + } + } + state.StateCount.Sub(1) } + +func (b *BeaconState) blockRootsRootSelector(field types.FieldIndex) ([32]byte, error) { + if b.rebuildTrie[field] { + if features.Get().EnableExperimentalState { + err := b.resetFieldTrie(field, customtypes.BlockRoots(b.blockRootsMultiValue.Value(b)), fieldparams.BlockRootsLength) + if err != nil { + return [32]byte{}, err + } + } else { + err := b.resetFieldTrie(field, b.blockRoots, fieldparams.BlockRootsLength) + if err != nil { + return [32]byte{}, err + } + } + delete(b.rebuildTrie, field) + return b.stateFieldLeaves[field].TrieRoot() + } + if features.Get().EnableExperimentalState { + return b.recomputeFieldTrie(field, customtypes.BlockRoots(b.blockRootsMultiValue.Value(b))) + } else { + return b.recomputeFieldTrie(field, b.blockRoots) + } +} + +func (b *BeaconState) stateRootsRootSelector(field types.FieldIndex) ([32]byte, error) { + if b.rebuildTrie[field] { + if features.Get().EnableExperimentalState { + err := b.resetFieldTrie(field, customtypes.StateRoots(b.stateRootsMultiValue.Value(b)), fieldparams.StateRootsLength) + if err != nil { + return [32]byte{}, err + } + } else { + err := b.resetFieldTrie(field, b.stateRoots, fieldparams.StateRootsLength) + if err != nil { + return [32]byte{}, err + } + } + delete(b.rebuildTrie, field) + return b.stateFieldLeaves[field].TrieRoot() + } + if features.Get().EnableExperimentalState { + return b.recomputeFieldTrie(field, customtypes.StateRoots(b.stateRootsMultiValue.Value(b))) + } else { + return b.recomputeFieldTrie(field, b.stateRoots) + } +} + +func (b *BeaconState) validatorsRootSelector(field types.FieldIndex) ([32]byte, error) { + if b.rebuildTrie[field] { + if features.Get().EnableExperimentalState { + err := b.resetFieldTrie(field, b.validatorsMultiValue.Value(b), fieldparams.ValidatorRegistryLimit) + if err != nil { + return [32]byte{}, err + } + } else { + err := b.resetFieldTrie(field, b.validators, fieldparams.ValidatorRegistryLimit) + if err != nil { + return [32]byte{}, err + } + } + delete(b.rebuildTrie, field) + return b.stateFieldLeaves[field].TrieRoot() + } + if features.Get().EnableExperimentalState { + return b.recomputeFieldTrie(field, b.validatorsMultiValue.Value(b)) + } else { + return b.recomputeFieldTrie(field, b.validators) + } +} + +func (b *BeaconState) balancesRootSelector(field types.FieldIndex) ([32]byte, error) { + if b.rebuildTrie[field] { + if features.Get().EnableExperimentalState { + err := b.resetFieldTrie(field, b.balancesMultiValue.Value(b), stateutil.ValidatorLimitForBalancesChunks()) + if err != nil { + return [32]byte{}, err + } + } else { + err := b.resetFieldTrie(field, b.balances, stateutil.ValidatorLimitForBalancesChunks()) + if err != nil { + return [32]byte{}, err + } + } + delete(b.rebuildTrie, field) + return b.stateFieldLeaves[field].TrieRoot() + } + if features.Get().EnableExperimentalState { + return b.recomputeFieldTrie(field, b.balancesMultiValue.Value(b)) + } else { + return b.recomputeFieldTrie(field, b.balances) + } +} + +func (b *BeaconState) randaoMixesRootSelector(field types.FieldIndex) ([32]byte, error) { + if b.rebuildTrie[field] { + if features.Get().EnableExperimentalState { + err := b.resetFieldTrie(field, customtypes.RandaoMixes(b.randaoMixesMultiValue.Value(b)), fieldparams.RandaoMixesLength) + if err != nil { + return [32]byte{}, err + } + } else { + err := b.resetFieldTrie(field, b.randaoMixes, fieldparams.RandaoMixesLength) + if err != nil { + return [32]byte{}, err + } + } + delete(b.rebuildTrie, field) + return b.stateFieldLeaves[field].TrieRoot() + } + if features.Get().EnableExperimentalState { + return b.recomputeFieldTrie(field, customtypes.RandaoMixes(b.randaoMixesMultiValue.Value(b))) + } else { + return b.recomputeFieldTrie(field, b.randaoMixes) + } +} diff --git a/beacon-chain/state/state-native/types.go b/beacon-chain/state/state-native/types.go index 05399b31610b..5ae6981e4479 100644 --- a/beacon-chain/state/state-native/types.go +++ b/beacon-chain/state/state-native/types.go @@ -11,6 +11,7 @@ import ( // Ensure type BeaconState below implements BeaconState interface. var _ state.BeaconState = (*BeaconState)(nil) +// initialization for tests func init() { fieldMap = make(map[types.FieldIndex]types.DataType) // Initialize the fixed sized arrays. diff --git a/beacon-chain/state/state-native/types/BUILD.bazel b/beacon-chain/state/state-native/types/BUILD.bazel index cc86d5b0ca20..66fe784dd2d4 100644 --- a/beacon-chain/state/state-native/types/BUILD.bazel +++ b/beacon-chain/state/state-native/types/BUILD.bazel @@ -5,5 +5,8 @@ go_library( srcs = ["types.go"], importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native/types", visibility = ["//visibility:public"], - deps = ["@com_github_pkg_errors//:go_default_library"], + deps = [ + "//consensus-types:go_default_library", + "@com_github_pkg_errors//:go_default_library", + ], ) diff --git a/beacon-chain/state/state-native/types/types.go b/beacon-chain/state/state-native/types/types.go index d181bce1bc4e..8c4e7fa05249 100644 --- a/beacon-chain/state/state-native/types/types.go +++ b/beacon-chain/state/state-native/types/types.go @@ -2,6 +2,7 @@ package types import ( "github.com/pkg/errors" + consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" ) // DataType signifies the data type of the field. @@ -210,3 +211,6 @@ const ( NextWithdrawalValidatorIndex HistoricalSummaries ) + +// Enumerator keeps track of the number of states created since the node's start. +var Enumerator = &consensus_types.ThreadSafeEnumerator{} diff --git a/beacon-chain/state/state-native/types_test.go b/beacon-chain/state/state-native/types_test.go index 05f029582178..b9f06d83e758 100644 --- a/beacon-chain/state/state-native/types_test.go +++ b/beacon-chain/state/state-native/types_test.go @@ -185,7 +185,7 @@ func TestBeaconState_ImmutabilityWithSharedResources(t *testing.T) { // Randao mixes require.DeepEqual(t, a.RandaoMixes(), b.RandaoMixes(), "Test precondition failed, fields are not equal") - require.NoError(t, a.UpdateRandaoMixesAtIndex(1, []byte("foo"))) + require.NoError(t, a.UpdateRandaoMixesAtIndex(1, bytesutil.ToBytes32([]byte("foo")))) if reflect.DeepEqual(a.RandaoMixes(), b.RandaoMixes()) { t.Error("Expect a.RandaoMixes() to be different from b.RandaoMixes()") } diff --git a/beacon-chain/state/stategen/migrate_test.go b/beacon-chain/state/stategen/migrate_test.go index ed0a59d634cf..39933b496a72 100644 --- a/beacon-chain/state/stategen/migrate_test.go +++ b/beacon-chain/state/stategen/migrate_test.go @@ -30,7 +30,13 @@ func TestMigrateToCold_CanSaveFinalizedInfo(t *testing.T) { require.NoError(t, service.MigrateToCold(ctx, br)) wanted := &finalizedInfo{state: beaconState, root: br, slot: 1} - assert.DeepEqual(t, wanted, service.finalizedInfo, "Incorrect finalized info") + assert.DeepEqual(t, wanted.root, service.finalizedInfo.root) + assert.Equal(t, wanted.slot, service.finalizedInfo.slot) + expectedHTR, err := wanted.state.HashTreeRoot(ctx) + require.NoError(t, err) + actualHTR, err := service.finalizedInfo.state.HashTreeRoot(ctx) + require.NoError(t, err) + assert.DeepEqual(t, expectedHTR, actualHTR) } func TestMigrateToCold_HappyPath(t *testing.T) { diff --git a/beacon-chain/state/stategen/mock_test.go b/beacon-chain/state/stategen/mock_test.go index f273ec252afb..784cc2f321c7 100644 --- a/beacon-chain/state/stategen/mock_test.go +++ b/beacon-chain/state/stategen/mock_test.go @@ -34,12 +34,20 @@ func TestMockHistoryStates(t *testing.T) { genesisRoot := hist.slotMap[0] st, err := hist.StateOrError(ctx, genesisRoot) require.NoError(t, err) - require.DeepEqual(t, hist.states[genesisRoot], st) + expectedHTR, err := hist.states[genesisRoot].HashTreeRoot(ctx) + require.NoError(t, err) + actualHTR, err := st.HashTreeRoot(ctx) + require.NoError(t, err) + require.DeepEqual(t, expectedHTR, actualHTR) require.Equal(t, primitives.Slot(0), st.Slot()) shouldExist, err := hist.StateOrError(ctx, hist.slotMap[middle]) require.NoError(t, err) - require.DeepEqual(t, hist.states[hist.slotMap[middle]], shouldExist) + expectedHTR, err = hist.states[hist.slotMap[middle]].HashTreeRoot(ctx) + require.NoError(t, err) + actualHTR, err = shouldExist.HashTreeRoot(ctx) + require.NoError(t, err) + require.DeepEqual(t, expectedHTR, actualHTR) require.Equal(t, middle, shouldExist.Slot()) cantExist, err := hist.StateOrError(ctx, hist.slotMap[end]) diff --git a/config/features/config.go b/config/features/config.go index 0850e60c9b9d..069df6235deb 100644 --- a/config/features/config.go +++ b/config/features/config.go @@ -37,6 +37,7 @@ const disabledFeatureFlag = "Disabled feature flag" // Flags is a struct to represent which features the client will perform on runtime. type Flags struct { // Feature related flags. + EnableExperimentalState bool // EnableExperimentalState turns on the latest and greatest (but potentially unstable) changes to the beacon state. WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory. EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p. DisableReorgLateBlocks bool // DisableReorgLateBlocks disables reorgs of late blocks. @@ -176,6 +177,11 @@ func ConfigureBeaconChain(ctx *cli.Context) error { return err } + if ctx.Bool(enableExperimentalState.Name) { + logEnabled(enableExperimentalState) + cfg.EnableExperimentalState = true + } + if ctx.Bool(writeSSZStateTransitionsFlag.Name) { logEnabled(writeSSZStateTransitionsFlag) cfg.WriteSSZStateTransitions = true diff --git a/config/features/flags.go b/config/features/flags.go index 214affbc0aa9..89605f93e09b 100644 --- a/config/features/flags.go +++ b/config/features/flags.go @@ -33,6 +33,10 @@ var ( Name: "dev", Usage: "Enable experimental features still in development. These features may not be stable.", } + enableExperimentalState = &cli.BoolFlag{ + Name: "enable-experimental-state", + Usage: "Turn on the latest and greatest (but potentially unstable) changes to the beacon state", + } writeSSZStateTransitionsFlag = &cli.BoolFlag{ Name: "interop-write-ssz-state-transitions", Usage: "Write ssz states to disk after attempted state transition", @@ -165,6 +169,7 @@ var ( var devModeFlags = []cli.Flag{ enableVerboseSigVerification, enableEIP4881, + enableExperimentalState, } // ValidatorFlags contains a list of all the feature flags that apply to the validator client. @@ -189,6 +194,7 @@ var E2EValidatorFlags = []string{ // BeaconChainFlags contains a list of all the feature flags that apply to the beacon-chain client. var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []cli.Flag{ devModeFlag, + enableExperimentalState, writeSSZStateTransitionsFlag, disableGRPCConnectionLogging, HoleskyTestnet, diff --git a/consensus-types/types.go b/consensus-types/types.go index 1d00b356a99e..867ddcba621b 100644 --- a/consensus-types/types.go +++ b/consensus-types/types.go @@ -3,6 +3,7 @@ package consensus_types import ( "errors" "fmt" + "sync/atomic" errors2 "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/runtime/version" @@ -13,9 +14,21 @@ var ( ErrNilObjectWrapped = errors.New("attempted to wrap nil object") // ErrUnsupportedField is returned when a getter/setter access is not supported. ErrUnsupportedField = errors.New("unsupported getter") + // ErrOutOfBounds is returned when a slice or array index does not exist. + ErrOutOfBounds = errors.New("index out of bounds") ) // ErrNotSupported constructs a message informing about an unsupported field access. func ErrNotSupported(funcName string, ver int) error { return errors2.Wrap(ErrUnsupportedField, fmt.Sprintf("%s is not supported for %s", funcName, version.String(ver))) } + +// ThreadSafeEnumerator is a thread-safe counter of all objects created since the node's start. +type ThreadSafeEnumerator struct { + counter uint64 +} + +// Inc increments the enumerator and returns the new object count. +func (c *ThreadSafeEnumerator) Inc() uint64 { + return atomic.AddUint64(&c.counter, 1) +} diff --git a/container/multi-value-slice/BUILD.bazel b/container/multi-value-slice/BUILD.bazel index b96cddf8d226..a86da5b4eb75 100644 --- a/container/multi-value-slice/BUILD.bazel +++ b/container/multi-value-slice/BUILD.bazel @@ -5,10 +5,6 @@ go_library( srcs = ["multi_value_slice.go"], importpath = "github.com/prysmaticlabs/prysm/v4/container/multi-value-slice", visibility = ["//visibility:public"], - deps = [ - "//container/multi-value-slice/interfaces:go_default_library", - "@com_github_google_uuid//:go_default_library", - ], ) go_test( @@ -18,6 +14,5 @@ go_test( deps = [ "//testing/assert:go_default_library", "//testing/require:go_default_library", - "@com_github_google_uuid//:go_default_library", ], ) diff --git a/container/multi-value-slice/interfaces/BUILD.bazel b/container/multi-value-slice/interfaces/BUILD.bazel deleted file mode 100644 index f8be9399296c..000000000000 --- a/container/multi-value-slice/interfaces/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@prysm//tools/go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["interfaces.go"], - importpath = "github.com/prysmaticlabs/prysm/v4/container/multi-value-slice/interfaces", - visibility = ["//visibility:public"], - deps = ["@com_github_google_uuid//:go_default_library"], -) diff --git a/container/multi-value-slice/interfaces/interfaces.go b/container/multi-value-slice/interfaces/interfaces.go deleted file mode 100644 index b22ab64de16b..000000000000 --- a/container/multi-value-slice/interfaces/interfaces.go +++ /dev/null @@ -1,9 +0,0 @@ -package interfaces - -import "github.com/google/uuid" - -// Identifiable represents an object that can be uniquely identified by its Id. -type Identifiable interface { - Id() uuid.UUID - SetId(id uuid.UUID) -} diff --git a/container/multi-value-slice/multi_value_slice.go b/container/multi-value-slice/multi_value_slice.go index a19837c2e1c7..5d26c3a7cca9 100644 --- a/container/multi-value-slice/multi_value_slice.go +++ b/container/multi-value-slice/multi_value_slice.go @@ -92,20 +92,25 @@ package mvslice import ( "fmt" "sync" - - "github.com/google/uuid" - "github.com/prysmaticlabs/prysm/v4/container/multi-value-slice/interfaces" ) +// Id is an object identifier. +type Id = uint64 + +// Identifiable represents an object that can be uniquely identified by its Id. +type Identifiable interface { + Id() Id +} + // MultiValueSlice defines an abstraction over all concrete implementations of the generic Slice. -type MultiValueSlice[O interfaces.Identifiable] interface { - Len(obj O) uuid.UUID +type MultiValueSlice[O Identifiable] interface { + Len(obj O) int } // Value defines a single value along with one or more IDs that share this value. type Value[V any] struct { val V - ids []uuid.UUID + ids []uint64 } // MultiValueItem defines a collection of Value items. @@ -119,11 +124,11 @@ type MultiValueItem[V any] struct { // - O interfaces.Identifiable - the type of objects sharing the slice. The constraint is required // because we need a way to compare objects against each other in order to know which objects // values should be accessed. -type Slice[V comparable, O interfaces.Identifiable] struct { +type Slice[V comparable, O Identifiable] struct { sharedItems []V individualItems map[uint64]*MultiValueItem[V] appendedItems []*MultiValueItem[V] - cachedLengths map[uuid.UUID]int + cachedLengths map[uint64]int lock sync.RWMutex } @@ -132,7 +137,7 @@ func (s *Slice[V, O]) Init(items []V) { s.sharedItems = items s.individualItems = map[uint64]*MultiValueItem[V]{} s.appendedItems = []*MultiValueItem[V]{} - s.cachedLengths = map[uuid.UUID]int{} + s.cachedLengths = map[uint64]int{} } // Len returns the number of items for the input object. @@ -283,7 +288,7 @@ func (s *Slice[V, O]) Append(obj O, val V) { defer s.lock.Unlock() if len(s.appendedItems) == 0 { - s.appendedItems = append(s.appendedItems, &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uuid.UUID{obj.Id()}}}}) + s.appendedItems = append(s.appendedItems, &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uint64{obj.Id()}}}}) s.cachedLengths[obj.Id()] = len(s.sharedItems) + 1 return } @@ -306,7 +311,7 @@ func (s *Slice[V, O]) Append(obj O, val V) { } } if newValue { - item.Values = append(item.Values, &Value[V]{val: val, ids: []uuid.UUID{obj.Id()}}) + item.Values = append(item.Values, &Value[V]{val: val, ids: []uint64{obj.Id()}}) } l, ok := s.cachedLengths[obj.Id()] @@ -320,7 +325,7 @@ func (s *Slice[V, O]) Append(obj O, val V) { } } - s.appendedItems = append(s.appendedItems, &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uuid.UUID{obj.Id()}}}}) + s.appendedItems = append(s.appendedItems, &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uint64{obj.Id()}}}}) s.cachedLengths[obj.Id()] = s.cachedLengths[obj.Id()] + 1 } @@ -419,7 +424,7 @@ func (s *Slice[V, O]) updateOriginalItem(obj O, index uint64, val V) { } if !ok { - s.individualItems[index] = &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uuid.UUID{obj.Id()}}}} + s.individualItems[index] = &MultiValueItem[V]{Values: []*Value[V]{{val: val, ids: []uint64{obj.Id()}}}} } else { newValue := true for _, v := range ind.Values { @@ -430,7 +435,7 @@ func (s *Slice[V, O]) updateOriginalItem(obj O, index uint64, val V) { } } if newValue { - ind.Values = append(ind.Values, &Value[V]{val: val, ids: []uuid.UUID{obj.Id()}}) + ind.Values = append(ind.Values, &Value[V]{val: val, ids: []uint64{obj.Id()}}) } } } @@ -464,13 +469,13 @@ func (s *Slice[V, O]) updateAppendedItem(obj O, index uint64, val V) error { } } if newValue { - item.Values = append(item.Values, &Value[V]{val: val, ids: []uuid.UUID{obj.Id()}}) + item.Values = append(item.Values, &Value[V]{val: val, ids: []uint64{obj.Id()}}) } return nil } -func containsId(ids []uuid.UUID, wanted uuid.UUID) (int, bool) { +func containsId(ids []uint64, wanted uint64) (int, bool) { for i, id := range ids { if id == wanted { return i, true diff --git a/container/multi-value-slice/multi_value_slice_test.go b/container/multi-value-slice/multi_value_slice_test.go index 6e03bde04de0..ff0340d4df67 100644 --- a/container/multi-value-slice/multi_value_slice_test.go +++ b/container/multi-value-slice/multi_value_slice_test.go @@ -4,40 +4,31 @@ import ( "math/rand" "testing" - "github.com/google/uuid" "github.com/prysmaticlabs/prysm/v4/testing/assert" "github.com/prysmaticlabs/prysm/v4/testing/require" ) -var ( - id1 = uuid.New() - id2 = uuid.New() - id999 = uuid.New() -) - type testObject struct { - id uuid.UUID - slice *Slice[int, *testObject] + id uint64 } -func (o *testObject) Id() uuid.UUID { +func (o *testObject) Id() uint64 { return o.id } -func (o *testObject) SetId(id uuid.UUID) { +func (o *testObject) SetId(id uint64) { o.id = id } func TestLen(t *testing.T) { s := &Slice[int, *testObject]{} s.Init([]int{1, 2, 3}) - id := uuid.New() - s.cachedLengths[id] = 123 + s.cachedLengths[1] = 123 t.Run("cached", func(t *testing.T) { - assert.Equal(t, 123, s.Len(&testObject{id: id})) + assert.Equal(t, 123, s.Len(&testObject{id: 1})) }) t.Run("not cached", func(t *testing.T) { - assert.Equal(t, 3, s.Len(&testObject{id: uuid.New()})) + assert.Equal(t, 3, s.Len(&testObject{id: 999})) }) } @@ -51,12 +42,12 @@ func TestCopy(t *testing.T) { // - length of destination object is cached s := setup() - src := &testObject{id: id1, slice: s} - dst := &testObject{id: id999, slice: s} + src := &testObject{id: 1} + dst := &testObject{id: 999} s.Copy(src, dst) - assert.Equal(t, (*MultiValueItem[int])(nil), dst.slice.individualItems[0]) + assert.Equal(t, (*MultiValueItem[int])(nil), s.individualItems[0]) assertIndividualFound(t, s, dst.id, 1, 1) assertIndividualFound(t, s, dst.id, 2, 3) assertIndividualFound(t, s, dst.id, 3, 1) @@ -64,7 +55,7 @@ func TestCopy(t *testing.T) { assertAppendedFound(t, s, dst.id, 0, 1) assertAppendedFound(t, s, dst.id, 1, 3) assertAppendedNotFound(t, s, dst.id, 2) - l, ok := s.cachedLengths[id999] + l, ok := s.cachedLengths[999] require.Equal(t, true, ok) assert.Equal(t, 7, l) } @@ -76,8 +67,8 @@ func TestValue(t *testing.T) { // - correct values are returned for an object without appended items s := setup() - first := &testObject{id: id1, slice: s} - second := &testObject{id: id2, slice: s} + first := &testObject{id: 1} + second := &testObject{id: 2} v := s.Value(first) @@ -104,9 +95,8 @@ func TestValue(t *testing.T) { s = &Slice[int, *testObject]{} s.Init([]int{1, 2, 3}) - id := uuid.New() - v = s.Value(&testObject{id: id}) + v = s.Value(&testObject{id: 999}) require.Equal(t, 3, len(v)) assert.Equal(t, 1, v[0]) @@ -122,8 +112,8 @@ func TestAt(t *testing.T) { // - ERROR when index not too large in general, but too large for an object s := setup() - first := &testObject{id: id1, slice: s} - second := &testObject{id: id2, slice: s} + first := &testObject{id: 1} + second := &testObject{id: 2} v, err := s.At(first, 0) require.NoError(t, err) @@ -194,8 +184,8 @@ func TestUpdateAt(t *testing.T) { // - ERROR when index not too large in general, but too large for an object s := setup() - first := &testObject{id: id1, slice: s} - second := &testObject{id: id2, slice: s} + first := &testObject{id: 1} + second := &testObject{id: 2} require.NoError(t, s.UpdateAt(first, 0, 999)) assert.Equal(t, 123, s.sharedItems[0]) @@ -258,8 +248,8 @@ func TestAppend(t *testing.T) { // we want to start with the simplest slice possible s := &Slice[int, *testObject]{} s.Init([]int{0}) - first := &testObject{id: id1, slice: s} - second := &testObject{id: id2, slice: s} + first := &testObject{id: 1} + second := &testObject{id: 2} // append first value ever s.Append(first, 1) @@ -306,7 +296,7 @@ func TestDetach(t *testing.T) { // - length removed from cache s := setup() - obj := &testObject{id: id1, slice: s} + obj := &testObject{id: 1} s.Detach(obj) @@ -352,11 +342,11 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 1, - ids: []uuid.UUID{id1}, + ids: []uint64{1}, }, { val: 2, - ids: []uuid.UUID{id2}, + ids: []uint64{2}, }, }, } @@ -364,7 +354,7 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 3, - ids: []uuid.UUID{id1, id2}, + ids: []uint64{1, 2}, }, }, } @@ -372,7 +362,7 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 1, - ids: []uuid.UUID{id1}, + ids: []uint64{1}, }, }, } @@ -380,7 +370,7 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 2, - ids: []uuid.UUID{id2}, + ids: []uint64{2}, }, }, } @@ -389,11 +379,11 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 1, - ids: []uuid.UUID{id1}, + ids: []uint64{1}, }, { val: 2, - ids: []uuid.UUID{id2}, + ids: []uint64{2}, }, }, }, @@ -401,7 +391,7 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 3, - ids: []uuid.UUID{id1, id2}, + ids: []uint64{1, 2}, }, }, }, @@ -409,18 +399,18 @@ func setup() *Slice[int, *testObject] { Values: []*Value[int]{ { val: 2, - ids: []uuid.UUID{id2}, + ids: []uint64{2}, }, }, }, } - s.cachedLengths[id1] = 7 - s.cachedLengths[id2] = 8 + s.cachedLengths[1] = 7 + s.cachedLengths[2] = 8 return s } -func assertIndividualFound(t *testing.T, slice *Slice[int, *testObject], id uuid.UUID, itemIndex uint64, expected int) { +func assertIndividualFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64, expected int) { found := false for _, v := range slice.individualItems[itemIndex].Values { for _, o := range v.ids { @@ -433,7 +423,7 @@ func assertIndividualFound(t *testing.T, slice *Slice[int, *testObject], id uuid assert.Equal(t, true, found) } -func assertIndividualNotFound(t *testing.T, slice *Slice[int, *testObject], id uuid.UUID, itemIndex uint64) { +func assertIndividualNotFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64) { found := false for _, v := range slice.individualItems[itemIndex].Values { for _, o := range v.ids { @@ -445,7 +435,7 @@ func assertIndividualNotFound(t *testing.T, slice *Slice[int, *testObject], id u assert.Equal(t, false, found) } -func assertAppendedFound(t *testing.T, slice *Slice[int, *testObject], id uuid.UUID, itemIndex uint64, expected int) { +func assertAppendedFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64, expected int) { found := false for _, v := range slice.appendedItems[itemIndex].Values { for _, o := range v.ids { @@ -458,7 +448,7 @@ func assertAppendedFound(t *testing.T, slice *Slice[int, *testObject], id uuid.U assert.Equal(t, true, found) } -func assertAppendedNotFound(t *testing.T, slice *Slice[int, *testObject], id uuid.UUID, itemIndex uint64) { +func assertAppendedNotFound(t *testing.T, slice *Slice[int, *testObject], id uint64, itemIndex uint64) { found := false for _, v := range slice.appendedItems[itemIndex].Values { for _, o := range v.ids { @@ -485,12 +475,11 @@ func BenchmarkValue(b *testing.B) { b.Run("100,000 equal individual items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _100k)) - s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}} + s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}} objs := make([]*testObject, _100k) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_100k)]) @@ -501,9 +490,8 @@ func BenchmarkValue(b *testing.B) { s.Init(make([]int, _100k)) objs := make([]*testObject, _100k) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}} + objs[i] = &testObject{id: uint64(i)} + s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}} } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_100k)]) @@ -512,12 +500,11 @@ func BenchmarkValue(b *testing.B) { b.Run("100,000 shared items and 100,000 equal appended items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _100k)) - s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}}} + s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}} objs := make([]*testObject, _100k) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_100k)]) @@ -529,9 +516,8 @@ func BenchmarkValue(b *testing.B) { s.appendedItems = []*MultiValueItem[int]{} objs := make([]*testObject, _100k) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}}) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_100k)]) @@ -547,12 +533,11 @@ func BenchmarkValue(b *testing.B) { b.Run("1,000,000 equal individual items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _1m)) - s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}} + s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}} objs := make([]*testObject, _1m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_1m)]) @@ -563,9 +548,8 @@ func BenchmarkValue(b *testing.B) { s.Init(make([]int, _1m)) objs := make([]*testObject, _1m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}} + objs[i] = &testObject{id: uint64(i)} + s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}} } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_1m)]) @@ -574,12 +558,11 @@ func BenchmarkValue(b *testing.B) { b.Run("1,000,000 shared items and 1,000,000 equal appended items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _1m)) - s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}}} + s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}} objs := make([]*testObject, _1m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_1m)]) @@ -591,9 +574,8 @@ func BenchmarkValue(b *testing.B) { s.appendedItems = []*MultiValueItem[int]{} objs := make([]*testObject, _1m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}}) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_1m)]) @@ -609,12 +591,11 @@ func BenchmarkValue(b *testing.B) { b.Run("10,000,000 equal individual items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _10m)) - s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}} + s.individualItems[0] = &MultiValueItem[int]{Values: []*Value[int]{{val: 999, ids: []uint64{}}}} objs := make([]*testObject, _10m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.individualItems[0].Values[0].ids = append(s.individualItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_10m)]) @@ -625,9 +606,8 @@ func BenchmarkValue(b *testing.B) { s.Init(make([]int, _10m)) objs := make([]*testObject, _10m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}} + objs[i] = &testObject{id: uint64(i)} + s.individualItems[uint64(i)] = &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}} } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_10m)]) @@ -636,12 +616,11 @@ func BenchmarkValue(b *testing.B) { b.Run("10,000,000 shared items and 10,000,000 equal appended items", func(b *testing.B) { s := &Slice[int, *testObject]{} s.Init(make([]int, _10m)) - s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uuid.UUID{}}}}} + s.appendedItems = []*MultiValueItem[int]{{Values: []*Value[int]{{val: 999, ids: []uint64{}}}}} objs := make([]*testObject, _10m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, id) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems[0].Values[0].ids = append(s.appendedItems[0].Values[0].ids, uint64(i)) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_10m)]) @@ -653,9 +632,8 @@ func BenchmarkValue(b *testing.B) { s.appendedItems = []*MultiValueItem[int]{} objs := make([]*testObject, _10m) for i := 0; i < len(objs); i++ { - id := uuid.New() - objs[i] = &testObject{id: id, slice: s} - s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uuid.UUID{id}}}}) + objs[i] = &testObject{id: uint64(i)} + s.appendedItems = append(s.appendedItems, &MultiValueItem[int]{Values: []*Value[int]{{val: i, ids: []uint64{uint64(i)}}}}) } for i := 0; i < b.N; i++ { s.Value(objs[rand.Intn(_10m)]) diff --git a/runtime/interop/premine-state.go b/runtime/interop/premine-state.go index a2b7aa334942..b2babe0ca068 100644 --- a/runtime/interop/premine-state.go +++ b/runtime/interop/premine-state.go @@ -89,24 +89,65 @@ func (s *PremineGenesisConfig) prepare(ctx context.Context) (state.BeaconState, func (s *PremineGenesisConfig) empty() (state.BeaconState, error) { var e state.BeaconState var err error + + bRoots := make([][]byte, fieldparams.BlockRootsLength) + for i := range bRoots { + bRoots[i] = bytesutil.PadTo([]byte{}, 32) + } + sRoots := make([][]byte, fieldparams.StateRootsLength) + for i := range sRoots { + sRoots[i] = bytesutil.PadTo([]byte{}, 32) + } + mixes := make([][]byte, fieldparams.RandaoMixesLength) + for i := range mixes { + mixes[i] = bytesutil.PadTo([]byte{}, 32) + } + switch s.Version { case version.Phase0: - e, err = state_native.InitializeFromProtoPhase0(ðpb.BeaconState{}) + e, err = state_native.InitializeFromProtoPhase0(ðpb.BeaconState{ + BlockRoots: bRoots, + StateRoots: sRoots, + RandaoMixes: mixes, + Balances: []uint64{}, + Validators: []*ethpb.Validator{}, + }) if err != nil { return nil, err } case version.Altair: - e, err = state_native.InitializeFromProtoAltair(ðpb.BeaconStateAltair{}) + e, err = state_native.InitializeFromProtoAltair(ðpb.BeaconStateAltair{ + BlockRoots: bRoots, + StateRoots: sRoots, + RandaoMixes: mixes, + Balances: []uint64{}, + InactivityScores: []uint64{}, + Validators: []*ethpb.Validator{}, + }) if err != nil { return nil, err } case version.Bellatrix: - e, err = state_native.InitializeFromProtoBellatrix(ðpb.BeaconStateBellatrix{}) + e, err = state_native.InitializeFromProtoBellatrix(ðpb.BeaconStateBellatrix{ + BlockRoots: bRoots, + StateRoots: sRoots, + RandaoMixes: mixes, + Balances: []uint64{}, + InactivityScores: []uint64{}, + Validators: []*ethpb.Validator{}, + }) if err != nil { return nil, err } case version.Capella: - e, err = state_native.InitializeFromProtoCapella(ðpb.BeaconStateCapella{}) + e, err = state_native.InitializeFromProtoCapella(ðpb.BeaconStateCapella{ + BlockRoots: bRoots, + StateRoots: sRoots, + RandaoMixes: mixes, + Balances: []uint64{}, + InactivityScores: []uint64{}, + Validators: []*ethpb.Validator{}, + }) if err != nil { return nil, err } @@ -121,12 +162,6 @@ func (s *PremineGenesisConfig) empty() (state.BeaconState, error) { if err = e.SetSlot(0); err != nil { return nil, err } - if err = e.SetValidators([]*ethpb.Validator{}); err != nil { - return nil, err - } - if err = e.SetBalances([]uint64{}); err != nil { - return nil, err - } if err = e.SetJustificationBits([]byte{0}); err != nil { return nil, err }