@@ -7,7 +7,7 @@ use superstruct::superstruct;
7
7
use types:: beacon_block_body:: KzgCommitments ;
8
8
use types:: blob_sidecar:: BlobsList ;
9
9
use types:: execution_requests:: {
10
- ConsolidationRequests , DepositRequests , RequestPrefix , WithdrawalRequests ,
10
+ ConsolidationRequests , DepositRequests , RequestType , WithdrawalRequests ,
11
11
} ;
12
12
use types:: { Blob , FixedVector , KzgProof , Unsigned } ;
13
13
@@ -401,47 +401,80 @@ impl<E: EthSpec> From<JsonExecutionPayload<E>> for ExecutionPayload<E> {
401
401
}
402
402
}
403
403
404
+ #[ derive( Debug , Clone ) ]
405
+ pub enum RequestsError {
406
+ InvalidHex ( hex:: FromHexError ) ,
407
+ EmptyRequest ( usize ) ,
408
+ InvalidOrdering ,
409
+ InvalidPrefix ( u8 ) ,
410
+ DecodeError ( String ) ,
411
+ }
412
+
404
413
/// Format of `ExecutionRequests` received over the engine api.
405
414
///
406
- /// Array of ssz-encoded requests list encoded as hex bytes.
407
- /// The prefix of the request type is used to index into the array.
408
- ///
409
- /// For e.g. [0xab, 0xcd, 0xef]
410
- /// Here, 0xab are the deposits bytes (prefix and index == 0)
411
- /// 0xcd are the withdrawals bytes (prefix and index == 1)
412
- /// 0xef are the consolidations bytes (prefix and index == 2)
415
+ /// Array of ssz-encoded requests list encoded as hex bytes prefixed
416
+ /// with a `RequestType`
413
417
#[ derive( Debug , Default , Clone , PartialEq , Serialize , Deserialize ) ]
414
418
#[ serde( transparent) ]
415
419
pub struct JsonExecutionRequests ( pub Vec < String > ) ;
416
420
417
421
impl < E : EthSpec > TryFrom < JsonExecutionRequests > for ExecutionRequests < E > {
418
- type Error = String ;
422
+ type Error = RequestsError ;
419
423
420
424
fn try_from ( value : JsonExecutionRequests ) -> Result < Self , Self :: Error > {
421
425
let mut requests = ExecutionRequests :: default ( ) ;
422
-
426
+ let mut prev_prefix : Option < RequestType > = None ;
423
427
for ( i, request) in value. 0 . into_iter ( ) . enumerate ( ) {
424
428
// hex string
425
429
let decoded_bytes = hex:: decode ( request. strip_prefix ( "0x" ) . unwrap_or ( & request) )
426
- . map_err ( |e| format ! ( "Invalid hex {:?}" , e) ) ?;
427
- match RequestPrefix :: from_prefix ( i as u8 ) {
428
- Some ( RequestPrefix :: Deposit ) => {
429
- requests. deposits = DepositRequests :: < E > :: from_ssz_bytes ( & decoded_bytes)
430
- . map_err ( |e| format ! ( "Failed to decode DepositRequest from EL: {:?}" , e) ) ?;
430
+ . map_err ( RequestsError :: InvalidHex ) ?;
431
+
432
+ // The first byte of each element is the `request_type` and the remaining bytes are the `request_data`.
433
+ // Elements with empty `request_data` **MUST** be excluded from the list.
434
+ let Some ( ( prefix_byte, request_bytes) ) = decoded_bytes. split_first ( ) else {
435
+ return Err ( RequestsError :: EmptyRequest ( i) ) ;
436
+ } ;
437
+ if request_bytes. is_empty ( ) {
438
+ return Err ( RequestsError :: EmptyRequest ( i) ) ;
439
+ }
440
+ // Elements of the list **MUST** be ordered by `request_type` in ascending order
441
+ let current_prefix = RequestType :: from_u8 ( * prefix_byte)
442
+ . ok_or ( RequestsError :: InvalidPrefix ( * prefix_byte) ) ?;
443
+ if let Some ( prev) = prev_prefix {
444
+ if prev. to_u8 ( ) >= current_prefix. to_u8 ( ) {
445
+ return Err ( RequestsError :: InvalidOrdering ) ;
431
446
}
432
- Some ( RequestPrefix :: Withdrawal ) => {
433
- requests. withdrawals = WithdrawalRequests :: < E > :: from_ssz_bytes ( & decoded_bytes)
447
+ }
448
+ prev_prefix = Some ( current_prefix) ;
449
+
450
+ match current_prefix {
451
+ RequestType :: Deposit => {
452
+ requests. deposits = DepositRequests :: < E > :: from_ssz_bytes ( request_bytes)
434
453
. map_err ( |e| {
435
- format ! ( "Failed to decode WithdrawalRequest from EL: {:?}" , e)
454
+ RequestsError :: DecodeError ( format ! (
455
+ "Failed to decode DepositRequest from EL: {:?}" ,
456
+ e
457
+ ) )
436
458
} ) ?;
437
459
}
438
- Some ( RequestPrefix :: Consolidation ) => {
460
+ RequestType :: Withdrawal => {
461
+ requests. withdrawals = WithdrawalRequests :: < E > :: from_ssz_bytes ( request_bytes)
462
+ . map_err ( |e| {
463
+ RequestsError :: DecodeError ( format ! (
464
+ "Failed to decode WithdrawalRequest from EL: {:?}" ,
465
+ e
466
+ ) )
467
+ } ) ?;
468
+ }
469
+ RequestType :: Consolidation => {
439
470
requests. consolidations =
440
- ConsolidationRequests :: < E > :: from_ssz_bytes ( & decoded_bytes) . map_err (
441
- |e| format ! ( "Failed to decode ConsolidationRequest from EL: {:?}" , e) ,
442
- ) ?;
471
+ ConsolidationRequests :: < E > :: from_ssz_bytes ( request_bytes) . map_err ( |e| {
472
+ RequestsError :: DecodeError ( format ! (
473
+ "Failed to decode ConsolidationRequest from EL: {:?}" ,
474
+ e
475
+ ) )
476
+ } ) ?;
443
477
}
444
- None => return Err ( "Empty requests string" . to_string ( ) ) ,
445
478
}
446
479
}
447
480
Ok ( requests)
@@ -510,7 +543,9 @@ impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
510
543
block_value : response. block_value ,
511
544
blobs_bundle : response. blobs_bundle . into ( ) ,
512
545
should_override_builder : response. should_override_builder ,
513
- requests : response. execution_requests . try_into ( ) ?,
546
+ requests : response. execution_requests . try_into ( ) . map_err ( |e| {
547
+ format ! ( "Failed to convert json to execution requests : {:?}" , e)
548
+ } ) ?,
514
549
} ) )
515
550
}
516
551
JsonGetPayloadResponse :: V5 ( response) => {
@@ -519,7 +554,9 @@ impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
519
554
block_value : response. block_value ,
520
555
blobs_bundle : response. blobs_bundle . into ( ) ,
521
556
should_override_builder : response. should_override_builder ,
522
- requests : response. execution_requests . try_into ( ) ?,
557
+ requests : response. execution_requests . try_into ( ) . map_err ( |e| {
558
+ format ! ( "Failed to convert json to execution requests {:?}" , e)
559
+ } ) ?,
523
560
} ) )
524
561
}
525
562
}
0 commit comments