@@ -267,7 +267,7 @@ pub struct JsonPayloadId {
267267 pub payload_id : u64 ,
268268}
269269
270- #[ derive( Debug , PartialEq , Serialize , Deserialize ) ]
270+ #[ derive( Debug , PartialEq , Default , Serialize , Deserialize ) ]
271271#[ serde( bound = "T: EthSpec" , rename_all = "camelCase" ) ]
272272pub struct JsonExecutionPayload < T : EthSpec > {
273273 pub parent_hash : Hash256 ,
@@ -285,13 +285,11 @@ pub struct JsonExecutionPayload<T: EthSpec> {
285285 pub gas_used : u64 ,
286286 #[ serde( with = "eth2_serde_utils::u64_hex_be" ) ]
287287 pub timestamp : u64 ,
288- // FIXME(paul): check serialization
289288 #[ serde( with = "ssz_types::serde_utils::hex_var_list" ) ]
290289 pub extra_data : VariableList < u8 , T :: MaxExtraDataBytes > ,
291290 pub base_fee_per_gas : Uint256 ,
292291 pub block_hash : Hash256 ,
293- // FIXME(paul): add transaction parsing.
294- #[ serde( default , skip_deserializing) ]
292+ #[ serde( with = "serde_transactions" ) ]
295293 pub transactions : VariableList < Transaction < T > , T :: MaxTransactionsPerPayload > ,
296294}
297295
@@ -357,7 +355,7 @@ pub struct JsonForkChoiceUpdatedRequest {
357355 pub finalized_block_hash : Hash256 ,
358356}
359357
360- // Serializes the `logs_bloom` field.
358+ /// Serializes the `logs_bloom` field of an `ExecutionPayload` .
361359pub mod serde_logs_bloom {
362360 use super :: * ;
363361 use eth2_serde_utils:: hex:: PrefixedHexVisitor ;
@@ -386,6 +384,81 @@ pub mod serde_logs_bloom {
386384 }
387385}
388386
387+ /// Serializes the `transactions` field of an `ExecutionPayload`.
388+ pub mod serde_transactions {
389+ use super :: * ;
390+ use eth2_serde_utils:: hex;
391+ use serde:: ser:: SerializeSeq ;
392+ use serde:: { de, Deserializer , Serializer } ;
393+ use std:: marker:: PhantomData ;
394+
395+ type Value < T , N > = VariableList < Transaction < T > , N > ;
396+
397+ #[ derive( Default ) ]
398+ pub struct ListOfBytesListVisitor < T , N > {
399+ _phantom_t : PhantomData < T > ,
400+ _phantom_n : PhantomData < N > ,
401+ }
402+
403+ impl < ' a , T : EthSpec , N : Unsigned > serde:: de:: Visitor < ' a > for ListOfBytesListVisitor < T , N > {
404+ type Value = Value < T , N > ;
405+
406+ fn expecting ( & self , formatter : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
407+ write ! ( formatter, "a list of 0x-prefixed byte lists" )
408+ }
409+
410+ fn visit_seq < A > ( self , mut seq : A ) -> Result < Self :: Value , A :: Error >
411+ where
412+ A : serde:: de:: SeqAccess < ' a > ,
413+ {
414+ let mut outer = VariableList :: default ( ) ;
415+
416+ while let Some ( val) = seq. next_element :: < String > ( ) ? {
417+ let inner_vec = hex:: decode ( & val) . map_err ( de:: Error :: custom) ?;
418+ let opaque_transaction = VariableList :: new ( inner_vec) . map_err ( |e| {
419+ serde:: de:: Error :: custom ( format ! ( "transaction too large: {:?}" , e) )
420+ } ) ?;
421+ let transaction = Transaction :: OpaqueTransaction ( opaque_transaction) ;
422+ outer. push ( transaction) . map_err ( |e| {
423+ serde:: de:: Error :: custom ( format ! ( "too many transactions: {:?}" , e) )
424+ } ) ?;
425+ }
426+
427+ Ok ( outer)
428+ }
429+ }
430+
431+ pub fn serialize < S , T : EthSpec , N : Unsigned > (
432+ value : & Value < T , N > ,
433+ serializer : S ,
434+ ) -> Result < S :: Ok , S :: Error >
435+ where
436+ S : Serializer ,
437+ {
438+ let mut seq = serializer. serialize_seq ( Some ( value. len ( ) ) ) ?;
439+ for transaction in value {
440+ // It's important to match on the inner values of the transaction. Serializing the
441+ // entire `Transaction` will result in appending the SSZ union prefix byte. The
442+ // execution node does not want that.
443+ let hex = match transaction {
444+ Transaction :: OpaqueTransaction ( val) => hex:: encode ( & val[ ..] ) ,
445+ } ;
446+ seq. serialize_element ( & hex) ?;
447+ }
448+ seq. end ( )
449+ }
450+
451+ pub fn deserialize < ' de , D , T : EthSpec , N : Unsigned > (
452+ deserializer : D ,
453+ ) -> Result < Value < T , N > , D :: Error >
454+ where
455+ D : Deserializer < ' de > ,
456+ {
457+ let visitor: ListOfBytesListVisitor < T , N > = <_ >:: default ( ) ;
458+ deserializer. deserialize_any ( visitor)
459+ }
460+ }
461+
389462#[ cfg( test) ]
390463mod test {
391464 use super :: * ;
@@ -443,6 +516,142 @@ mod test {
443516
444517 const LOGS_BLOOM_01 : & str = "0x01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" ;
445518
519+ fn encode_transactions < E : EthSpec > (
520+ transactions : VariableList < Transaction < E > , E :: MaxTransactionsPerPayload > ,
521+ ) -> Result < serde_json:: Value , serde_json:: Error > {
522+ let ep: JsonExecutionPayload < E > = JsonExecutionPayload {
523+ transactions,
524+ ..<_ >:: default ( )
525+ } ;
526+ let json = serde_json:: to_value ( & ep) ?;
527+ Ok ( json. get ( "transactions" ) . unwrap ( ) . clone ( ) )
528+ }
529+
530+ fn decode_transactions < E : EthSpec > (
531+ transactions : serde_json:: Value ,
532+ ) -> Result < VariableList < Transaction < E > , E :: MaxTransactionsPerPayload > , serde_json:: Error > {
533+ let json = json ! ( {
534+ "parentHash" : HASH_00 ,
535+ "coinbase" : ADDRESS_01 ,
536+ "stateRoot" : HASH_01 ,
537+ "receiptRoot" : HASH_00 ,
538+ "logsBloom" : LOGS_BLOOM_01 ,
539+ "random" : HASH_01 ,
540+ "blockNumber" : "0x0" ,
541+ "gasLimit" : "0x1" ,
542+ "gasUsed" : "0x2" ,
543+ "timestamp" : "0x2a" ,
544+ "extraData" : "0x" ,
545+ "baseFeePerGas" : "0x1" ,
546+ "blockHash" : HASH_01 ,
547+ "transactions" : transactions,
548+ } ) ;
549+ let ep: JsonExecutionPayload < E > = serde_json:: from_value ( json) ?;
550+ Ok ( ep. transactions )
551+ }
552+
553+ fn assert_transactions_serde < E : EthSpec > (
554+ name : & str ,
555+ as_obj : VariableList < Transaction < E > , E :: MaxTransactionsPerPayload > ,
556+ as_json : serde_json:: Value ,
557+ ) {
558+ assert_eq ! (
559+ encode_transactions( as_obj. clone( ) ) . unwrap( ) ,
560+ as_json,
561+ "encoding for {}" ,
562+ name
563+ ) ;
564+ assert_eq ! (
565+ decode_transactions( as_json) . unwrap( ) ,
566+ as_obj,
567+ "decoding for {}" ,
568+ name
569+ ) ;
570+ }
571+
572+ /// Example: if `spec == &[1, 1]`, then two one-byte transactions will be created.
573+ fn generate_opaque_transactions < E : EthSpec > (
574+ spec : & [ usize ] ,
575+ ) -> VariableList < Transaction < E > , E :: MaxTransactionsPerPayload > {
576+ let mut txs = VariableList :: default ( ) ;
577+
578+ for & num_bytes in spec {
579+ let mut tx = VariableList :: default ( ) ;
580+ for _ in 0 ..num_bytes {
581+ tx. push ( 0 ) . unwrap ( ) ;
582+ }
583+ txs. push ( Transaction :: OpaqueTransaction ( tx) ) . unwrap ( ) ;
584+ }
585+
586+ txs
587+ }
588+
589+ #[ test]
590+ fn transaction_serde ( ) {
591+ assert_transactions_serde :: < MainnetEthSpec > (
592+ "empty" ,
593+ generate_opaque_transactions ( & [ ] ) ,
594+ json ! ( [ ] ) ,
595+ ) ;
596+ assert_transactions_serde :: < MainnetEthSpec > (
597+ "one empty tx" ,
598+ generate_opaque_transactions ( & [ 0 ] ) ,
599+ json ! ( [ "0x" ] ) ,
600+ ) ;
601+ assert_transactions_serde :: < MainnetEthSpec > (
602+ "two empty txs" ,
603+ generate_opaque_transactions ( & [ 0 , 0 ] ) ,
604+ json ! ( [ "0x" , "0x" ] ) ,
605+ ) ;
606+ assert_transactions_serde :: < MainnetEthSpec > (
607+ "one one-byte tx" ,
608+ generate_opaque_transactions ( & [ 1 ] ) ,
609+ json ! ( [ "0x00" ] ) ,
610+ ) ;
611+ assert_transactions_serde :: < MainnetEthSpec > (
612+ "two one-byte txs" ,
613+ generate_opaque_transactions ( & [ 1 , 1 ] ) ,
614+ json ! ( [ "0x00" , "0x00" ] ) ,
615+ ) ;
616+ assert_transactions_serde :: < MainnetEthSpec > (
617+ "mixed bag" ,
618+ generate_opaque_transactions ( & [ 0 , 1 , 3 , 0 ] ) ,
619+ json ! ( [ "0x" , "0x00" , "0x000000" , "0x" ] ) ,
620+ ) ;
621+
622+ /*
623+ * Check for too many transactions
624+ */
625+
626+ let num_max_txs = <MainnetEthSpec as EthSpec >:: MaxTransactionsPerPayload :: to_usize ( ) ;
627+ let max_txs = ( 0 ..num_max_txs) . map ( |_| "0x00" ) . collect :: < Vec < _ > > ( ) ;
628+ let too_many_txs = ( 0 ..=num_max_txs) . map ( |_| "0x00" ) . collect :: < Vec < _ > > ( ) ;
629+
630+ decode_transactions :: < MainnetEthSpec > ( serde_json:: to_value ( max_txs) . unwrap ( ) ) . unwrap ( ) ;
631+ assert ! (
632+ decode_transactions:: <MainnetEthSpec >( serde_json:: to_value( too_many_txs) . unwrap( ) )
633+ . is_err( )
634+ ) ;
635+
636+ /*
637+ * Check for transaction too large
638+ */
639+
640+ use eth2_serde_utils:: hex;
641+
642+ let num_max_bytes = <MainnetEthSpec as EthSpec >:: MaxBytesPerOpaqueTransaction :: to_usize ( ) ;
643+ let max_bytes = ( 0 ..num_max_bytes) . map ( |_| 0_u8 ) . collect :: < Vec < _ > > ( ) ;
644+ let too_many_bytes = ( 0 ..=num_max_bytes) . map ( |_| 0_u8 ) . collect :: < Vec < _ > > ( ) ;
645+ decode_transactions :: < MainnetEthSpec > (
646+ serde_json:: to_value ( & [ hex:: encode ( & max_bytes) ] ) . unwrap ( ) ,
647+ )
648+ . unwrap ( ) ;
649+ assert ! ( decode_transactions:: <MainnetEthSpec >(
650+ serde_json:: to_value( & [ hex:: encode( & too_many_bytes) ] ) . unwrap( )
651+ )
652+ . is_err( ) ) ;
653+ }
654+
446655 #[ tokio:: test]
447656 async fn get_block_by_number_request ( ) {
448657 Tester :: new ( )
0 commit comments