diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index 08c1075712..597d3e6496 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "getters_sync_committee.go", "getters_validator.go", "getters_withdrawal.go", + "gloas.go", "hasher.go", "multi_value_slices.go", "proofs.go", diff --git a/beacon-chain/state/state-native/beacon_state.go b/beacon-chain/state/state-native/beacon_state.go index 07b950871c..45b082183e 100644 --- a/beacon-chain/state/state-native/beacon_state.go +++ b/beacon-chain/state/state-native/beacon_state.go @@ -70,6 +70,14 @@ type BeaconState struct { pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] proposerLookahead []primitives.ValidatorIndex // proposer_look_ahead: List[uint64, (MIN_LOOKAHEAD + 1)*SLOTS_PER_EPOCH] + // Gloas fields + latestExecutionPayloadBid *ethpb.ExecutionPayloadBid + executionPayloadAvailability []byte + builderPendingPayments []*ethpb.BuilderPendingPayment + builderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal + latestBlockHash []byte + latestWithdrawalsRoot []byte + id uint64 lock sync.RWMutex dirtyFields map[types.FieldIndex]bool @@ -125,6 +133,12 @@ type beaconStateMarshalable struct { PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"` PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"` ProposerLookahead []primitives.ValidatorIndex `json:"proposer_look_ahead" yaml:"proposer_look_ahead"` + LatestExecutionPayloadBid *ethpb.ExecutionPayloadBid `json:"latest_execution_payload_bid" yaml:"latest_execution_payload_bid"` + ExecutionPayloadAvailability []byte `json:"execution_payload_availability" yaml:"execution_payload_availability"` + BuilderPendingPayments []*ethpb.BuilderPendingPayment `json:"builder_pending_payments" yaml:"builder_pending_payments"` + BuilderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal `json:"builder_pending_withdrawals" yaml:"builder_pending_withdrawals"` + LatestBlockHash []byte `json:"latest_block_hash" yaml:"latest_block_hash"` + LatestWithdrawalsRoot []byte `json:"latest_withdrawals_root" yaml:"latest_withdrawals_root"` } func (b *BeaconState) MarshalJSON() ([]byte, error) { @@ -179,6 +193,12 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) { PendingPartialWithdrawals: b.pendingPartialWithdrawals, PendingConsolidations: b.pendingConsolidations, ProposerLookahead: b.proposerLookahead, + LatestExecutionPayloadBid: b.latestExecutionPayloadBid, + ExecutionPayloadAvailability: b.executionPayloadAvailability, + BuilderPendingPayments: b.builderPendingPayments, + BuilderPendingWithdrawals: b.builderPendingWithdrawals, + LatestBlockHash: b.latestBlockHash, + LatestWithdrawalsRoot: b.latestWithdrawalsRoot, } return json.Marshal(marshalable) } diff --git a/beacon-chain/state/state-native/getters_state.go b/beacon-chain/state/state-native/getters_state.go index 86a503fdc2..a6a3529f0c 100644 --- a/beacon-chain/state/state-native/getters_state.go +++ b/beacon-chain/state/state-native/getters_state.go @@ -259,6 +259,57 @@ func (b *BeaconState) ToProtoUnsafe() any { PendingConsolidations: b.pendingConsolidations, ProposerLookahead: lookahead, } + case version.Gloas: + lookahead := make([]uint64, len(b.proposerLookahead)) + for i, v := range b.proposerLookahead { + lookahead[i] = uint64(v) + } + + return ðpb.BeaconStateGloas{ + GenesisTime: b.genesisTime, + GenesisValidatorsRoot: gvrCopy[:], + Slot: b.slot, + Fork: b.fork, + LatestBlockHeader: b.latestBlockHeader, + BlockRoots: br, + StateRoots: sr, + HistoricalRoots: b.historicalRoots.Slice(), + Eth1Data: b.eth1Data, + Eth1DataVotes: b.eth1DataVotes, + Eth1DepositIndex: b.eth1DepositIndex, + Validators: vals, + Balances: bals, + RandaoMixes: rm, + Slashings: b.slashings, + PreviousEpochParticipation: b.previousEpochParticipation, + CurrentEpochParticipation: b.currentEpochParticipation, + JustificationBits: b.justificationBits, + PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint, + CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint, + FinalizedCheckpoint: b.finalizedCheckpoint, + InactivityScores: inactivityScores, + CurrentSyncCommittee: b.currentSyncCommittee, + NextSyncCommittee: b.nextSyncCommittee, + LatestExecutionPayloadBid: b.latestExecutionPayloadBid, + NextWithdrawalIndex: b.nextWithdrawalIndex, + NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex, + HistoricalSummaries: b.historicalSummaries, + DepositRequestsStartIndex: b.depositRequestsStartIndex, + DepositBalanceToConsume: b.depositBalanceToConsume, + ExitBalanceToConsume: b.exitBalanceToConsume, + EarliestExitEpoch: b.earliestExitEpoch, + ConsolidationBalanceToConsume: b.consolidationBalanceToConsume, + EarliestConsolidationEpoch: b.earliestConsolidationEpoch, + PendingDeposits: b.pendingDeposits, + PendingPartialWithdrawals: b.pendingPartialWithdrawals, + PendingConsolidations: b.pendingConsolidations, + ProposerLookahead: lookahead, + ExecutionPayloadAvailability: b.executionPayloadAvailability, + BuilderPendingPayments: b.builderPendingPayments, + BuilderPendingWithdrawals: b.builderPendingWithdrawals, + LatestBlockHash: b.latestBlockHash, + LatestWithdrawalsRoot: b.latestWithdrawalsRoot, + } default: return nil } @@ -510,6 +561,57 @@ func (b *BeaconState) ToProto() any { PendingConsolidations: b.pendingConsolidationsVal(), ProposerLookahead: lookahead, } + case version.Gloas: + lookahead := make([]uint64, len(b.proposerLookahead)) + for i, v := range b.proposerLookahead { + lookahead[i] = uint64(v) + } + + return ðpb.BeaconStateGloas{ + GenesisTime: b.genesisTime, + GenesisValidatorsRoot: gvrCopy[:], + Slot: b.slot, + Fork: b.forkVal(), + LatestBlockHeader: b.latestBlockHeaderVal(), + BlockRoots: br, + StateRoots: sr, + HistoricalRoots: b.historicalRoots.Slice(), + Eth1Data: b.eth1DataVal(), + Eth1DataVotes: b.eth1DataVotesVal(), + Eth1DepositIndex: b.eth1DepositIndex, + Validators: b.validatorsVal(), + Balances: b.balancesVal(), + RandaoMixes: rm, + Slashings: b.slashingsVal(), + PreviousEpochParticipation: b.previousEpochParticipationVal(), + CurrentEpochParticipation: b.currentEpochParticipationVal(), + JustificationBits: b.justificationBitsVal(), + PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(), + CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(), + FinalizedCheckpoint: b.finalizedCheckpointVal(), + InactivityScores: b.inactivityScoresVal(), + CurrentSyncCommittee: b.currentSyncCommitteeVal(), + NextSyncCommittee: b.nextSyncCommitteeVal(), + LatestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(), + NextWithdrawalIndex: b.nextWithdrawalIndex, + NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex, + HistoricalSummaries: b.historicalSummariesVal(), + DepositRequestsStartIndex: b.depositRequestsStartIndex, + DepositBalanceToConsume: b.depositBalanceToConsume, + ExitBalanceToConsume: b.exitBalanceToConsume, + EarliestExitEpoch: b.earliestExitEpoch, + ConsolidationBalanceToConsume: b.consolidationBalanceToConsume, + EarliestConsolidationEpoch: b.earliestConsolidationEpoch, + PendingDeposits: b.pendingDepositsVal(), + PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(), + PendingConsolidations: b.pendingConsolidationsVal(), + ProposerLookahead: lookahead, + ExecutionPayloadAvailability: b.executionPayloadAvailabilityVal(), + BuilderPendingPayments: b.builderPendingPaymentsVal(), + BuilderPendingWithdrawals: b.builderPendingWithdrawalsVal(), + LatestBlockHash: b.latestBlockHashVal(), + LatestWithdrawalsRoot: b.latestWithdrawalsRootVal(), + } default: return nil } diff --git a/beacon-chain/state/state-native/gloas.go b/beacon-chain/state/state-native/gloas.go new file mode 100644 index 0000000000..b438de3dad --- /dev/null +++ b/beacon-chain/state/state-native/gloas.go @@ -0,0 +1,74 @@ +package state_native + +import ( + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" +) + +// executionPayloadAvailabilityVal returns a copy of the execution payload availability. +// This assumes that a lock is already held on BeaconState. +func (b *BeaconState) executionPayloadAvailabilityVal() []byte { + if b.executionPayloadAvailability == nil { + return nil + } + + availability := make([]byte, len(b.executionPayloadAvailability)) + copy(availability, b.executionPayloadAvailability) + + return availability +} + +// builderPendingPaymentsVal returns a copy of the builder pending payments. +// This assumes that a lock is already held on BeaconState. +func (b *BeaconState) builderPendingPaymentsVal() []*ethpb.BuilderPendingPayment { + if b.builderPendingPayments == nil { + return nil + } + + payments := make([]*ethpb.BuilderPendingPayment, len(b.builderPendingPayments)) + for i, payment := range b.builderPendingPayments { + payments[i] = payment.Copy() + } + + return payments +} + +// builderPendingWithdrawalsVal returns a copy of the builder pending withdrawals. +// This assumes that a lock is already held on BeaconState. +func (b *BeaconState) builderPendingWithdrawalsVal() []*ethpb.BuilderPendingWithdrawal { + if b.builderPendingWithdrawals == nil { + return nil + } + + withdrawals := make([]*ethpb.BuilderPendingWithdrawal, len(b.builderPendingWithdrawals)) + for i, withdrawal := range b.builderPendingWithdrawals { + withdrawals[i] = withdrawal.Copy() + } + + return withdrawals +} + +// latestBlockHashVal returns a copy of the latest block hash. +// This assumes that a lock is already held on BeaconState. +func (b *BeaconState) latestBlockHashVal() []byte { + if b.latestBlockHash == nil { + return nil + } + + hash := make([]byte, len(b.latestBlockHash)) + copy(hash, b.latestBlockHash) + + return hash +} + +// latestWithdrawalsRootVal returns a copy of the latest withdrawals root. +// This assumes that a lock is already held on BeaconState. +func (b *BeaconState) latestWithdrawalsRootVal() []byte { + if b.latestWithdrawalsRoot == nil { + return nil + } + + root := make([]byte, len(b.latestWithdrawalsRoot)) + copy(root, b.latestWithdrawalsRoot) + + return root +} diff --git a/beacon-chain/state/state-native/hasher.go b/beacon-chain/state/state-native/hasher.go index c0c19f9f0e..95d2cfaa13 100644 --- a/beacon-chain/state/state-native/hasher.go +++ b/beacon-chain/state/state-native/hasher.go @@ -43,6 +43,8 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateElectraFieldCount) case version.Fulu: fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateFuluFieldCount) + case version.Gloas: + fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateGloasFieldCount) default: return nil, fmt.Errorf("unknown state version %s", version.String(state.version)) } @@ -245,7 +247,7 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b fieldRoots[types.LatestExecutionPayloadHeaderCapella.RealPosition()] = executionPayloadRoot[:] } - if state.version >= version.Deneb { + if state.version >= version.Deneb && state.version < version.Gloas { // Execution payload root. executionPayloadRoot, err := state.latestExecutionPayloadHeaderDeneb.HashTreeRoot() if err != nil { @@ -254,6 +256,16 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b fieldRoots[types.LatestExecutionPayloadHeaderDeneb.RealPosition()] = executionPayloadRoot[:] } + if state.version >= version.Gloas { + // Execution payload bid root for Gloas. + bidRoot, err := state.latestExecutionPayloadBid.HashTreeRoot() + if err != nil { + return nil, err + } + + fieldRoots[types.LatestExecutionPayloadBid.RealPosition()] = bidRoot[:] + } + if state.version >= version.Capella { // Next withdrawal index root. nextWithdrawalIndexRoot := make([]byte, 32) @@ -328,5 +340,34 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b } fieldRoots[types.ProposerLookahead.RealPosition()] = proposerLookaheadRoot[:] } + + if state.version >= version.Gloas { + epaRoot, err := stateutil.ExecutionPayloadAvailabilityRoot(state.executionPayloadAvailability) + if err != nil { + return nil, errors.Wrap(err, "could not compute execution payload availability merkleization") + } + + fieldRoots[types.ExecutionPayloadAvailability.RealPosition()] = epaRoot[:] + + bppRoot, err := stateutil.BuilderPendingPaymentsRoot(state.builderPendingPayments) + if err != nil { + return nil, errors.Wrap(err, "could not compute builder pending payments merkleization") + } + + fieldRoots[types.BuilderPendingPayments.RealPosition()] = bppRoot[:] + + bpwRoot, err := stateutil.BuilderPendingWithdrawalsRoot(state.builderPendingWithdrawals) + if err != nil { + return nil, errors.Wrap(err, "could not compute builder pending withdrawals merkleization") + } + + fieldRoots[types.BuilderPendingWithdrawals.RealPosition()] = bpwRoot[:] + + lbhRoot := bytesutil.ToBytes32(state.latestBlockHash) + fieldRoots[types.LatestBlockHash.RealPosition()] = lbhRoot[:] + + lwrRoot := bytesutil.ToBytes32(state.latestWithdrawalsRoot) + fieldRoots[types.LatestWithdrawalsRoot.RealPosition()] = lwrRoot[:] + } return fieldRoots, nil } diff --git a/beacon-chain/state/state-native/state_trie.go b/beacon-chain/state/state-native/state_trie.go index d9eca3866e..3e6f465e2a 100644 --- a/beacon-chain/state/state-native/state_trie.go +++ b/beacon-chain/state/state-native/state_trie.go @@ -79,24 +79,25 @@ var ( bellatrixFields = append(altairFields, types.LatestExecutionPayloadHeader) - capellaFields = append( - altairFields, - types.LatestExecutionPayloadHeaderCapella, + withdrawalAndHistoricalSummaryFields = []types.FieldIndex{ types.NextWithdrawalIndex, types.NextWithdrawalValidatorIndex, types.HistoricalSummaries, - ) + } - denebFields = append( + capellaFields = slices.Concat( altairFields, - types.LatestExecutionPayloadHeaderDeneb, - types.NextWithdrawalIndex, - types.NextWithdrawalValidatorIndex, - types.HistoricalSummaries, + []types.FieldIndex{types.LatestExecutionPayloadHeaderCapella}, + withdrawalAndHistoricalSummaryFields, ) - electraFields = append( - denebFields, + denebFields = slices.Concat( + altairFields, + []types.FieldIndex{types.LatestExecutionPayloadHeaderDeneb}, + withdrawalAndHistoricalSummaryFields, + ) + + electraAdditionalFields = []types.FieldIndex{ types.DepositRequestsStartIndex, types.DepositBalanceToConsume, types.ExitBalanceToConsume, @@ -106,12 +107,34 @@ var ( types.PendingDeposits, types.PendingPartialWithdrawals, types.PendingConsolidations, + } + + electraFields = slices.Concat( + denebFields, + electraAdditionalFields, ) fuluFields = append( electraFields, types.ProposerLookahead, ) + + gloasAdditionalFields = []types.FieldIndex{ + types.ExecutionPayloadAvailability, + types.BuilderPendingPayments, + types.BuilderPendingWithdrawals, + types.LatestBlockHash, + types.LatestWithdrawalsRoot, + } + + gloasFields = slices.Concat( + altairFields, + []types.FieldIndex{types.LatestExecutionPayloadBid}, + withdrawalAndHistoricalSummaryFields, + electraAdditionalFields, + []types.FieldIndex{types.ProposerLookahead}, + gloasAdditionalFields, + ) ) const ( @@ -122,6 +145,7 @@ const ( denebSharedFieldRefCount = 7 electraSharedFieldRefCount = 10 fuluSharedFieldRefCount = 11 + gloasSharedFieldRefCount = 12 // Adds PendingBuilderWithdrawal to the shared-ref set and LatestExecutionPayloadHeader is removed ) // InitializeFromProtoPhase0 the beacon state from a protobuf representation. @@ -159,6 +183,11 @@ func InitializeFromProtoFulu(st *ethpb.BeaconStateFulu) (state.BeaconState, erro return InitializeFromProtoUnsafeFulu(proto.Clone(st).(*ethpb.BeaconStateFulu)) } +// InitializeFromProtoGloas the beacon state from a protobuf representation. +func InitializeFromProtoGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) { + return InitializeFromProtoUnsafeGloas(proto.Clone(st).(*ethpb.BeaconStateGloas)) +} + // InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields // and sets them as fields of the BeaconState type. func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, error) { @@ -736,6 +765,111 @@ func InitializeFromProtoUnsafeFulu(st *ethpb.BeaconStateFulu) (state.BeaconState return b, nil } +// InitializeFromProtoUnsafeGloas directly uses the beacon state protobuf fields +// and sets them as fields of the BeaconState type. +func InitializeFromProtoUnsafeGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) { + if st == nil { + return nil, errors.New("received nil state") + } + + hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots))) + for i, r := range st.HistoricalRoots { + hRoots[i] = bytesutil.ToBytes32(r) + } + + proposerLookahead := make([]primitives.ValidatorIndex, len(st.ProposerLookahead)) + for i, v := range st.ProposerLookahead { + proposerLookahead[i] = primitives.ValidatorIndex(v) + } + + fieldCount := params.BeaconConfig().BeaconStateGloasFieldCount + b := &BeaconState{ + version: version.Gloas, + genesisTime: st.GenesisTime, + genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot), + slot: st.Slot, + fork: st.Fork, + latestBlockHeader: st.LatestBlockHeader, + historicalRoots: hRoots, + eth1Data: st.Eth1Data, + eth1DataVotes: st.Eth1DataVotes, + eth1DepositIndex: st.Eth1DepositIndex, + slashings: st.Slashings, + previousEpochParticipation: st.PreviousEpochParticipation, + currentEpochParticipation: st.CurrentEpochParticipation, + justificationBits: st.JustificationBits, + previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint, + currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint, + finalizedCheckpoint: st.FinalizedCheckpoint, + currentSyncCommittee: st.CurrentSyncCommittee, + nextSyncCommittee: st.NextSyncCommittee, + nextWithdrawalIndex: st.NextWithdrawalIndex, + nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex, + historicalSummaries: st.HistoricalSummaries, + depositRequestsStartIndex: st.DepositRequestsStartIndex, + depositBalanceToConsume: st.DepositBalanceToConsume, + exitBalanceToConsume: st.ExitBalanceToConsume, + earliestExitEpoch: st.EarliestExitEpoch, + consolidationBalanceToConsume: st.ConsolidationBalanceToConsume, + earliestConsolidationEpoch: st.EarliestConsolidationEpoch, + pendingDeposits: st.PendingDeposits, + pendingPartialWithdrawals: st.PendingPartialWithdrawals, + pendingConsolidations: st.PendingConsolidations, + proposerLookahead: proposerLookahead, + latestExecutionPayloadBid: st.LatestExecutionPayloadBid, + executionPayloadAvailability: st.ExecutionPayloadAvailability, + builderPendingPayments: st.BuilderPendingPayments, + builderPendingWithdrawals: st.BuilderPendingWithdrawals, + latestBlockHash: st.LatestBlockHash, + latestWithdrawalsRoot: st.LatestWithdrawalsRoot, + 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), + } + + 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, gloasSharedFieldRefCount) + + for _, f := range gloasFields { + b.dirtyFields[f] = true + b.rebuildTrie[f] = true + b.dirtyIndices[f] = []uint64{} + + trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0) + if err != nil { + return nil, err + } + + b.stateFieldLeaves[f] = trie + } + + // Initialize field reference tracking for shared data. + b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1) + b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1) + b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1) + b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1) + b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1) + b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1) + b.sharedFieldReferences[types.ProposerLookahead] = stateutil.NewRef(1) + b.sharedFieldReferences[types.BuilderPendingWithdrawals] = stateutil.NewRef(1) // New in Gloas. + + state.Count.Inc() + // Finalizer runs when dst is being destroyed in garbage collection. + runtime.SetFinalizer(b, finalizerCleanup) + + return b, nil +} + // Copy returns a deep copy of the beacon state. func (b *BeaconState) Copy() state.BeaconState { b.lock.RLock() @@ -757,6 +891,8 @@ func (b *BeaconState) Copy() state.BeaconState { fieldCount = params.BeaconConfig().BeaconStateElectraFieldCount case version.Fulu: fieldCount = params.BeaconConfig().BeaconStateFuluFieldCount + case version.Gloas: + fieldCount = params.BeaconConfig().BeaconStateGloasFieldCount } dst := &BeaconState{ @@ -811,6 +947,12 @@ func (b *BeaconState) Copy() state.BeaconState { latestExecutionPayloadHeader: b.latestExecutionPayloadHeader.Copy(), latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella.Copy(), latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb.Copy(), + latestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(), + executionPayloadAvailability: b.executionPayloadAvailabilityVal(), + builderPendingPayments: b.builderPendingPaymentsVal(), + builderPendingWithdrawals: b.builderPendingWithdrawalsVal(), + latestBlockHash: b.latestBlockHashVal(), + latestWithdrawalsRoot: b.latestWithdrawalsRootVal(), id: types.Enumerator.Inc(), @@ -847,6 +989,8 @@ func (b *BeaconState) Copy() state.BeaconState { dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, electraSharedFieldRefCount) case version.Fulu: dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, fuluSharedFieldRefCount) + case version.Gloas: + dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, gloasSharedFieldRefCount) } for field, ref := range b.sharedFieldReferences { @@ -942,6 +1086,8 @@ func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error { b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateElectraFieldCount) case version.Fulu: b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFuluFieldCount) + case version.Gloas: + b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateGloasFieldCount) default: return fmt.Errorf("unknown state version (%s) when computing dirty fields in merklization", version.String(b.version)) } @@ -1180,6 +1326,19 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex) return stateutil.PendingConsolidationsRoot(b.pendingConsolidations) case types.ProposerLookahead: return stateutil.ProposerLookaheadRoot(b.proposerLookahead) + case types.LatestExecutionPayloadBid: + return b.latestExecutionPayloadBid.HashTreeRoot() + case types.ExecutionPayloadAvailability: + return stateutil.ExecutionPayloadAvailabilityRoot(b.executionPayloadAvailability) + + case types.BuilderPendingPayments: + return stateutil.BuilderPendingPaymentsRoot(b.builderPendingPayments) + case types.BuilderPendingWithdrawals: + return stateutil.BuilderPendingWithdrawalsRoot(b.builderPendingWithdrawals) + case types.LatestBlockHash: + return bytesutil.ToBytes32(b.latestBlockHash), nil + case types.LatestWithdrawalsRoot: + return bytesutil.ToBytes32(b.latestWithdrawalsRoot), nil } return [32]byte{}, errors.New("invalid field index provided") } diff --git a/beacon-chain/state/state-native/types/types.go b/beacon-chain/state/state-native/types/types.go index 2d7aa0fb0a..ea09d54c91 100644 --- a/beacon-chain/state/state-native/types/types.go +++ b/beacon-chain/state/state-native/types/types.go @@ -88,6 +88,8 @@ func (f FieldIndex) String() string { return "latestExecutionPayloadHeaderCapella" case LatestExecutionPayloadHeaderDeneb: return "latestExecutionPayloadHeaderDeneb" + case LatestExecutionPayloadBid: + return "latestExecutionPayloadBid" case NextWithdrawalIndex: return "nextWithdrawalIndex" case NextWithdrawalValidatorIndex: @@ -114,6 +116,16 @@ func (f FieldIndex) String() string { return "pendingConsolidations" case ProposerLookahead: return "proposerLookahead" + case ExecutionPayloadAvailability: + return "executionPayloadAvailability" + case BuilderPendingPayments: + return "builderPendingPayments" + case BuilderPendingWithdrawals: + return "builderPendingWithdrawals" + case LatestBlockHash: + return "latestBlockHash" + case LatestWithdrawalsRoot: + return "latestWithdrawalsRoot" default: return fmt.Sprintf("unknown field index number: %d", f) } @@ -171,7 +183,7 @@ func (f FieldIndex) RealPosition() int { return 22 case NextSyncCommittee: return 23 - case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb: + case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb, LatestExecutionPayloadBid: return 24 case NextWithdrawalIndex: return 25 @@ -199,6 +211,16 @@ func (f FieldIndex) RealPosition() int { return 36 case ProposerLookahead: return 37 + case ExecutionPayloadAvailability: + return 38 + case BuilderPendingPayments: + return 39 + case BuilderPendingWithdrawals: + return 40 + case LatestBlockHash: + return 41 + case LatestWithdrawalsRoot: + return 42 default: return -1 } @@ -251,6 +273,7 @@ const ( LatestExecutionPayloadHeader LatestExecutionPayloadHeaderCapella LatestExecutionPayloadHeaderDeneb + LatestExecutionPayloadBid // Gloas: EIP-7732 NextWithdrawalIndex NextWithdrawalValidatorIndex HistoricalSummaries @@ -264,6 +287,11 @@ const ( PendingPartialWithdrawals // Electra: EIP-7251 PendingConsolidations // Electra: EIP-7251 ProposerLookahead // Fulu: EIP-7917 + ExecutionPayloadAvailability // Gloas: EIP-7732 + BuilderPendingPayments // Gloas: EIP-7732 + BuilderPendingWithdrawals // Gloas: EIP-7732 + LatestBlockHash // Gloas: EIP-7732 + LatestWithdrawalsRoot // Gloas: EIP-7732 ) // Enumerator keeps track of the number of states created since the node's start. diff --git a/beacon-chain/state/stateutil/BUILD.bazel b/beacon-chain/state/stateutil/BUILD.bazel index 30762e02b1..7d711446b2 100644 --- a/beacon-chain/state/stateutil/BUILD.bazel +++ b/beacon-chain/state/stateutil/BUILD.bazel @@ -4,7 +4,10 @@ go_library( name = "go_default_library", srcs = [ "block_header_root.go", + "builder_pending_payments_root.go", + "builder_pending_withdrawals_root.go", "eth1_root.go", + "execution_payload_availability_root.go", "field_root_attestation.go", "field_root_eth1.go", "field_root_validator.go", diff --git a/beacon-chain/state/stateutil/builder_pending_payments_root.go b/beacon-chain/state/stateutil/builder_pending_payments_root.go new file mode 100644 index 0000000000..033ea2be6e --- /dev/null +++ b/beacon-chain/state/stateutil/builder_pending_payments_root.go @@ -0,0 +1,22 @@ +package stateutil + +import ( + "github.com/OffchainLabs/prysm/v7/encoding/ssz" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" +) + +// BuilderPendingPaymentsRoot computes the merkle root of a slice of BuilderPendingPayment. +func BuilderPendingPaymentsRoot(slice []*ethpb.BuilderPendingPayment) ([32]byte, error) { + roots := make([][32]byte, len(slice)) + + for i, payment := range slice { + r, err := payment.HashTreeRoot() + if err != nil { + return [32]byte{}, err + } + + roots[i] = r + } + + return ssz.MerkleizeVector(roots, uint64(len(roots))), nil +} diff --git a/beacon-chain/state/stateutil/builder_pending_withdrawals_root.go b/beacon-chain/state/stateutil/builder_pending_withdrawals_root.go new file mode 100644 index 0000000000..7e9aebeb38 --- /dev/null +++ b/beacon-chain/state/stateutil/builder_pending_withdrawals_root.go @@ -0,0 +1,12 @@ +package stateutil + +import ( + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/encoding/ssz" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" +) + +// BuilderPendingWithdrawalsRoot computes the SSZ root of a slice of BuilderPendingWithdrawal. +func BuilderPendingWithdrawalsRoot(slice []*ethpb.BuilderPendingWithdrawal) ([32]byte, error) { + return ssz.SliceRoot(slice, fieldparams.BuilderPendingWithdrawalsLimit) +} diff --git a/beacon-chain/state/stateutil/execution_payload_availability_root.go b/beacon-chain/state/stateutil/execution_payload_availability_root.go new file mode 100644 index 0000000000..03849b7f6d --- /dev/null +++ b/beacon-chain/state/stateutil/execution_payload_availability_root.go @@ -0,0 +1,25 @@ +package stateutil + +import ( + "fmt" + + "github.com/OffchainLabs/prysm/v7/encoding/ssz" +) + +// ExecutionPayloadAvailabilityRoot computes the merkle root of an execution payload availability bitvector. +func ExecutionPayloadAvailabilityRoot(bitvector []byte) ([32]byte, error) { + chunkCount := (len(bitvector) + 31) / 32 + chunks := make([][32]byte, chunkCount) + + for i := range chunks { + start := i * 32 + end := min(start+32, len(bitvector)) + copy(chunks[i][:], bitvector[start:end]) + } + + root, err := ssz.BitwiseMerkleize(chunks, uint64(len(chunks)), uint64(len(chunks))) + if err != nil { + return [32]byte{}, fmt.Errorf("could not merkleize execution payload availability: %w", err) + } + return root, nil +} diff --git a/changelog/ttsao_add-gloas-beacon-state.md b/changelog/ttsao_add-gloas-beacon-state.md new file mode 100644 index 0000000000..5fa0df53a6 --- /dev/null +++ b/changelog/ttsao_add-gloas-beacon-state.md @@ -0,0 +1,3 @@ +### Added + +- Implement Gloas state \ No newline at end of file diff --git a/config/fieldparams/mainnet.go b/config/fieldparams/mainnet.go index c3a5e7eaba..a52995e2f2 100644 --- a/config/fieldparams/mainnet.go +++ b/config/fieldparams/mainnet.go @@ -44,6 +44,7 @@ const ( MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block. MaxRandomByte = uint64(1<<8 - 1) // MaxRandomByte defines max for a random byte using for proposer and sync committee sampling. MaxRandomValueElectra = uint64(1<<16 - 1) // MaxRandomValueElectra defines max for a random value using for proposer and sync committee sampling. + BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals. // Introduced in Fulu network upgrade. NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network. diff --git a/config/fieldparams/minimal.go b/config/fieldparams/minimal.go index 5f2ffd6ad3..d0f231e60f 100644 --- a/config/fieldparams/minimal.go +++ b/config/fieldparams/minimal.go @@ -44,6 +44,7 @@ const ( MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block. MaxRandomByte = uint64(1<<8 - 1) // Maximum value for a random value using for proposer and sync committee sampling. MaxRandomValueElectra = uint64(1<<16 - 1) // Maximum value for a random value using for proposer and sync committee sampling. + BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals. // Introduced in Fulu network upgrade. NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network. diff --git a/config/params/config.go b/config/params/config.go index 17d7a0b4a3..21d30997b1 100644 --- a/config/params/config.go +++ b/config/params/config.go @@ -162,6 +162,7 @@ type BeaconChainConfig struct { BeaconStateDenebFieldCount int // BeaconStateDenebFieldCount defines how many fields are in beacon state post upgrade to Deneb. BeaconStateElectraFieldCount int // BeaconStateElectraFieldCount defines how many fields are in beacon state post upgrade to Electra. BeaconStateFuluFieldCount int // BeaconStateFuluFieldCount defines how many fields are in beacon state post upgrade to Fulu. + BeaconStateGloasFieldCount int // BeaconStateGloasFieldCount defines how many fields are in beacon state post upgrade to Gloas. // Slasher constants. WeakSubjectivityPeriod primitives.Epoch // WeakSubjectivityPeriod defines the time period expressed in number of epochs were proof of stake network should validate block headers and attestations for slashable events. diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index af6c47dbe1..e7d2f6449e 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -206,6 +206,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{ BeaconStateDenebFieldCount: 28, BeaconStateElectraFieldCount: 37, BeaconStateFuluFieldCount: 38, + BeaconStateGloasFieldCount: 43, // Slasher related values. WeakSubjectivityPeriod: 54000, diff --git a/encoding/ssz/detect/configfork.go b/encoding/ssz/detect/configfork.go index 3550ffeb3c..c5520630ea 100644 --- a/encoding/ssz/detect/configfork.go +++ b/encoding/ssz/detect/configfork.go @@ -172,6 +172,18 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconState(marshaled []byte) (s state. if err != nil { return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName) } + case version.Gloas: + st := ðpb.BeaconStateGloas{} + + err = st.UnmarshalSSZ(marshaled) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName) + } + + s, err = state_native.InitializeFromProtoUnsafeGloas(st) + if err != nil { + return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName) + } default: return nil, fmt.Errorf("unable to initialize BeaconState for fork version=%s", forkName) } diff --git a/proto/engine/v1/gloas.pb.go b/proto/engine/v1/gloas.pb.go new file mode 100755 index 0000000000..189e1bff13 --- /dev/null +++ b/proto/engine/v1/gloas.pb.go @@ -0,0 +1,288 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.3 +// protoc v3.21.7 +// source: proto/engine/v1/gloas.proto + +package enginev1 + +import ( + reflect "reflect" + sync "sync" + + github_com_OffchainLabs_prysm_v6_consensus_types_primitives "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + _ "github.com/OffchainLabs/prysm/v7/proto/eth/ext" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ExecutionPayloadEnvelope struct { + state protoimpl.MessageState `protogen:"open.v1"` + Payload *ExecutionPayloadDeneb `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + ExecutionRequests *ExecutionRequests `protobuf:"bytes,2,opt,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"` + BuilderIndex github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex `protobuf:"varint,3,opt,name=builder_index,json=builderIndex,proto3" json:"builder_index,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.ValidatorIndex"` + BeaconBlockRoot []byte `protobuf:"bytes,4,opt,name=beacon_block_root,json=beaconBlockRoot,proto3" json:"beacon_block_root,omitempty" ssz-size:"32"` + Slot github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Slot"` + BlobKzgCommitments [][]byte `protobuf:"bytes,6,rep,name=blob_kzg_commitments,json=blobKzgCommitments,proto3" json:"blob_kzg_commitments,omitempty" ssz-max:"4096" ssz-size:"?,48"` + StateRoot []byte `protobuf:"bytes,7,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty" ssz-size:"32"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExecutionPayloadEnvelope) Reset() { + *x = ExecutionPayloadEnvelope{} + mi := &file_proto_engine_v1_gloas_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExecutionPayloadEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecutionPayloadEnvelope) ProtoMessage() {} + +func (x *ExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_proto_engine_v1_gloas_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. +func (*ExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { + return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{0} +} + +func (x *ExecutionPayloadEnvelope) GetPayload() *ExecutionPayloadDeneb { + if x != nil { + return x.Payload + } + return nil +} + +func (x *ExecutionPayloadEnvelope) GetExecutionRequests() *ExecutionRequests { + if x != nil { + return x.ExecutionRequests + } + return nil +} + +func (x *ExecutionPayloadEnvelope) GetBuilderIndex() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex { + if x != nil { + return x.BuilderIndex + } + return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex(0) +} + +func (x *ExecutionPayloadEnvelope) GetBeaconBlockRoot() []byte { + if x != nil { + return x.BeaconBlockRoot + } + return nil +} + +func (x *ExecutionPayloadEnvelope) GetSlot() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot { + if x != nil { + return x.Slot + } + return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot(0) +} + +func (x *ExecutionPayloadEnvelope) GetBlobKzgCommitments() [][]byte { + if x != nil { + return x.BlobKzgCommitments + } + return nil +} + +func (x *ExecutionPayloadEnvelope) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +type SignedExecutionPayloadEnvelope struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message *ExecutionPayloadEnvelope `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SignedExecutionPayloadEnvelope) Reset() { + *x = SignedExecutionPayloadEnvelope{} + mi := &file_proto_engine_v1_gloas_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignedExecutionPayloadEnvelope) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignedExecutionPayloadEnvelope) ProtoMessage() {} + +func (x *SignedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message { + mi := &file_proto_engine_v1_gloas_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead. +func (*SignedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) { + return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{1} +} + +func (x *SignedExecutionPayloadEnvelope) GetMessage() *ExecutionPayloadEnvelope { + if x != nil { + return x.Message + } + return nil +} + +func (x *SignedExecutionPayloadEnvelope) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +var File_proto_engine_v1_gloas_proto protoreflect.FileDescriptor + +var file_proto_engine_v1_gloas_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, + 0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x65, 0x74, 0x68, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x04, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x73, 0x0a, + 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, + 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, + 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x1e, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x46, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, + 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x3b, 0x5a, 0x39, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, + 0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_proto_engine_v1_gloas_proto_rawDescOnce sync.Once + file_proto_engine_v1_gloas_proto_rawDescData = file_proto_engine_v1_gloas_proto_rawDesc +) + +func file_proto_engine_v1_gloas_proto_rawDescGZIP() []byte { + file_proto_engine_v1_gloas_proto_rawDescOnce.Do(func() { + file_proto_engine_v1_gloas_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_engine_v1_gloas_proto_rawDescData) + }) + return file_proto_engine_v1_gloas_proto_rawDescData +} + +var file_proto_engine_v1_gloas_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_engine_v1_gloas_proto_goTypes = []any{ + (*ExecutionPayloadEnvelope)(nil), // 0: ethereum.engine.v1.ExecutionPayloadEnvelope + (*SignedExecutionPayloadEnvelope)(nil), // 1: ethereum.engine.v1.SignedExecutionPayloadEnvelope + (*ExecutionPayloadDeneb)(nil), // 2: ethereum.engine.v1.ExecutionPayloadDeneb + (*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests +} +var file_proto_engine_v1_gloas_proto_depIdxs = []int32{ + 2, // 0: ethereum.engine.v1.ExecutionPayloadEnvelope.payload:type_name -> ethereum.engine.v1.ExecutionPayloadDeneb + 3, // 1: ethereum.engine.v1.ExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests + 0, // 2: ethereum.engine.v1.SignedExecutionPayloadEnvelope.message:type_name -> ethereum.engine.v1.ExecutionPayloadEnvelope + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_proto_engine_v1_gloas_proto_init() } +func file_proto_engine_v1_gloas_proto_init() { + if File_proto_engine_v1_gloas_proto != nil { + return + } + file_proto_engine_v1_execution_engine_proto_init() + file_proto_engine_v1_electra_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_engine_v1_gloas_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proto_engine_v1_gloas_proto_goTypes, + DependencyIndexes: file_proto_engine_v1_gloas_proto_depIdxs, + MessageInfos: file_proto_engine_v1_gloas_proto_msgTypes, + }.Build() + File_proto_engine_v1_gloas_proto = out.File + file_proto_engine_v1_gloas_proto_rawDesc = nil + file_proto_engine_v1_gloas_proto_goTypes = nil + file_proto_engine_v1_gloas_proto_depIdxs = nil +} diff --git a/proto/prysm/v1alpha1/BUILD.bazel b/proto/prysm/v1alpha1/BUILD.bazel index 799840abd4..9850f6944f 100644 --- a/proto/prysm/v1alpha1/BUILD.bazel +++ b/proto/prysm/v1alpha1/BUILD.bazel @@ -371,6 +371,7 @@ go_library( "beacon_block.go", "cloners.go", "eip_7521.go", + "gloas.go", "log.go", "sync_committee_mainnet.go", "sync_committee_minimal.go", # keep diff --git a/proto/prysm/v1alpha1/gloas.go b/proto/prysm/v1alpha1/gloas.go new file mode 100644 index 0000000000..3a12cdea09 --- /dev/null +++ b/proto/prysm/v1alpha1/gloas.go @@ -0,0 +1,47 @@ +package eth + +import ( + "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" +) + +// Copy creates a deep copy of ExecutionPayloadBid. +func (header *ExecutionPayloadBid) Copy() *ExecutionPayloadBid { + if header == nil { + return nil + } + return &ExecutionPayloadBid{ + ParentBlockHash: bytesutil.SafeCopyBytes(header.ParentBlockHash), + ParentBlockRoot: bytesutil.SafeCopyBytes(header.ParentBlockRoot), + BlockHash: bytesutil.SafeCopyBytes(header.BlockHash), + FeeRecipient: bytesutil.SafeCopyBytes(header.FeeRecipient), + GasLimit: header.GasLimit, + BuilderIndex: header.BuilderIndex, + Slot: header.Slot, + Value: header.Value, + BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.BlobKzgCommitmentsRoot), + } +} + +// Copy creates a deep copy of BuilderPendingWithdrawal. +func (withdrawal *BuilderPendingWithdrawal) Copy() *BuilderPendingWithdrawal { + if withdrawal == nil { + return nil + } + return &BuilderPendingWithdrawal{ + FeeRecipient: bytesutil.SafeCopyBytes(withdrawal.FeeRecipient), + Amount: withdrawal.Amount, + BuilderIndex: withdrawal.BuilderIndex, + WithdrawableEpoch: withdrawal.WithdrawableEpoch, + } +} + +// Copy creates a deep copy of BuilderPendingPayment. +func (payment *BuilderPendingPayment) Copy() *BuilderPendingPayment { + if payment == nil { + return nil + } + return &BuilderPendingPayment{ + Weight: payment.Weight, + Withdrawal: payment.Withdrawal.Copy(), + } +} diff --git a/proto/prysm/v1alpha1/gloas_test.go b/proto/prysm/v1alpha1/gloas_test.go new file mode 100644 index 0000000000..54d5189014 --- /dev/null +++ b/proto/prysm/v1alpha1/gloas_test.go @@ -0,0 +1,168 @@ +package eth + +import ( + "reflect" + "testing" + + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" +) + +func TestExecutionPayloadBid_Copy(t *testing.T) { + tests := []struct { + name string + bid *ExecutionPayloadBid + }{ + { + name: "nil bid", + bid: nil, + }, + { + name: "empty bid", + bid: &ExecutionPayloadBid{}, + }, + { + name: "fully populated bid", + bid: &ExecutionPayloadBid{ + ParentBlockHash: []byte("parent_block_hash_32_bytes_long!"), + ParentBlockRoot: []byte("parent_block_root_32_bytes_long!"), + BlockHash: []byte("block_hash_32_bytes_are_long!!"), + FeeRecipient: []byte("fee_recipient_20_byt"), + GasLimit: 15000000, + BuilderIndex: primitives.ValidatorIndex(42), + Slot: primitives.Slot(12345), + Value: 1000000000000000000, + BlobKzgCommitmentsRoot: []byte("blob_kzg_commitments_32_bytes!!"), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + copied := tt.bid.Copy() + if tt.bid == nil { + if copied != nil { + t.Errorf("Copy() of nil should return nil, got %v", copied) + } + return + } + + if !reflect.DeepEqual(tt.bid, copied) { + t.Errorf("Copy() = %v, want %v", copied, tt.bid) + } + + if len(tt.bid.ParentBlockHash) > 0 { + tt.bid.ParentBlockHash[0] = 0xFF + if copied.ParentBlockHash[0] == 0xFF { + t.Error("Copy() did not create deep copy of ParentBlockHash") + } + } + }) + } +} + +func TestBuilderPendingWithdrawal_Copy(t *testing.T) { + tests := []struct { + name string + withdrawal *BuilderPendingWithdrawal + }{ + { + name: "nil withdrawal", + withdrawal: nil, + }, + { + name: "empty withdrawal", + withdrawal: &BuilderPendingWithdrawal{}, + }, + { + name: "fully populated withdrawal", + withdrawal: &BuilderPendingWithdrawal{ + FeeRecipient: []byte("fee_recipient_20_byt"), + Amount: primitives.Gwei(5000000000), + BuilderIndex: primitives.ValidatorIndex(123), + WithdrawableEpoch: primitives.Epoch(456), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + copied := tt.withdrawal.Copy() + if tt.withdrawal == nil { + if copied != nil { + t.Errorf("Copy() of nil should return nil, got %v", copied) + } + return + } + + if !reflect.DeepEqual(tt.withdrawal, copied) { + t.Errorf("Copy() = %v, want %v", copied, tt.withdrawal) + } + + // Verify deep copy by modifying original + if len(tt.withdrawal.FeeRecipient) > 0 { + tt.withdrawal.FeeRecipient[0] = 0xFF + if copied.FeeRecipient[0] == 0xFF { + t.Error("Copy() did not create deep copy of FeeRecipient") + } + } + }) + } +} + +func TestBuilderPendingPayment_Copy(t *testing.T) { + tests := []struct { + name string + payment *BuilderPendingPayment + }{ + { + name: "nil payment", + payment: nil, + }, + { + name: "empty payment", + payment: &BuilderPendingPayment{}, + }, + { + name: "payment with nil withdrawal", + payment: &BuilderPendingPayment{ + Weight: primitives.Gwei(1000), + Withdrawal: nil, + }, + }, + { + name: "fully populated payment", + payment: &BuilderPendingPayment{ + Weight: primitives.Gwei(2500), + Withdrawal: &BuilderPendingWithdrawal{ + FeeRecipient: []byte("test_recipient_20byt"), + Amount: primitives.Gwei(10000), + BuilderIndex: primitives.ValidatorIndex(789), + WithdrawableEpoch: primitives.Epoch(999), + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + copied := tt.payment.Copy() + if tt.payment == nil { + if copied != nil { + t.Errorf("Copy() of nil should return nil, got %v", copied) + } + return + } + + if !reflect.DeepEqual(tt.payment, copied) { + t.Errorf("Copy() = %v, want %v", copied, tt.payment) + } + + if tt.payment.Withdrawal != nil && len(tt.payment.Withdrawal.FeeRecipient) > 0 { + tt.payment.Withdrawal.FeeRecipient[0] = 0xFF + if copied.Withdrawal != nil && len(copied.Withdrawal.FeeRecipient) > 0 && copied.Withdrawal.FeeRecipient[0] == 0xFF { + t.Error("Copy() did not create deep copy of nested Withdrawal.FeeRecipient") + } + } + }) + } +} diff --git a/runtime/version/fork.go b/runtime/version/fork.go index fefcb4432e..385cc58046 100644 --- a/runtime/version/fork.go +++ b/runtime/version/fork.go @@ -14,6 +14,7 @@ const ( Deneb Electra Fulu + Gloas ) var versionToString = map[int]string{ diff --git a/testing/spectest/shared/gloas/ssz_static/BUILD.bazel b/testing/spectest/shared/gloas/ssz_static/BUILD.bazel index 04a733ed15..3c5936068b 100644 --- a/testing/spectest/shared/gloas/ssz_static/BUILD.bazel +++ b/testing/spectest/shared/gloas/ssz_static/BUILD.bazel @@ -7,8 +7,10 @@ go_library( importpath = "github.com/OffchainLabs/prysm/v7/testing/spectest/shared/gloas/ssz_static", visibility = ["//testing/spectest:__subpackages__"], deps = [ + "//beacon-chain/state/state-native:go_default_library", "//proto/engine/v1:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//testing/require:go_default_library", "//testing/spectest/shared/common/ssz_static:go_default_library", "@com_github_prysmaticlabs_fastssz//:go_default_library", ], diff --git a/testing/spectest/shared/gloas/ssz_static/ssz_static.go b/testing/spectest/shared/gloas/ssz_static/ssz_static.go index 0fea08594a..94693ca3f1 100644 --- a/testing/spectest/shared/gloas/ssz_static/ssz_static.go +++ b/testing/spectest/shared/gloas/ssz_static/ssz_static.go @@ -1,11 +1,14 @@ package ssz_static import ( + "context" "errors" "testing" + state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native" enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/require" common "github.com/OffchainLabs/prysm/v7/testing/spectest/shared/common/ssz_static" fssz "github.com/prysmaticlabs/fastssz" ) @@ -16,8 +19,18 @@ func RunSSZStaticTests(t *testing.T, config string) { } func customHtr(t *testing.T, htrs []common.HTR, object any) []common.HTR { - // TODO: Add custom HTR for BeaconStateGloas when state-native support is implemented - // For now, only use the default fastssz HTR methods + _, ok := object.(*ethpb.BeaconStateGloas) + if !ok { + return htrs + } + + htrs = append(htrs, func(s any) ([32]byte, error) { + beaconState, err := state_native.InitializeFromProtoGloas(s.(*ethpb.BeaconStateGloas)) + require.NoError(t, err) + + return beaconState.HashTreeRoot(context.Background()) + }) + return htrs }