From c178784e1cd4a58cb9d916d9dd7583abed502dc1 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 9 Dec 2024 20:27:48 -0800 Subject: [PATCH 1/3] Add proper gas limit check through local computation --- CHANGELOG.md | 1 + .../rpc/prysm/v1alpha1/validator/proposer.go | 7 +- .../v1alpha1/validator/proposer_bellatrix.go | 43 +++- .../validator/proposer_bellatrix_test.go | 189 ++++++++++++++++-- .../validator/proposer_execution_payload.go | 5 +- 5 files changed, 222 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6143188a7fa1..bffe3e888d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - Added a Prometheus error counter metric for SSE requests. - Save light client updates and bootstraps in DB. - Added more comprehensive tests for `BlockToLightClientHeader`. [PR](https://github.com/prysmaticlabs/prysm/pull/14699) +- Added proper gas limit check for header from the builder. ### Changed diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index 1b5f772b0064..8765a983658d 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -236,7 +236,12 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed // There's no reason to try to get a builder bid if local override is true. var builderBid builderapi.Bid if !(local.OverrideBuilder || skipMevBoost) { - builderBid, err = vs.getBuilderPayloadAndBlobs(ctx, sBlk.Block().Slot(), sBlk.Block().ProposerIndex()) + latestHeader, err := head.LatestExecutionPayloadHeader() + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get latest execution payload header: %v", err) + } + parentGasLimit := latestHeader.GasLimit() + builderBid, err = vs.getBuilderPayloadAndBlobs(ctx, sBlk.Block().Slot(), sBlk.Block().ProposerIndex(), parentGasLimit) if err != nil { builderGetPayloadMissCount.Inc() log.WithError(err).Error("Could not get builder payload") diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go index f05b0ee6b8db..6ef07cdf35f6 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go @@ -51,6 +51,7 @@ var emptyTransactionsRoot = [32]byte{127, 254, 36, 30, 166, 1, 135, 253, 176, 24 // blockBuilderTimeout is the maximum amount of time allowed for a block builder to respond to a // block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec. const blockBuilderTimeout = 1 * time.Second +const gasLimitAdjustmentFactor = 1024 // Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions. func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, local *blocks.GetPayloadResponse, bid builder.Bid, builderBoostFactor primitives.Gwei) (primitives.Wei, *enginev1.BlobsBundle, error) { @@ -170,7 +171,11 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc // This function retrieves the payload header and kzg commitments given the slot number and the validator index. // It's a no-op if the latest head block is not versioned bellatrix. -func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot primitives.Slot, idx primitives.ValidatorIndex) (builder.Bid, error) { +func (vs *Server) getPayloadHeaderFromBuilder( + ctx context.Context, + slot primitives.Slot, + idx primitives.ValidatorIndex, + parentGasLimit uint64) (builder.Bid, error) { ctx, span := trace.StartSpan(ctx, "ProposerServer.getPayloadHeaderFromBuilder") defer span.End() @@ -243,6 +248,16 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot primitiv return nil, fmt.Errorf("incorrect parent hash %#x != %#x", header.ParentHash(), h.BlockHash()) } + reg, err := vs.BlockBuilder.RegistrationByValidatorID(ctx, idx) + if err != nil { + log.WithError(err).Warn("Proposer: failed to get registration by validator ID, could not check gas limit") + } else { + gasLimit := expectedGasLimit(parentGasLimit, reg.GasLimit) + if gasLimit != header.GasLimit() { + return nil, fmt.Errorf("incorrect header gas limit %d != %d", gasLimit, header.GasLimit()) + } + } + t, err := slots.ToTime(uint64(vs.TimeFetcher.GenesisTime().Unix()), slot) if err != nil { return nil, err @@ -393,3 +408,29 @@ func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.Executi return nil } + +// Calculates expected gas limit based on parent gas limit and target gas limit. +// Spec code: +// +// def expected_gas_limit(parent_gas_limit, target_gas_limit, adjustment_factor): +// max_gas_limit_difference = (parent_gas_limit // adjustment_factor) - 1 +// if target_gas_limit > parent_gas_limit: +// gas_diff = target_gas_limit - parent_gas_limit +// return parent_gas_limit + min(gas_diff, max_gas_limit_difference) +// else: +// gas_diff = parent_gas_limit - target_gas_limit +// return parent_gas_limit - min(gas_diff, max_gas_limit_difference) +func expectedGasLimit(parentGasLimit, targetGasLimit uint64) uint64 { + maxGasLimitDiff := parentGasLimit/gasLimitAdjustmentFactor - 1 + if targetGasLimit > parentGasLimit { + if targetGasLimit-parentGasLimit > maxGasLimitDiff { + return parentGasLimit + maxGasLimitDiff + } + return targetGasLimit + } else { + if parentGasLimit-targetGasLimit > maxGasLimitDiff { + return parentGasLimit - maxGasLimitDiff + } + return targetGasLimit + } +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go index 7d02c392539e..1183b6726fe4 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go @@ -94,14 +94,14 @@ func TestServer_setExecutionData(t *testing.T) { ForkchoiceFetcher: &blockchainTest.ChainService{}, TrackedValidatorsCache: cache.NewTrackedValidatorsCache(), } - + gasLimit := uint64(30000000) t.Run("No builder configured. Use local block", func(t *testing.T) { blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella()) require.NoError(t, err) b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) require.IsNil(t, builderBid) _, bundle, err := setExecutionData(context.Background(), blk, res, builderBid, defaultBuilderBoostFactor) @@ -115,7 +115,11 @@ func TestServer_setExecutionData(t *testing.T) { blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella()) require.NoError(t, err) require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()}, - []*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) + []*ethpb.ValidatorRegistrationV1{{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + Timestamp: uint64(time.Now().Unix()), + GasLimit: gasLimit, + Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) ti, err := slots.ToTime(uint64(time.Now().Unix()), 0) require.NoError(t, err) sk, err := bls.RandKey() @@ -135,6 +139,7 @@ func TestServer_setExecutionData(t *testing.T) { BlockHash: make([]byte, fieldparams.RootLength), TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength), WithdrawalsRoot: make([]byte, fieldparams.RootLength), + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo([]byte{1}, 32), @@ -164,7 +169,7 @@ func TestServer_setExecutionData(t *testing.T) { res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -184,7 +189,11 @@ func TestServer_setExecutionData(t *testing.T) { blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella()) require.NoError(t, err) require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()}, - []*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) + []*ethpb.ValidatorRegistrationV1{{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + Timestamp: uint64(time.Now().Unix()), + GasLimit: gasLimit, + Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) ti, err := slots.ToTime(uint64(time.Now().Unix()), 0) require.NoError(t, err) sk, err := bls.RandKey() @@ -207,6 +216,7 @@ func TestServer_setExecutionData(t *testing.T) { BlockHash: make([]byte, fieldparams.RootLength), TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength), WithdrawalsRoot: wr[:], + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo(builderValue, 32), @@ -236,7 +246,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -256,7 +266,11 @@ func TestServer_setExecutionData(t *testing.T) { blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella()) require.NoError(t, err) require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()}, - []*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) + []*ethpb.ValidatorRegistrationV1{{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + Timestamp: uint64(time.Now().Unix()), + GasLimit: gasLimit, + Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) ti, err := slots.ToTime(uint64(time.Now().Unix()), 0) require.NoError(t, err) sk, err := bls.RandKey() @@ -278,6 +292,7 @@ func TestServer_setExecutionData(t *testing.T) { Timestamp: uint64(ti.Unix()), BlockNumber: 2, WithdrawalsRoot: wr[:], + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo(builderValue, 32), @@ -307,7 +322,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -327,7 +342,11 @@ func TestServer_setExecutionData(t *testing.T) { blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella()) require.NoError(t, err) require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()}, - []*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) + []*ethpb.ValidatorRegistrationV1{{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + Timestamp: uint64(time.Now().Unix()), + GasLimit: gasLimit, + Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) ti, err := slots.ToTime(uint64(time.Now().Unix()), 0) require.NoError(t, err) sk, err := bls.RandKey() @@ -349,6 +368,7 @@ func TestServer_setExecutionData(t *testing.T) { Timestamp: uint64(ti.Unix()), BlockNumber: 2, WithdrawalsRoot: wr[:], + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo(builderValue, 32), @@ -378,7 +398,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -404,7 +424,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -436,7 +456,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) _, err = builderBid.Header() require.NoError(t, err) @@ -471,7 +491,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.NoError(t, err) builderKzgCommitments, err := builderBid.BlobKzgCommitments() if builderBid.Version() >= version.Deneb { @@ -503,7 +523,7 @@ func TestServer_setExecutionData(t *testing.T) { b := blk.Block() res, err := vs.getLocalPayload(ctx, b, capellaTransitionState) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex(), gasLimit) require.ErrorIs(t, consensus_types.ErrNilObjectWrapped, err) // Builder returns fault. Use local block require.IsNil(t, builderBid) _, bundle, err := setExecutionData(context.Background(), blk, res, nil, defaultBuilderBoostFactor) @@ -578,6 +598,7 @@ func TestServer_setExecutionData(t *testing.T) { WithdrawalsRoot: wr[:], BlobGasUsed: 123, ExcessBlobGas: 456, + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo(builderValue, 32), @@ -599,7 +620,11 @@ func TestServer_setExecutionData(t *testing.T) { Cfg: &builderTest.Config{BeaconDB: beaconDB}, } require.NoError(t, beaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()}, - []*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) + []*ethpb.ValidatorRegistrationV1{{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + Timestamp: uint64(time.Now().Unix()), + GasLimit: gasLimit, + Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}})) wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockDeneb()) require.NoError(t, err) @@ -619,7 +644,7 @@ func TestServer_setExecutionData(t *testing.T) { require.NoError(t, err) blk.SetSlot(primitives.Slot(params.BeaconConfig().DenebForkEpoch) * params.BeaconConfig().SlotsPerEpoch) require.NoError(t, err) - builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, blk.Block().Slot(), blk.Block().ProposerIndex()) + builderBid, err := vs.getBuilderPayloadAndBlobs(ctx, blk.Block().Slot(), blk.Block().ProposerIndex(), gasLimit) require.NoError(t, err) builderPayload, err := builderBid.Header() require.NoError(t, err) @@ -660,6 +685,8 @@ func TestServer_getPayloadHeader(t *testing.T) { sk, err := bls.RandKey() require.NoError(t, err) + + gasLimit := uint64(30000000) bid := ðpb.BuilderBid{ Header: &v1.ExecutionPayloadHeader{ FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), @@ -672,6 +699,7 @@ func TestServer_getPayloadHeader(t *testing.T) { TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength), ParentHash: params.BeaconConfig().ZeroHash[:], Timestamp: uint64(ti.Unix()), + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo([]byte{1, 2, 3}, 32), @@ -709,6 +737,7 @@ func TestServer_getPayloadHeader(t *testing.T) { ParentHash: params.BeaconConfig().ZeroHash[:], Timestamp: uint64(tiCapella.Unix()), WithdrawalsRoot: wr[:], + GasLimit: gasLimit, }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo([]byte{1, 2, 3}, 32), @@ -720,7 +749,29 @@ func TestServer_getPayloadHeader(t *testing.T) { Signature: sk.Sign(srCapella[:]).Marshal(), } - require.NoError(t, err) + incorrectGasLimitBid := ðpb.BuilderBid{ + Header: &v1.ExecutionPayloadHeader{ + FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), + StateRoot: make([]byte, fieldparams.RootLength), + ReceiptsRoot: make([]byte, fieldparams.RootLength), + LogsBloom: make([]byte, fieldparams.LogsBloomLength), + PrevRandao: make([]byte, fieldparams.RootLength), + BaseFeePerGas: make([]byte, fieldparams.RootLength), + BlockHash: make([]byte, fieldparams.RootLength), + TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength), + ParentHash: params.BeaconConfig().ZeroHash[:], + Timestamp: uint64(tiCapella.Unix()), + GasLimit: 31000000, + }, + Pubkey: sk.PublicKey().Marshal(), + Value: bytesutil.PadTo([]byte{1, 2, 3}, 32), + } + signedIncorrectGasLimitBid := + ðpb.SignedBuilderBid{ + Message: incorrectGasLimitBid, + Signature: sk.Sign(srCapella[:]).Marshal(), + } + tests := []struct { name string head interfaces.ReadOnlySignedBeaconBlock @@ -847,15 +898,39 @@ func TestServer_getPayloadHeader(t *testing.T) { }, returnedHeaderCapella: bidCapella.Header, }, + { + name: "incorrect gas limit", + mock: &builderTest.MockBuilderService{ + Bid: signedIncorrectGasLimitBid, + }, + fetcher: &blockchainTest.ChainService{ + Block: func() interfaces.ReadOnlySignedBeaconBlock { + wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix()) + require.NoError(t, err) + wb.SetSlot(primitives.Slot(params.BeaconConfig().BellatrixForkEpoch) * params.BeaconConfig().SlotsPerEpoch) + return wb + }(), + }, + err: "incorrect header gas limit 30000000 != 31000000", + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - vs := &Server{BlockBuilder: tc.mock, HeadFetcher: tc.fetcher, TimeFetcher: &blockchainTest.ChainService{ + vs := &Server{BeaconDB: dbTest.SetupDB(t), BlockBuilder: tc.mock, HeadFetcher: tc.fetcher, TimeFetcher: &blockchainTest.ChainService{ Genesis: genesis, }} + regCache := cache.NewRegistrationCache() + regCache.UpdateIndexToRegisteredMap(context.Background(), map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1{ + 0: { + GasLimit: gasLimit, + FeeRecipient: make([]byte, 20), + Pubkey: make([]byte, 48), + }, + }) + tc.mock.RegistrationCache = regCache hb, err := vs.HeadFetcher.HeadBlock(context.Background()) require.NoError(t, err) - bid, err := vs.getPayloadHeaderFromBuilder(context.Background(), hb.Block().Slot(), 0) + bid, err := vs.getPayloadHeaderFromBuilder(context.Background(), hb.Block().Slot(), 0, 30000000) if tc.err != "" { require.ErrorContains(t, tc.err, err) } else { @@ -971,3 +1046,79 @@ func TestEmptyTransactionsRoot(t *testing.T) { require.NoError(t, err) require.DeepEqual(t, r, emptyTransactionsRoot) } + +func Test_expectedGasLimit(t *testing.T) { + type args struct { + parentGasLimit uint64 + targetGasLimit uint64 + } + tests := []struct { + name string + args args + want uint64 + }{ + { + name: "Increase within limit", + args: args{ + parentGasLimit: 15000000, + targetGasLimit: 15000100, + }, + want: 15000100, + }, + { + name: "Increase exceeding limit", + args: args{ + parentGasLimit: 15000000, + targetGasLimit: 16000000, + }, + want: 15014647, // maxGasLimitDiff = (15000000 / 1024) - 1 = 1464 + }, + { + name: "Decrease within limit", + args: args{ + parentGasLimit: 15000000, + targetGasLimit: 14999990, + }, + want: 14999990, + }, + { + name: "Decrease exceeding limit", + args: args{ + parentGasLimit: 15000000, + targetGasLimit: 14000000, + }, + want: 14985353, // maxGasLimitDiff = (15000000 / 1024) - 1 = 1464 + }, + { + name: "Target equals parent", + args: args{ + parentGasLimit: 15000000, + targetGasLimit: 15000000, + }, + want: 15000000, // No change + }, + { + name: "Very small parent gas limit", + args: args{ + parentGasLimit: 1025, + targetGasLimit: 2000, + }, + want: 1025 + ((1025 / 1024) - 1), + }, + { + name: "Target far below parent but limited", + args: args{ + parentGasLimit: 20000000, + targetGasLimit: 10000000, + }, + want: 19980470, // maxGasLimitDiff = (20000000 / 1024) - 1 + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := expectedGasLimit(tt.args.parentGasLimit, tt.args.targetGasLimit); got != tt.want { + t.Errorf("expectedGasLimit() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go index 00d98853fd2d..d491daa22ba0 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go @@ -239,7 +239,8 @@ func (vs *Server) getTerminalBlockHashIfExists(ctx context.Context, transitionTi func (vs *Server) getBuilderPayloadAndBlobs(ctx context.Context, slot primitives.Slot, - vIdx primitives.ValidatorIndex) (builder.Bid, error) { + vIdx primitives.ValidatorIndex, + parentGasLimit uint64) (builder.Bid, error) { ctx, span := trace.StartSpan(ctx, "ProposerServer.getBuilderPayloadAndBlobs") defer span.End() @@ -255,7 +256,7 @@ func (vs *Server) getBuilderPayloadAndBlobs(ctx context.Context, return nil, nil } - return vs.getPayloadHeaderFromBuilder(ctx, slot, vIdx) + return vs.getPayloadHeaderFromBuilder(ctx, slot, vIdx, parentGasLimit) } var errActivationNotReached = errors.New("activation epoch not reached") From 77abdfd6ae42f803d90619bbb35ca32160775653 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 10 Dec 2024 06:52:06 -0800 Subject: [PATCH 2/3] Potuz's feedback --- .../v1alpha1/validator/proposer_bellatrix.go | 24 +++++++++++-------- .../validator/proposer_bellatrix_test.go | 8 +++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go index 6ef07cdf35f6..398be240de35 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go @@ -420,17 +420,21 @@ func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.Executi // else: // gas_diff = parent_gas_limit - target_gas_limit // return parent_gas_limit - min(gas_diff, max_gas_limit_difference) -func expectedGasLimit(parentGasLimit, targetGasLimit uint64) uint64 { - maxGasLimitDiff := parentGasLimit/gasLimitAdjustmentFactor - 1 - if targetGasLimit > parentGasLimit { - if targetGasLimit-parentGasLimit > maxGasLimitDiff { +func expectedGasLimit(parentGasLimit, proposerGasLimit uint64) uint64 { + maxGasLimitDiff := uint64(0) + if parentGasLimit > gasLimitAdjustmentFactor { + maxGasLimitDiff = parentGasLimit/gasLimitAdjustmentFactor - 1 + } + if proposerGasLimit > parentGasLimit { + if proposerGasLimit-parentGasLimit > maxGasLimitDiff { return parentGasLimit + maxGasLimitDiff } - return targetGasLimit - } else { - if parentGasLimit-targetGasLimit > maxGasLimitDiff { - return parentGasLimit - maxGasLimitDiff - } - return targetGasLimit + return proposerGasLimit } + + if parentGasLimit-proposerGasLimit > maxGasLimitDiff { + return parentGasLimit - maxGasLimitDiff + } + return proposerGasLimit + } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go index 1183b6726fe4..54f06c918191 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix_test.go @@ -1113,6 +1113,14 @@ func Test_expectedGasLimit(t *testing.T) { }, want: 19980470, // maxGasLimitDiff = (20000000 / 1024) - 1 }, + { + name: "Parent gas limit under flows", + args: args{ + parentGasLimit: 1023, + targetGasLimit: 30000000, + }, + want: 1023, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 824b21c3db3e66159898c9955d1eb12d4f970d0c Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 10 Dec 2024 07:05:52 -0800 Subject: [PATCH 3/3] Fix new line --- beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go | 1 - 1 file changed, 1 deletion(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go index 398be240de35..4c341dc0e942 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go @@ -436,5 +436,4 @@ func expectedGasLimit(parentGasLimit, proposerGasLimit uint64) uint64 { return parentGasLimit - maxGasLimitDiff } return proposerGasLimit - }