Compare commits

...

4 Commits

Author SHA1 Message Date
terence tsao
f2488fb5b2 Add gloas block gossip changes 2026-02-17 20:36:58 -08:00
terence
8ee28394ab Add Gloas attestation committee index validation (#16359)
Adds Gloas fork attestation validation rules for gossip processing. This
implements the new committee index validation requirements introduced in
the Gloas fork.

## Changes
- Uses attestation epoch to determine if Gloas rules apply
- **Committee index validation**: 
  - Committee index must be < 2 (0 or 1 only)
- Same-slot attestations (where `attestation.data.slot == block.slot`)
must use committee index 0
  - Different-slot attestations can use either committee index 0 or 1
2026-02-17 18:00:32 +00:00
kasey
b31e2ffe51 avoid copying the finalized state when computing cgc (#16355)
Reviewing some (unrelated) sync code today I noticed that we are using a
stategen accessor for the finalized state which copies the entire state
object to look up validator balances to compute the custody_group_count.
This excess memory allocation is likely causing GC pressure and
increasing memory utilization.

This PR avoids state copying for this purpose by making the following
changes:
- Adds a new method to the `ReadOnlyBalances` state interface:
`EffectiveBalances([]primitives.ValidatorIndex) (uint64, []uint64,
error)`. This method computes returns the sum of the effective balances
of the given list of validator indices, a list with the individual
effective balance of each requested index (where the i-th element in the
return corresponds to the i-th element of the parameter), and an error -
which is necessary due to index bounds checks and quirks of multi-value
slice that can apparently result in the state being unusable for such
lookups if not correctly initialized.
- Adds a new method to the stategen interface
`FinalizedReadOnlyBalances`, which returns the finalized state asserted
to the `ReadOnlyBalances` interface.
- Switches the peerdas code to use the sum given by `EffectiveBalances`.

There was some existing nil checking code in the peerdas package that I
didn't want to modify, so I added a new compound interface in stategen
to allow the returned state to also expose the `IsNil` method.

fixes https://github.com/OffchainLabs/prysm/issues/16354

---------

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2026-02-17 08:50:58 +00:00
terence
22e77add54 Refactor function ProcessExecutionPayload with ApplyExecutionPayload (#16356)
This PR refactors `ProcessExecutionPayload` with `ApplyExecutionPayload`
so caller can use Apply method to calculate post state root. Note that
validations are not required for Apply function. We really need the
state mutation lines that's:
```
1. Ensure latest_block_header.state_root is set (if zero, set it to the pre‑payload HashTreeRoot)...
2. processExecutionRequests()
3. QueueBuilderPayment()
4. SetExecutionPayloadAvailability(state.Slot(), true)
5. SetLatestBlockHash(payload.BlockHash())
```
I decided to keep them there because a. it's cheap b. it makes refactor
cleaner to reason c. API/caller may want to validate envelope and bid
consistency (ex: beacon api has option to validate consensus)
2026-02-13 15:51:22 +00:00
17 changed files with 357 additions and 27 deletions

View File

@@ -112,6 +112,34 @@ func ProcessExecutionPayload(
return errors.Wrap(err, "signature verification failed")
}
envelope, err := signedEnvelope.Envelope()
if err != nil {
return errors.Wrap(err, "could not get envelope from signed envelope")
}
if err := ApplyExecutionPayload(ctx, st, envelope); err != nil {
return err
}
r, err := st.HashTreeRoot(ctx)
if err != nil {
return errors.Wrap(err, "could not get hash tree root")
}
if r != envelope.StateRoot() {
return fmt.Errorf("state root mismatch: expected %#x, got %#x", envelope.StateRoot(), r)
}
return nil
}
// ApplyExecutionPayload applies the execution payload envelope to the state and performs the same
// consistency checks as the full processing path. This keeps the post-payload state root computation
// on a shared code path, even though some bid/payload checks are not strictly required for the root itself.
func ApplyExecutionPayload(
ctx context.Context,
st state.BeaconState,
envelope interfaces.ROExecutionPayloadEnvelope,
) error {
latestHeader := st.LatestBlockHeader()
if len(latestHeader.StateRoot) == 0 || bytes.Equal(latestHeader.StateRoot, make([]byte, 32)) {
previousStateRoot, err := st.HashTreeRoot(ctx)
@@ -128,10 +156,6 @@ func ProcessExecutionPayload(
if err != nil {
return errors.Wrap(err, "could not compute block header root")
}
envelope, err := signedEnvelope.Envelope()
if err != nil {
return errors.Wrap(err, "could not get envelope from signed envelope")
}
beaconBlockRoot := envelope.BeaconBlockRoot()
if !bytes.Equal(beaconBlockRoot[:], blockHeaderRoot[:]) {
@@ -217,14 +241,6 @@ func ProcessExecutionPayload(
return errors.Wrap(err, "could not set latest block hash")
}
r, err := st.HashTreeRoot(ctx)
if err != nil {
return errors.Wrap(err, "could not get hash tree root")
}
if r != envelope.StateRoot() {
return fmt.Errorf("state root mismatch: expected %#x, got %#x", envelope.StateRoot(), r)
}
return nil
}

View File

@@ -73,23 +73,22 @@ func PopulateFromSidecar(sidecar blocks.VerifiedRODataColumn) *SidecarReconstruc
// ValidatorsCustodyRequirement returns the number of custody groups regarding the validator indices attached to the beacon node.
// https://github.com/ethereum/consensus-specs/blob/master/specs/fulu/validator.md#validator-custody
func ValidatorsCustodyRequirement(state beaconState.ReadOnlyBeaconState, validatorsIndex map[primitives.ValidatorIndex]bool) (uint64, error) {
totalNodeBalance := uint64(0)
func ValidatorsCustodyRequirement(st beaconState.ReadOnlyBalances, validatorsIndex map[primitives.ValidatorIndex]bool) (uint64, error) {
cfg := params.BeaconConfig()
idxs := make([]primitives.ValidatorIndex, 0, len(validatorsIndex))
for index := range validatorsIndex {
validator, err := state.ValidatorAtIndexReadOnly(index)
if err != nil {
return 0, errors.Wrapf(err, "validator at index %v", index)
}
totalNodeBalance += validator.EffectiveBalance()
idxs = append(idxs, index)
}
totalBalance, err := st.EffectiveBalanceSum(idxs)
if err != nil {
return 0, errors.Wrap(err, "effective balances")
}
cfg := params.BeaconConfig()
numberOfCustodyGroups := cfg.NumberOfCustodyGroups
validatorCustodyRequirement := cfg.ValidatorCustodyRequirement
balancePerAdditionalCustodyGroup := cfg.BalancePerAdditionalCustodyGroup
count := totalNodeBalance / balancePerAdditionalCustodyGroup
count := totalBalance / balancePerAdditionalCustodyGroup
return min(max(count, validatorCustodyRequirement), numberOfCustodyGroups), nil
}

View File

@@ -152,6 +152,7 @@ type ReadOnlyBalances interface {
Balances() []uint64
BalanceAtIndex(idx primitives.ValidatorIndex) (uint64, error)
BalancesLength() int
EffectiveBalanceSum([]primitives.ValidatorIndex) (uint64, error)
}
// ReadOnlyCheckpoint defines a struct which only has read access to checkpoint methods.

View File

@@ -8,6 +8,7 @@ import (
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/pkg/errors"
)
// Validators participating in consensus on the beacon chain.
@@ -80,6 +81,25 @@ func (b *BeaconState) ValidatorAtIndex(idx primitives.ValidatorIndex) (*ethpb.Va
return b.validatorAtIndex(idx)
}
// EffectiveBalances returns the sum of the effective balances of the given list of validator indices, the eb of each given validator, or an
// error if one of the indices is out of bounds, or the state wasn't correctly initialized.
func (b *BeaconState) EffectiveBalanceSum(idxs []primitives.ValidatorIndex) (uint64, error) {
b.lock.RLock()
defer b.lock.RUnlock()
var sum uint64
for i := range idxs {
if b.validatorsMultiValue == nil {
return 0, errors.Wrap(state.ErrNilValidatorsInState, "nil validators multi-value slice")
}
v, err := b.validatorsMultiValue.At(b, uint64(idxs[i]))
if err != nil {
return 0, errors.Wrap(err, "validators multi value at index")
}
sum += v.EffectiveBalance
}
return sum, nil
}
func (b *BeaconState) validatorAtIndex(idx primitives.ValidatorIndex) (*ethpb.Validator, error) {
if b.validatorsMultiValue == nil {
return &ethpb.Validator{}, nil

View File

@@ -5,6 +5,7 @@ import (
"context"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
)
@@ -14,6 +15,8 @@ type StateManager struct {
StatesBySlot map[primitives.Slot]state.BeaconState
}
var _ stategen.StateManager = (*StateManager)(nil)
// NewService --
func NewService() *StateManager {
return &StateManager{
@@ -101,3 +104,8 @@ func (m *StateManager) AddStateForSlot(state state.BeaconState, slot primitives.
func (m *StateManager) DeleteStateFromCaches(context.Context, [32]byte) error {
return nil
}
// FinalizedReadOnlyBalances --
func (m *StateManager) FinalizedReadOnlyBalances() stategen.NilCheckableReadOnlyBalances {
panic("unimplemented")
}

View File

@@ -27,6 +27,13 @@ var defaultHotStateDBInterval primitives.Slot = 128
var populatePubkeyCacheOnce sync.Once
// NilCheckableReadOnlyBalances adds the IsNil method to ReadOnlyBalances
// to allow checking if the underlying state value is nil.
type NilCheckableReadOnlyBalances interface {
state.ReadOnlyBalances
IsNil() bool
}
// StateManager represents a management object that handles the internal
// logic of maintaining both hot and cold states in DB.
type StateManager interface {
@@ -43,6 +50,7 @@ type StateManager interface {
ActiveNonSlashedBalancesByRoot(context.Context, [32]byte) ([]uint64, error)
StateByRootIfCachedNoCopy(blockRoot [32]byte) state.BeaconState
StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
FinalizedReadOnlyBalances() NilCheckableReadOnlyBalances
}
// State is a concrete implementation of StateManager.
@@ -201,3 +209,8 @@ func (s *State) FinalizedState() state.BeaconState {
defer s.finalizedInfo.lock.RUnlock()
return s.finalizedInfo.state.Copy()
}
// Returns the finalized state as a ReadOnlyBalances so that it can be used read-only without copying.
func (s *State) FinalizedReadOnlyBalances() NilCheckableReadOnlyBalances {
return s.finalizedInfo.state
}

View File

@@ -54,6 +54,7 @@ go_library(
"validate_aggregate_proof.go",
"validate_attester_slashing.go",
"validate_beacon_attestation.go",
"validate_beacon_block_gloas.go",
"validate_beacon_blocks.go",
"validate_blob.go",
"validate_bls_to_execution_change.go",
@@ -211,6 +212,7 @@ go_test(
"validate_aggregate_proof_test.go",
"validate_attester_slashing_test.go",
"validate_beacon_attestation_test.go",
"validate_beacon_block_gloas_test.go",
"validate_beacon_blocks_test.go",
"validate_blob_test.go",
"validate_bls_to_execution_change_test.go",

View File

@@ -185,7 +185,7 @@ func (s *Service) validatorsCustodyRequirement() (uint64, error) {
}
// Retrieve the finalized state.
finalizedState := s.cfg.stateGen.FinalizedState()
finalizedState := s.cfg.stateGen.FinalizedReadOnlyBalances()
if finalizedState == nil || finalizedState.IsNil() {
return 0, nilFinalizedStateError
}

View File

@@ -268,10 +268,23 @@ func (s *Service) validateCommitteeIndexAndCount(
a eth.Att,
bs state.ReadOnlyBeaconState,
) (primitives.CommitteeIndex, uint64, pubsub.ValidationResult, error) {
// - [REJECT] attestation.data.index == 0
if a.Version() >= version.Electra && a.GetData().CommitteeIndex != 0 {
return 0, 0, pubsub.ValidationReject, errors.New("attestation data's committee index must be 0")
// Validate committee index based on fork.
if a.Version() >= version.Electra {
data := a.GetData()
attEpoch := slots.ToEpoch(data.Slot)
postGloas := attEpoch >= params.BeaconConfig().GloasForkEpoch
if postGloas {
if result, err := s.validateGloasCommitteeIndex(data); result != pubsub.ValidationAccept {
return 0, 0, result, err
}
} else {
// [REJECT] attestation.data.index == 0 (New in Electra, removed in Gloas)
if data.CommitteeIndex != 0 {
return 0, 0, pubsub.ValidationReject, errors.New("attestation data's committee index must be 0")
}
}
}
valCount, err := helpers.ActiveValidatorCount(ctx, bs, slots.ToEpoch(a.GetData().Slot))
if err != nil {
return 0, 0, pubsub.ValidationIgnore, err
@@ -356,6 +369,29 @@ func validateAttestingIndex(
return pubsub.ValidationAccept, nil
}
// validateGloasCommitteeIndex validates committee index rules for Gloas fork.
// [REJECT] attestation.data.index < 2. (New in Gloas)
// [REJECT] attestation.data.index == 0 if block.slot == attestation.data.slot. (New in Gloas)
func (s *Service) validateGloasCommitteeIndex(data *eth.AttestationData) (pubsub.ValidationResult, error) {
if data.CommitteeIndex >= 2 {
return pubsub.ValidationReject, errors.New("attestation data's committee index must be < 2")
}
// Same-slot attestations must use committee index 0
if data.CommitteeIndex != 0 {
blockRoot := bytesutil.ToBytes32(data.BeaconBlockRoot)
slot, err := s.cfg.chain.RecentBlockSlot(blockRoot)
if err != nil {
return pubsub.ValidationIgnore, err
}
if slot == data.Slot {
return pubsub.ValidationReject, errors.New("same slot attestations must use committee index 0")
}
}
return pubsub.ValidationAccept, nil
}
// generateUnaggregatedAttCacheKey generates the cache key for unaggregated attestation tracking.
func generateUnaggregatedAttCacheKey(att eth.Att) (string, error) {
var attester uint64

View File

@@ -684,3 +684,75 @@ func Test_validateCommitteeIndexAndCount_Boundary(t *testing.T) {
require.ErrorContains(t, "committee index", err)
require.Equal(t, pubsub.ValidationReject, res)
}
func Test_validateGloasCommitteeIndex(t *testing.T) {
tests := []struct {
name string
committeeIndex primitives.CommitteeIndex
attestationSlot primitives.Slot
blockSlot primitives.Slot
wantResult pubsub.ValidationResult
wantErr string
}{
{
name: "committee index >= 2 should reject",
committeeIndex: 2,
attestationSlot: 10,
blockSlot: 10,
wantResult: pubsub.ValidationReject,
wantErr: "committee index must be < 2",
},
{
name: "committee index 0 should accept",
committeeIndex: 0,
attestationSlot: 10,
blockSlot: 10,
wantResult: pubsub.ValidationAccept,
wantErr: "",
},
{
name: "committee index 1 different-slot should accept",
committeeIndex: 1,
attestationSlot: 10,
blockSlot: 9,
wantResult: pubsub.ValidationAccept,
wantErr: "",
},
{
name: "committee index 1 same-slot should reject",
committeeIndex: 1,
attestationSlot: 10,
blockSlot: 10,
wantResult: pubsub.ValidationReject,
wantErr: "same slot attestations must use committee index 0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockChain := &mockChain.ChainService{
BlockSlot: tt.blockSlot,
}
s := &Service{
cfg: &config{
chain: mockChain,
},
}
data := &ethpb.AttestationData{
Slot: tt.attestationSlot,
CommitteeIndex: tt.committeeIndex,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot"), 32),
}
result, err := s.validateGloasCommitteeIndex(data)
require.Equal(t, tt.wantResult, result)
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@@ -0,0 +1,61 @@
package sync
import (
"context"
"github.com/OffchainLabs/prysm/v7/config/params"
consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/time/slots"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
)
// validateExecutionPayloadBid validates execution payload bid gossip rules.
// [REJECT] The bid's parent (defined by bid.parent_block_root) equals the block's parent (defined by block.parent_root).
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the consensus layer --
// i.e. validate that len(bid.blob_kzg_commitments) <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block
func (s *Service) validateExecutionPayloadBid(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock) (pubsub.ValidationResult, error) {
if blk.Version() < version.Gloas {
return pubsub.ValidationAccept, nil
}
signedBid, err := blk.Body().SignedExecutionPayloadBid()
if err != nil {
return pubsub.ValidationIgnore, errors.Wrap(err, "unable to read bid from block")
}
wrappedBid, err := consensusblocks.WrappedROSignedExecutionPayloadBid(signedBid)
if err != nil {
return pubsub.ValidationIgnore, errors.Wrap(err, "unable to wrap signed execution payload bid")
}
bid, err := wrappedBid.Bid()
if err != nil {
return pubsub.ValidationIgnore, errors.Wrap(err, "unable to read bid from signed execution payload bid")
}
if bid.ParentBlockRoot() != blk.ParentRoot() {
return pubsub.ValidationReject, errors.New("bid parent block root does not match block parent root")
}
maxBlobsPerBlock := params.BeaconConfig().MaxBlobsPerBlockAtEpoch(slots.ToEpoch(blk.Slot()))
if bid.BlobKzgCommitmentCount() > uint64(maxBlobsPerBlock) {
return pubsub.ValidationReject, errors.Wrapf(errRejectCommitmentLen, "%d > %d", bid.BlobKzgCommitmentCount(), maxBlobsPerBlock)
}
return pubsub.ValidationAccept, nil
}
// validateExecutionPayloadBidParentSeen validates parent payload gossip rules.
// [IGNORE] The block's parent execution payload (defined by bid.parent_block_hash) has been seen
// (via gossip or non-gossip sources) (a client MAY queue blocks for processing once the parent payload is retrieved).
func (s *Service) validateExecutionPayloadBidParentSeen(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock) (pubsub.ValidationResult, error) {
// TODO: Requires blockchain service changes to expose parent payload seen status
return pubsub.ValidationAccept, nil
}
// validateExecutionPayloadBidParentValid validates parent payload verification status.
// If execution_payload verification of block's execution payload parent by an execution node is complete:
// [REJECT] The block's execution payload parent (defined by bid.parent_block_hash) passes all validation.
func (s *Service) validateExecutionPayloadBidParentValid(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock) (pubsub.ValidationResult, error) {
// TODO: Requires blockchain service changes to expose execution payload parent validation status.
return pubsub.ValidationAccept, nil
}

View File

@@ -0,0 +1,75 @@
package sync
import (
"context"
"testing"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v7/testing/util"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/stretchr/testify/require"
)
func TestValidateExecutionPayloadBid_Accept(t *testing.T) {
params.SetupTestConfigCleanup(t)
ctx := context.Background()
parentRoot := bytesutil.PadTo([]byte{0x01}, fieldparams.RootLength)
block := util.NewBeaconBlockGloas()
block.Block.ParentRoot = parentRoot
block.Block.Body.SignedExecutionPayloadBid.Message.ParentBlockRoot = parentRoot
block.Block.Body.SignedExecutionPayloadBid.Message.BlobKzgCommitments = nil
wsb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
s := &Service{}
res, err := s.validateExecutionPayloadBid(ctx, wsb.Block())
require.NoError(t, err)
require.Equal(t, pubsub.ValidationAccept, res)
}
func TestValidateExecutionPayloadBid_RejectParentRootMismatch(t *testing.T) {
params.SetupTestConfigCleanup(t)
ctx := context.Background()
block := util.NewBeaconBlockGloas()
block.Block.ParentRoot = bytesutil.PadTo([]byte{0x01}, fieldparams.RootLength)
block.Block.Body.SignedExecutionPayloadBid.Message.ParentBlockRoot = bytesutil.PadTo([]byte{0x02}, fieldparams.RootLength)
wsb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
s := &Service{}
res, err := s.validateExecutionPayloadBid(ctx, wsb.Block())
require.Error(t, err)
require.Equal(t, pubsub.ValidationReject, res)
}
func TestValidateExecutionPayloadBid_RejectTooManyCommitments(t *testing.T) {
params.SetupTestConfigCleanup(t)
ctx := context.Background()
parentRoot := bytesutil.PadTo([]byte{0x01}, fieldparams.RootLength)
block := util.NewBeaconBlockGloas()
block.Block.ParentRoot = parentRoot
block.Block.Body.SignedExecutionPayloadBid.Message.ParentBlockRoot = parentRoot
maxBlobs := params.BeaconConfig().MaxBlobsPerBlockAtEpoch(0)
commitments := make([][]byte, maxBlobs+1)
for i := range commitments {
commitments[i] = bytesutil.PadTo([]byte{0x02}, fieldparams.BLSPubkeyLength)
}
block.Block.Body.SignedExecutionPayloadBid.Message.BlobKzgCommitments = commitments
wsb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
s := &Service{}
res, err := s.validateExecutionPayloadBid(ctx, wsb.Block())
require.Error(t, err)
require.Equal(t, pubsub.ValidationReject, res)
}

View File

@@ -127,6 +127,9 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
log.WithError(err).WithFields(getBlockFields(blk)).Debug("Received block with an invalid parent")
return pubsub.ValidationReject, err
}
if res, err := s.validateExecutionPayloadBidParentValid(ctx, blk.Block()); err != nil {
return res, err
}
s.pendingQueueLock.RLock()
if s.seenPendingBlocks[blockRoot] {
@@ -198,6 +201,16 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
log.WithError(err).WithFields(getBlockFields(blk)).Debug("Could not identify parent for block")
return pubsub.ValidationIgnore, err
}
if res, err := s.validateExecutionPayloadBidParentSeen(ctx, blk.Block()); err != nil {
return res, err
}
if res, err := s.validateExecutionPayloadBid(ctx, blk.Block()); err != nil {
if res == pubsub.ValidationReject {
s.setBadBlock(ctx, blockRoot)
}
return res, err
}
err = s.validateBeaconBlock(ctx, blk, blockRoot)
if err != nil {
@@ -365,7 +378,7 @@ func (s *Service) blockVerifyingState(ctx context.Context, blk interfaces.ReadOn
}
func validateDenebBeaconBlock(blk interfaces.ReadOnlyBeaconBlock) error {
if blk.Version() < version.Deneb {
if blk.Version() < version.Deneb || blk.Version() >= version.Gloas {
return nil
}
commits, err := blk.Body().BlobKzgCommitments()
@@ -398,6 +411,10 @@ func validateDenebBeaconBlock(blk interfaces.ReadOnlyBeaconBlock) error {
// [IGNORE] The block's parent (defined by block.parent_root) passes all validation (including execution
// node verification of the block.body.execution_payload).
func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, verifyingState state.ReadOnlyBeaconState, blk interfaces.ReadOnlyBeaconBlock) error {
if blk.Version() >= version.Gloas {
return nil
}
// Error if block and state are not the same version
if verifyingState.Version() != blk.Version() {
return errors.New("block and state are not the same version")

View File

@@ -0,0 +1,2 @@
### Fixed
- Avoid copying the full finalized state every time we compute cgc.

View File

@@ -0,0 +1,3 @@
### Added
- Add gossip beacon attestation validation conditions for Gloas fork

View File

@@ -0,0 +1,3 @@
### Added
- Add Gloas beacon block gossip validation for execution payload bids

View File

@@ -0,0 +1,2 @@
### Ignored
- Refactor ProcessExecutionPayload to ApplyExecutionPayload