diff --git a/consensus/misc/eip1559.go b/consensus/misc/eip1559.go index fd578264e47..e0a2d724643 100644 --- a/consensus/misc/eip1559.go +++ b/consensus/misc/eip1559.go @@ -104,29 +104,66 @@ func (f eip1559Calculator) CurrentFees(chainConfig *chain.Config, db kv.Getter) // DecodeHolocene1559Params extracts the Holcene 1559 parameters from the encoded form: // https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#eip1559params-encoding -func DecodeHolocene1559Params(params types.BlockNonce) (uint64, uint64) { - elasticity := binary.BigEndian.Uint32(params[4:]) +// +// Returns 0,0 if the format is invalid, though ValidateHolocene1559Params should be used instead of +// this function for validity checking. +func DecodeHolocene1559Params(params []byte) (uint64, uint64) { + if len(params) != 8 { + return 0, 0 + } denominator := binary.BigEndian.Uint32(params[:4]) - return uint64(elasticity), uint64(denominator) + elasticity := binary.BigEndian.Uint32(params[4:]) + return uint64(denominator), uint64(elasticity) } -func EncodeHolocene1559Params(elasticity, denom uint32) types.BlockNonce { - var nonce types.BlockNonce - binary.BigEndian.PutUint32(nonce[4:], elasticity) - binary.BigEndian.PutUint32(nonce[:4], denom) - return nonce +// DecodeHoloceneExtraData decodes holocene extra data without performing full validation. +func DecodeHoloceneExtraData(extra []byte) (uint64, uint64) { + if len(extra) != 9 { + return 0, 0 + } + return DecodeHolocene1559Params(extra[1:]) +} + +func EncodeHolocene1559Params(denom, elasticity uint32) []byte { + r := make([]byte, 8) + binary.BigEndian.PutUint32(r[:4], denom) + binary.BigEndian.PutUint32(r[4:], elasticity) + return r } -// ValidateHoloceneParams checks if the encoded parameters are valid according to the Holocene +func EncodeHoloceneExtraData(denom, elasticity uint32) []byte { + r := make([]byte, 9) + // leave version byte 0 + binary.BigEndian.PutUint32(r[1:5], denom) + binary.BigEndian.PutUint32(r[5:], elasticity) + return r +} + +// ValidateHolocene1559Params checks if the encoded parameters are valid according to the Holocene // upgrade. -func ValidateHoloceneParams(params types.BlockNonce) error { - e, d := DecodeHolocene1559Params(params) +func ValidateHolocene1559Params(params []byte) error { + if len(params) != 8 { + return fmt.Errorf("holocene eip-1559 params should be 8 bytes, got %d", len(params)) + } + d, e := DecodeHolocene1559Params(params) if e != 0 && d == 0 { return fmt.Errorf("holocene params cannot have a 0 denominator unless elasticity is also 0") } return nil } +// ValidateHoloceneExtraData checks if the header extraData is valid according to the Holocene +// upgrade. +func ValidateHoloceneExtraData(extra []byte) error { + if len(extra) != 9 { + return fmt.Errorf("holocene extraData should be 9 bytes, got %d", len(extra)) + } + if extra[0] != 0 { + return fmt.Errorf("holocene extraData should have 0 version byte, got %d", extra[0]) + } + return ValidateHolocene1559Params(extra[1:]) +} + // The time belongs to the new block to check which upgrades are active. func CalcBaseFee(config *chain.Config, parent *types.Header, time uint64) *big.Int { // If the current block is the first EIP-1559 block, return the InitialBaseFee. @@ -137,11 +174,11 @@ func CalcBaseFee(config *chain.Config, parent *types.Header, time uint64) *big.I elasticity := config.ElasticityMultiplier(params.ElasticityMultiplier) denominator := getBaseFeeChangeDenominator(config, params.BaseFeeChangeDenominator, time) - if config.IsHolocene(time) { - // Holocene requires we get the 1559 parameters from the nonce field of the parent header - // unless the field is zero, in which case we use the Canyon values. - if parent.Nonce != types.BlockNonce([8]byte{}) { - elasticity, denominator = DecodeHolocene1559Params(parent.Nonce) + if config.IsHolocene(parent.Time) { + denominator, elasticity = DecodeHoloceneExtraData(parent.Extra) + if denominator == 0 { + // this shouldn't happen as the ExtraData should have been validated prior + panic("invalid eip-1559 params in extradata") } } diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go index 1f300fc8fb5..cf505d2ff27 100644 --- a/consensus/misc/eip1559_test.go +++ b/consensus/misc/eip1559_test.go @@ -160,8 +160,8 @@ func TestCalcBaseFeeOptimism(t *testing.T) { t.Errorf("test %d: have %d want %d, ", i, have, want) } if test.postCanyon { - // make sure Holocene activation doesn't change the outcome; since these tests have a - // zero nonce, they should be handled using the Canyon config. + // make sure Holocene activation doesn't change the outcome; since these tests have empty eip1559 params, + // they should be handled using the Canyon config. parent.Time = 10 if have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) @@ -172,22 +172,19 @@ func TestCalcBaseFeeOptimism(t *testing.T) { // TestCalcBaseFeeHolocene assumes all blocks are Optimism blocks post-Holocene upgrade func TestCalcBaseFeeOptimismHolocene(t *testing.T) { - elasticity2Denom10Nonce := EncodeHolocene1559Params(2, 10) - elasticity10Denom2Nonce := EncodeHolocene1559Params(10, 2) parentBaseFee := int64(10_000_000) parentGasLimit := uint64(30_000_000) - tests := []struct { - parentGasUsed uint64 - expectedBaseFee int64 - nonce types.BlockNonce + parentGasUsed uint64 + expectedBaseFee int64 + denom, elasticity uint32 }{ - {parentGasLimit / 2, parentBaseFee, elasticity2Denom10Nonce}, // target - {10_000_000, 9_666_667, elasticity2Denom10Nonce}, // below - {20_000_000, 10_333_333, elasticity2Denom10Nonce}, // above - {parentGasLimit / 10, parentBaseFee, elasticity10Denom2Nonce}, // target - {1_000_000, 6_666_667, elasticity10Denom2Nonce}, // below - {30_000_000, 55_000_000, elasticity10Denom2Nonce}, // above + {parentGasLimit / 2, parentBaseFee, 10, 2}, // target + {10_000_000, 9_666_667, 10, 2}, // below + {20_000_000, 10_333_333, 10, 2}, // above + {parentGasLimit / 10, parentBaseFee, 2, 10}, // target + {1_000_000, 6_666_667, 2, 10}, // below + {30_000_000, 55_000_000, 2, 10}, // above } for i, test := range tests { parent := &types.Header{ @@ -195,8 +192,8 @@ func TestCalcBaseFeeOptimismHolocene(t *testing.T) { GasLimit: parentGasLimit, GasUsed: test.parentGasUsed, BaseFee: big.NewInt(parentBaseFee), - Time: 10, - Nonce: test.nonce, + Time: 12, + Extra: EncodeHoloceneExtraData(test.denom, test.elasticity), } if have, want := CalcBaseFee(opConfig(), parent, parent.Time+2), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 { t.Errorf("test %d: have %d want %d, ", i, have, want) diff --git a/erigon-lib/gointerfaces/types/types.pb.go b/erigon-lib/gointerfaces/types/types.pb.go index adae72de7ec..133e8fe7981 100644 --- a/erigon-lib/gointerfaces/types/types.pb.go +++ b/erigon-lib/gointerfaces/types/types.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.33.0 // protoc v4.24.2 // source: types/types.proto diff --git a/eth/stagedsync/stage_mining_create_block.go b/eth/stagedsync/stage_mining_create_block.go index 86157e25079..3b22ef3a343 100644 --- a/eth/stagedsync/stage_mining_create_block.go +++ b/eth/stagedsync/stage_mining_create_block.go @@ -219,22 +219,17 @@ func SpawnMiningCreateBlockStage(s *StageState, tx kv.RwTx, cfg MiningCreateBloc ibs := state.New(stateReader) if cfg.chainConfig.IsHolocene(header.Time) { - if cfg.blockBuilderParameters.EIP1559Params == nil { - return fmt.Errorf("expected eip1559 params, got none") - } - var nonce types.BlockNonce - copy(nonce[:], cfg.blockBuilderParameters.EIP1559Params) - if err := misc.ValidateHoloceneParams(nonce); err != nil { + if err := misc.ValidateHolocene1559Params(cfg.blockBuilderParameters.EIP1559Params); err != nil { return err } - header.Nonce = nonce - // If this is a holocene block and the params are 0, we must convert them to their Canyon - // defaults in the header. - if header.Nonce == [8]byte{} { - elasticity := cfg.chainConfig.ElasticityMultiplier(params.ElasticityMultiplier) - denominator := cfg.chainConfig.BaseFeeChangeDenominator(params.BaseFeeChangeDenominator, header.Time) - header.Nonce = misc.EncodeHolocene1559Params(uint32(elasticity), uint32(denominator)) + // If this is a holocene block and the params are 0, we must convert them to their previous + // constants in the header. + d, e := misc.DecodeHolocene1559Params(cfg.blockBuilderParameters.EIP1559Params) + if d == 0 { + d = cfg.chainConfig.BaseFeeChangeDenominator(params.BaseFeeChangeDenominator, header.Time) + e = cfg.chainConfig.ElasticityMultiplier(params.ElasticityMultiplier) } + header.Extra = misc.EncodeHoloceneExtraData(uint32(d), uint32(e)) } else if cfg.blockBuilderParameters.EIP1559Params != nil { return fmt.Errorf("got eip1559 params, expected none") } diff --git a/turbo/engineapi/engine_server.go b/turbo/engineapi/engine_server.go index 78702bd197e..fcb51844434 100644 --- a/turbo/engineapi/engine_server.go +++ b/turbo/engineapi/engine_server.go @@ -1,6 +1,7 @@ package engineapi import ( + "bytes" "context" "encoding/binary" "errors" @@ -159,6 +160,14 @@ func (s *EngineServer) newPayload(ctx context.Context, req *engine_types.Executi ReceiptHash: req.ReceiptsRoot, TxHash: types.DeriveSha(types.BinaryTransactions(txs)), } + + // Payload must have eip-1559 params in ExtraData after Holocene + if s.config.IsHolocene(req.Timestamp.Uint64()) { + if err := misc.ValidateHoloceneExtraData(req.ExtraData); err != nil { + return nil, &rpc.InvalidParamsError{Message: "holocene payloads must have eip-1559 params, got none"} + } + } + var withdrawals []*types.Withdrawal if version >= clparams.CapellaVersion { withdrawals = req.Withdrawals @@ -531,7 +540,6 @@ func (s *EngineServer) forkchoiceUpdated(ctx context.Context, forkchoiceState *e SuggestedFeeRecipient: gointerfaces.ConvertAddressToH160(payloadAttributes.SuggestedFeeRecipient), Transactions: txs, NoTxPool: payloadAttributes.NoTxPool, - Eip_1559Params: payloadAttributes.EIP1559Params, } if version >= clparams.CapellaVersion { @@ -547,14 +555,10 @@ func (s *EngineServer) forkchoiceUpdated(ctx context.Context, forkchoiceState *e return nil, &engine_helpers.InvalidPayloadAttributesErr } if s.config.IsHolocene(payloadAttributes.Timestamp.Uint64()) { - var eip1559Params types.BlockNonce - copy(eip1559Params[:], payloadAttributes.EIP1559Params) - if len(payloadAttributes.EIP1559Params) != 8 { + if err := misc.ValidateHolocene1559Params(payloadAttributes.EIP1559Params); err != nil { return nil, &engine_helpers.InvalidPayloadAttributesErr } - if err := misc.ValidateHoloceneParams(eip1559Params); err != nil { - return nil, err - } + req.Eip_1559Params = bytes.Clone(payloadAttributes.EIP1559Params) } else if len(payloadAttributes.EIP1559Params) != 0 { return nil, &engine_helpers.InvalidPayloadAttributesErr }