From 9411be428f16f008e1d033a92404286f4aa2d743 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 26 Sep 2025 11:44:26 +0800 Subject: [PATCH 1/4] eth/catalyst: check getPayloadV4/5 based on Osaka --- eth/catalyst/api.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index b37c26149f6..88de8a29eca 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -446,7 +446,16 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork } - return api.getPayload(payloadID, false) + data, err := api.getPayload(payloadID, false) + if err != nil { + return nil, err + } + + // Check if the payload timestamp is greater or equal to Osaka activation timestamp + if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Osaka { + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV4 is not available after Osaka fork")) + } + return data, nil } // GetPayloadV5 returns a cached payload by id. This endpoint should only @@ -458,7 +467,16 @@ func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.Execu if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork } - return api.getPayload(payloadID, false) + data, err := api.getPayload(payloadID, false) + if err != nil { + return nil, err + } + + // Check if the payload timestamp falls within the time frame of the Osaka fork + if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) < forks.Osaka { + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV5 is not available after Osaka fork")) + } + return data, nil } func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) { From fac65f9c6c6e5c6ef7346c617d3b033c98cf3141 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 26 Sep 2025 12:27:36 +0800 Subject: [PATCH 2/4] check for v1..3 --- eth/catalyst/api.go | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 88de8a29eca..afb42f862b5 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -413,6 +413,10 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } + // Check if the payload timestamp is greater or equal to Shanghai activation timestamp + if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Shanghai { + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV1 is not available after Shanghai fork")) + } return data.ExecutionPayload, nil } @@ -428,7 +432,15 @@ func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.Execu if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) { return nil, engine.UnsupportedFork } - return api.getPayload(payloadID, false) + data, err := api.getPayload(payloadID, false) + if err != nil { + return nil, err + } + // Check if the payload timestamp is greater or equal to Cancun activation timestamp + if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Cancun { + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV2 is not available after Cancun fork")) + } + return data, nil } // GetPayloadV3 returns a cached payload by id. This endpoint should only @@ -437,7 +449,15 @@ func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.Execu if !payloadID.Is(engine.PayloadV3) { return nil, engine.UnsupportedFork } - return api.getPayload(payloadID, false) + data, err := api.getPayload(payloadID, false) + if err != nil { + return nil, err + } + // Check if the payload timestamp is greater or equal to Prague activation timestamp + if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Prague { + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV3 is not available after Prague fork")) + } + return data, nil } // GetPayloadV4 returns a cached payload by id. This endpoint should only @@ -450,7 +470,6 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp is greater or equal to Osaka activation timestamp if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Osaka { return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV4 is not available after Osaka fork")) @@ -471,10 +490,9 @@ func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp falls within the time frame of the Osaka fork if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) < forks.Osaka { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV5 is not available after Osaka fork")) + return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV5 is not available before Osaka fork")) } return data, nil } From c5593eddcd2e938bc1683f0e987725276e8c4b64 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 26 Sep 2025 13:00:17 +0800 Subject: [PATCH 3/4] check fork --- eth/catalyst/api.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index afb42f862b5..ebf4dc7f9dc 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -413,10 +413,6 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp is greater or equal to Shanghai activation timestamp - if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Shanghai { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV1 is not available after Shanghai fork")) - } return data.ExecutionPayload, nil } @@ -436,9 +432,9 @@ func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp is greater or equal to Cancun activation timestamp - if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Cancun { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV2 is not available after Cancun fork")) + // Check if the payload timestamp falls within the Shanghai fork timeframe + if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Shanghai) { + return nil, engine.UnsupportedFork } return data, nil } @@ -453,9 +449,9 @@ func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp is greater or equal to Prague activation timestamp - if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Prague { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV3 is not available after Prague fork")) + // Check if the payload timestamp falls within the Cancun fork timeframe + if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Cancun) { + return nil, engine.UnsupportedFork } return data, nil } @@ -470,9 +466,9 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp is greater or equal to Osaka activation timestamp - if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) >= forks.Osaka { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV4 is not available after Osaka fork")) + // Check if the payload timestamp falls within the Prague fork timeframe + if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Prague) { + return nil, engine.UnsupportedFork } return data, nil } @@ -490,9 +486,9 @@ func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } - // Check if the payload timestamp falls within the time frame of the Osaka fork + // Check if the payload timestamp falls within the time frame of the Osaka fork or later if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) < forks.Osaka { - return nil, engine.UnsupportedFork.With(errors.New("engine_getPayloadV5 is not available before Osaka fork")) + return nil, engine.UnsupportedFork } return data, nil } From 65dd7584bac82a8c79efb07b28c49132c0823452 Mon Sep 17 00:00:00 2001 From: lightclient Date: Fri, 26 Sep 2025 07:42:39 -0600 Subject: [PATCH 4/4] eth/catalyst: check timestamp for compatibility in GetPayload methods --- eth/catalyst/api.go | 106 +++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 60 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ebf4dc7f9dc..c5d6e46a0e6 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -406,10 +406,12 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit // GetPayloadV1 returns a cached payload by id. func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) { - if !payloadID.Is(engine.PayloadV1) { - return nil, engine.UnsupportedFork - } - data, err := api.getPayload(payloadID, false) + data, err := api.getPayload( + payloadID, + false, + []engine.PayloadVersion{engine.PayloadV1}, + nil, + ) if err != nil { return nil, err } @@ -418,59 +420,34 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu // GetPayloadV2 returns a cached payload by id. func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { - // executionPayload: ExecutionPayloadV1 | ExecutionPayloadV2 where: - // - // - ExecutionPayloadV1 MUST be returned if the payload timestamp is lower - // than the Shanghai timestamp - // - // - ExecutionPayloadV2 MUST be returned if the payload timestamp is greater - // or equal to the Shanghai timestamp - if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) { - return nil, engine.UnsupportedFork - } - data, err := api.getPayload(payloadID, false) - if err != nil { - return nil, err - } - // Check if the payload timestamp falls within the Shanghai fork timeframe - if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Shanghai) { - return nil, engine.UnsupportedFork - } - return data, nil + return api.getPayload( + payloadID, + false, + []engine.PayloadVersion{engine.PayloadV1, engine.PayloadV2}, + []forks.Fork{forks.Shanghai}, + ) } // GetPayloadV3 returns a cached payload by id. This endpoint should only // be used for the Cancun fork. func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { - if !payloadID.Is(engine.PayloadV3) { - return nil, engine.UnsupportedFork - } - data, err := api.getPayload(payloadID, false) - if err != nil { - return nil, err - } - // Check if the payload timestamp falls within the Cancun fork timeframe - if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Cancun) { - return nil, engine.UnsupportedFork - } - return data, nil + return api.getPayload( + payloadID, + false, + []engine.PayloadVersion{engine.PayloadV3}, + []forks.Fork{forks.Cancun}, + ) } // GetPayloadV4 returns a cached payload by id. This endpoint should only // be used for the Prague fork. func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { - if !payloadID.Is(engine.PayloadV3) { - return nil, engine.UnsupportedFork - } - data, err := api.getPayload(payloadID, false) - if err != nil { - return nil, err - } - // Check if the payload timestamp falls within the Prague fork timeframe - if data.ExecutionPayload != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks.Prague) { - return nil, engine.UnsupportedFork - } - return data, nil + return api.getPayload( + payloadID, + false, + []engine.PayloadVersion{engine.PayloadV3}, + []forks.Fork{forks.Prague}, + ) } // GetPayloadV5 returns a cached payload by id. This endpoint should only @@ -479,26 +456,35 @@ func (api *ConsensusAPI) GetPayloadV4(payloadID engine.PayloadID) (*engine.Execu // This method follows the same specification as engine_getPayloadV4 with // changes of returning BlobsBundleV2 with BlobSidecar version 1. func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { - if !payloadID.Is(engine.PayloadV3) { - return nil, engine.UnsupportedFork - } - data, err := api.getPayload(payloadID, false) - if err != nil { - return nil, err - } - // Check if the payload timestamp falls within the time frame of the Osaka fork or later - if data.ExecutionPayload != nil && api.config().LatestFork(data.ExecutionPayload.Timestamp) < forks.Osaka { - return nil, engine.UnsupportedFork - } - return data, nil + return api.getPayload( + payloadID, + false, + []engine.PayloadVersion{engine.PayloadV3}, + []forks.Fork{ + forks.Osaka, + forks.BPO1, + forks.BPO2, + forks.BPO3, + forks.BPO4, + forks.BPO5, + }) } -func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*engine.ExecutionPayloadEnvelope, error) { +// getPayload will retreive the specified payload and verify it conforms to the +// endpoint's allowed payload versions and forks. +func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool, versions []engine.PayloadVersion, forks []forks.Fork) (*engine.ExecutionPayloadEnvelope, error) { log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID) + if !payloadID.Is(versions...) { + return nil, engine.UnsupportedFork + } data := api.localBlocks.get(payloadID, full) if data == nil { return nil, engine.UnknownPayload } + if forks != nil && !api.checkFork(data.ExecutionPayload.Timestamp, forks...) { + return nil, engine.UnsupportedFork + } + return data, nil }