@@ -62,8 +62,10 @@ pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone + 'static
6262/// * If V3, this ensures that the payload timestamp is within the Cancun timestamp.
6363/// * If V4, this ensures that the payload timestamp is within the Prague timestamp.
6464/// * If V5, this ensures that the payload timestamp is within the Osaka timestamp.
65+ /// * If V6, this ensures that the payload timestamp is within the Amsterdam timestamp.
6566///
66- /// Additionally, it ensures that `engine_getPayloadV4` is not used for an Osaka payload.
67+ /// Additionally, it ensures that `engine_getPayloadV4` is not used for an Osaka payload and that
68+ /// staggered endpoint upgrades reject the next fork once a newer method version is required.
6769///
6870/// Otherwise, this will return [`EngineObjectValidationError::UnsupportedFork`].
6971pub fn validate_payload_timestamp (
@@ -151,12 +153,26 @@ pub fn validate_payload_timestamp(
151153 return Err ( EngineObjectValidationError :: UnsupportedFork )
152154 }
153155
156+ let is_amsterdam = chain_spec. is_amsterdam_active_at_timestamp ( timestamp) ;
157+
158+ // Staggered endpoint upgrades must reject Amsterdam payloads until the Amsterdam-specific
159+ // method version is used.
160+ if is_amsterdam &&
161+ matches ! (
162+ ( version, kind) ,
163+ ( EngineApiMessageVersion :: V3 , MessageValidationKind :: PayloadAttributes ) |
164+ ( EngineApiMessageVersion :: V4 , MessageValidationKind :: Payload ) |
165+ ( EngineApiMessageVersion :: V5 , MessageValidationKind :: GetPayload )
166+ )
167+ {
168+ return Err ( EngineObjectValidationError :: UnsupportedFork )
169+ }
170+
154171 // `engine_getPayloadV4` MUST reject payloads with a timestamp >= Osaka.
155172 if version. is_v4 ( ) && kind == MessageValidationKind :: GetPayload && is_osaka {
156173 return Err ( EngineObjectValidationError :: UnsupportedFork )
157174 }
158175
159- let is_amsterdam = chain_spec. is_amsterdam_active_at_timestamp ( timestamp) ;
160176 if version. is_v6 ( ) && !is_amsterdam {
161177 // From the Engine API spec:
162178 // <https://github.com/ethereum/execution-apis/blob/15399c2e2f16a5f800bf3f285640357e2c245ad9/src/engine/osaka.md#specification>
@@ -183,16 +199,30 @@ pub fn validate_block_access_list_presence<T: EthereumHardforks>(
183199 has_block_access_list : bool ,
184200) -> Result < ( ) , EngineObjectValidationError > {
185201 let is_amsterdam_active = chain_spec. is_amsterdam_active_at_timestamp ( timestamp) ;
186-
187202 match version {
188203 EngineApiMessageVersion :: V1 |
189204 EngineApiMessageVersion :: V2 |
190205 EngineApiMessageVersion :: V3 |
191- EngineApiMessageVersion :: V4 |
192- EngineApiMessageVersion :: V5 => {
206+ EngineApiMessageVersion :: V4 => {
193207 if has_block_access_list {
194208 return Err ( message_validation_kind
195- . to_error ( VersionSpecificValidationError :: BlockAccessListNotSupportedBeforeV6 ) )
209+ . to_error ( VersionSpecificValidationError :: BlockAccessListNotSupported ) )
210+ }
211+ }
212+
213+ EngineApiMessageVersion :: V5 => {
214+ if message_validation_kind == MessageValidationKind :: Payload {
215+ if is_amsterdam_active && !has_block_access_list {
216+ return Err ( message_validation_kind
217+ . to_error ( VersionSpecificValidationError :: NoBlockAccessListPostAmsterdam ) )
218+ }
219+ if !is_amsterdam_active && has_block_access_list {
220+ return Err ( message_validation_kind
221+ . to_error ( VersionSpecificValidationError :: HasBlockAccessListPreAmsterdam ) )
222+ }
223+ } else if has_block_access_list {
224+ return Err ( message_validation_kind
225+ . to_error ( VersionSpecificValidationError :: BlockAccessListNotSupported ) )
196226 }
197227 }
198228
@@ -224,14 +254,42 @@ pub fn validate_slot_number_presence<T: EthereumHardforks>(
224254 let is_amsterdam_active = chain_spec. is_amsterdam_active_at_timestamp ( timestamp) ;
225255
226256 match version {
227- EngineApiMessageVersion :: V1 |
228- EngineApiMessageVersion :: V2 |
229- EngineApiMessageVersion :: V3 |
230- EngineApiMessageVersion :: V4 |
231- EngineApiMessageVersion :: V5 => {
257+ EngineApiMessageVersion :: V1 | EngineApiMessageVersion :: V2 | EngineApiMessageVersion :: V3 => {
232258 if has_slot_number {
233259 return Err ( message_validation_kind
234- . to_error ( VersionSpecificValidationError :: SlotNumberNotSupportedBeforeV6 ) )
260+ . to_error ( VersionSpecificValidationError :: SlotNumberNotSupported ) )
261+ }
262+ }
263+
264+ EngineApiMessageVersion :: V4 => {
265+ if message_validation_kind == MessageValidationKind :: PayloadAttributes {
266+ if is_amsterdam_active && !has_slot_number {
267+ return Err ( message_validation_kind
268+ . to_error ( VersionSpecificValidationError :: NoSlotNumberPostAmsterdam ) )
269+ }
270+ if !is_amsterdam_active && has_slot_number {
271+ return Err ( message_validation_kind
272+ . to_error ( VersionSpecificValidationError :: HasSlotNumberPreAmsterdam ) )
273+ }
274+ } else if has_slot_number {
275+ return Err ( message_validation_kind
276+ . to_error ( VersionSpecificValidationError :: SlotNumberNotSupported ) )
277+ }
278+ }
279+
280+ EngineApiMessageVersion :: V5 => {
281+ if message_validation_kind == MessageValidationKind :: Payload {
282+ if is_amsterdam_active && !has_slot_number {
283+ return Err ( message_validation_kind
284+ . to_error ( VersionSpecificValidationError :: NoSlotNumberPostAmsterdam ) )
285+ }
286+ if !is_amsterdam_active && has_slot_number {
287+ return Err ( message_validation_kind
288+ . to_error ( VersionSpecificValidationError :: HasSlotNumberPreAmsterdam ) )
289+ }
290+ } else if has_slot_number {
291+ return Err ( message_validation_kind
292+ . to_error ( VersionSpecificValidationError :: SlotNumberNotSupported ) )
235293 }
236294 }
237295
@@ -651,6 +709,75 @@ mod tests {
651709 assert_matches ! ( res, Ok ( ( ) ) ) ;
652710 }
653711
712+ #[ test]
713+ fn validate_amsterdam_staggered_version_restrictions ( ) {
714+ let chain_spec = ChainSpecBuilder :: mainnet ( ) . amsterdam_activated ( ) . build ( ) ;
715+
716+ let res = validate_payload_timestamp (
717+ & chain_spec,
718+ EngineApiMessageVersion :: V3 ,
719+ 0 ,
720+ MessageValidationKind :: PayloadAttributes ,
721+ ) ;
722+ assert_matches ! ( res, Err ( EngineObjectValidationError :: UnsupportedFork ) ) ;
723+
724+ let res = validate_payload_timestamp (
725+ & chain_spec,
726+ EngineApiMessageVersion :: V4 ,
727+ 0 ,
728+ MessageValidationKind :: Payload ,
729+ ) ;
730+ assert_matches ! ( res, Err ( EngineObjectValidationError :: UnsupportedFork ) ) ;
731+
732+ let res = validate_payload_timestamp (
733+ & chain_spec,
734+ EngineApiMessageVersion :: V5 ,
735+ 0 ,
736+ MessageValidationKind :: GetPayload ,
737+ ) ;
738+ assert_matches ! ( res, Err ( EngineObjectValidationError :: UnsupportedFork ) ) ;
739+
740+ let res = validate_payload_timestamp (
741+ & chain_spec,
742+ EngineApiMessageVersion :: V6 ,
743+ 0 ,
744+ MessageValidationKind :: GetPayload ,
745+ ) ;
746+ assert_matches ! ( res, Ok ( ( ) ) ) ;
747+ }
748+
749+ #[ test]
750+ fn validate_amsterdam_slot_and_bal_presence ( ) {
751+ let chain_spec = ChainSpecBuilder :: mainnet ( ) . amsterdam_activated ( ) . build ( ) ;
752+
753+ let res = validate_slot_number_presence (
754+ & chain_spec,
755+ EngineApiMessageVersion :: V4 ,
756+ MessageValidationKind :: PayloadAttributes ,
757+ 0 ,
758+ true ,
759+ ) ;
760+ assert_matches ! ( res, Ok ( ( ) ) ) ;
761+
762+ let res = validate_slot_number_presence (
763+ & chain_spec,
764+ EngineApiMessageVersion :: V5 ,
765+ MessageValidationKind :: Payload ,
766+ 0 ,
767+ true ,
768+ ) ;
769+ assert_matches ! ( res, Ok ( ( ) ) ) ;
770+
771+ let res = validate_block_access_list_presence (
772+ & chain_spec,
773+ EngineApiMessageVersion :: V5 ,
774+ MessageValidationKind :: Payload ,
775+ 0 ,
776+ true ,
777+ ) ;
778+ assert_matches ! ( res, Ok ( ( ) ) ) ;
779+ }
780+
654781 #[ test]
655782 fn execution_requests_validation ( ) {
656783 assert_matches ! ( validate_execution_requests( & [ ] ) , Ok ( ( ) ) ) ;
0 commit comments