88#![ cfg_attr( not( test) , warn( unused_crate_dependencies) ) ]
99#![ cfg_attr( docsrs, feature( doc_cfg, doc_auto_cfg) ) ]
1010
11+ use alloy_primitives:: Bytes ;
12+ use reth_chainspec:: EthereumHardforks ;
13+
1114mod error;
1215pub use error:: {
1316 EngineObjectValidationError , InvalidPayloadAttributesError , PayloadBuilderError ,
@@ -24,7 +27,6 @@ pub use traits::{
2427mod payload;
2528pub use payload:: PayloadOrAttributes ;
2629
27- use reth_chainspec:: EthereumHardforks ;
2830/// The types that are used by the engine API.
2931pub trait PayloadTypes : Send + Sync + Unpin + core:: fmt:: Debug + Clone + ' static {
3032 /// The built payload type.
@@ -363,12 +365,85 @@ pub enum PayloadKind {
363365 WaitForPending ,
364366}
365367
368+ /// Validates that execution requests are valid according to Engine API specification.
369+ ///
370+ /// `executionRequests`: `Array of DATA` - List of execution layer triggered requests. Each list
371+ /// element is a `requests` byte array as defined by [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685).
372+ /// The first byte of each element is the `request_type` and the remaining bytes are the
373+ /// `request_data`. Elements of the list **MUST** be ordered by `request_type` in ascending order.
374+ /// Elements with empty `request_data` **MUST** be excluded from the list. If any element is out of
375+ /// order or has a length of 1-byte or shorter, client software **MUST** return `-32602: Invalid
376+ /// params` error.
377+ pub fn validate_execution_requests ( requests : & [ Bytes ] ) -> Result < ( ) , EngineObjectValidationError > {
378+ let mut last_request_type = None ;
379+ for request in requests {
380+ if request. len ( ) <= 1 {
381+ return Err ( EngineObjectValidationError :: InvalidParams (
382+ "empty execution request" . to_string ( ) . into ( ) ,
383+ ) )
384+ }
385+
386+ let request_type = request[ 0 ] ;
387+ if Some ( request_type) < last_request_type {
388+ return Err ( EngineObjectValidationError :: InvalidParams (
389+ "execution requests out of order" . to_string ( ) . into ( ) ,
390+ ) )
391+ }
392+
393+ last_request_type = Some ( request_type) ;
394+ }
395+ Ok ( ( ) )
396+ }
397+
366398#[ cfg( test) ]
367399mod tests {
368400 use super :: * ;
401+ use assert_matches:: assert_matches;
369402
370403 #[ test]
371404 fn version_ord ( ) {
372405 assert ! ( EngineApiMessageVersion :: V4 > EngineApiMessageVersion :: V3 ) ;
373406 }
407+
408+ #[ test]
409+ fn execution_requests_validation ( ) {
410+ assert_matches ! ( validate_execution_requests( & [ ] ) , Ok ( ( ) ) ) ;
411+
412+ let valid_requests = [
413+ Bytes :: from_iter ( [ 1 , 2 ] ) ,
414+ Bytes :: from_iter ( [ 2 , 3 ] ) ,
415+ Bytes :: from_iter ( [ 3 , 4 ] ) ,
416+ Bytes :: from_iter ( [ 4 , 5 ] ) ,
417+ ] ;
418+ assert_matches ! ( validate_execution_requests( & valid_requests) , Ok ( ( ) ) ) ;
419+
420+ let requests_with_empty = [
421+ Bytes :: from_iter ( [ 1 , 2 ] ) ,
422+ Bytes :: from_iter ( [ 2 , 3 ] ) ,
423+ Bytes :: new ( ) ,
424+ Bytes :: from_iter ( [ 3 , 4 ] ) ,
425+ ] ;
426+ assert_matches ! (
427+ validate_execution_requests( & requests_with_empty) ,
428+ Err ( EngineObjectValidationError :: InvalidParams ( _) )
429+ ) ;
430+
431+ let mut requests_valid_reversed = valid_requests;
432+ requests_valid_reversed. reverse ( ) ;
433+ assert_matches ! (
434+ validate_execution_requests( & requests_with_empty) ,
435+ Err ( EngineObjectValidationError :: InvalidParams ( _) )
436+ ) ;
437+
438+ let requests_out_of_order = [
439+ Bytes :: from_iter ( [ 1 , 2 ] ) ,
440+ Bytes :: from_iter ( [ 2 , 3 ] ) ,
441+ Bytes :: from_iter ( [ 4 , 5 ] ) ,
442+ Bytes :: from_iter ( [ 3 , 4 ] ) ,
443+ ] ;
444+ assert_matches ! (
445+ validate_execution_requests( & requests_out_of_order) ,
446+ Err ( EngineObjectValidationError :: InvalidParams ( _) )
447+ ) ;
448+ }
374449}
0 commit comments