@@ -11,7 +11,9 @@ use tracing::{debug, info, warn};
1111use crate :: {
1212 rpc:: { RpcApiContext , RpcHandler } ,
1313 types:: {
14- fork_choice:: { ForkChoiceResponse , ForkChoiceState , PayloadAttributesV3 } ,
14+ fork_choice:: {
15+ ForkChoiceResponse , ForkChoiceState , PayloadAttributesV3 , PayloadAttributesV4 ,
16+ } ,
1517 payload:: PayloadStatus ,
1618 } ,
1719 utils:: RpcErr ,
@@ -128,6 +130,46 @@ impl RpcHandler for ForkChoiceUpdatedV3 {
128130 }
129131}
130132
133+ #[ derive( Debug ) ]
134+ pub struct ForkChoiceUpdatedV4 {
135+ pub fork_choice_state : ForkChoiceState ,
136+ pub payload_attributes : Option < PayloadAttributesV4 > ,
137+ }
138+
139+ impl From < ForkChoiceUpdatedV4 > for RpcRequest {
140+ fn from ( val : ForkChoiceUpdatedV4 ) -> Self {
141+ RpcRequest {
142+ method : "engine_forkchoiceUpdatedV4" . to_string ( ) ,
143+ params : Some ( vec ! [
144+ serde_json:: json!( val. fork_choice_state) ,
145+ serde_json:: json!( val. payload_attributes) ,
146+ ] ) ,
147+ ..Default :: default ( )
148+ }
149+ }
150+ }
151+
152+ impl RpcHandler for ForkChoiceUpdatedV4 {
153+ fn parse ( params : & Option < Vec < Value > > ) -> Result < Self , RpcErr > {
154+ let ( fork_choice_state, payload_attributes) = parse_v4 ( params) ?;
155+ Ok ( ForkChoiceUpdatedV4 {
156+ fork_choice_state,
157+ payload_attributes,
158+ } )
159+ }
160+
161+ async fn handle ( & self , context : RpcApiContext ) -> Result < Value , RpcErr > {
162+ let ( head_block_opt, mut response) =
163+ handle_forkchoice ( & self . fork_choice_state , context. clone ( ) , 4 ) . await ?;
164+ if let ( Some ( head_block) , Some ( attributes) ) = ( head_block_opt, & self . payload_attributes ) {
165+ validate_attributes_v4 ( attributes, & head_block, & context) ?;
166+ let payload_id = build_payload_v4 ( attributes, context, & self . fork_choice_state ) . await ?;
167+ response. set_id ( payload_id) ;
168+ }
169+ serde_json:: to_value ( response) . map_err ( |error| RpcErr :: Internal ( error. to_string ( ) ) )
170+ }
171+ }
172+
131173fn parse (
132174 params : & Option < Vec < Value > > ,
133175 is_v3 : bool ,
@@ -386,6 +428,7 @@ async fn build_payload(
386428 random : attributes. prev_randao ,
387429 withdrawals : attributes. withdrawals . clone ( ) ,
388430 beacon_root : attributes. parent_beacon_block_root ,
431+ slot_number : None ,
389432 version,
390433 elasticity_multiplier : ELASTICITY_MULTIPLIER ,
391434 gas_ceil : context. gas_ceil ,
@@ -411,3 +454,104 @@ async fn build_payload(
411454 . await ;
412455 Ok ( payload_id)
413456}
457+
458+ fn parse_v4 (
459+ params : & Option < Vec < Value > > ,
460+ ) -> Result < ( ForkChoiceState , Option < PayloadAttributesV4 > ) , RpcErr > {
461+ let params = params
462+ . as_ref ( )
463+ . ok_or ( RpcErr :: BadParams ( "No params provided" . to_owned ( ) ) ) ?;
464+
465+ if params. len ( ) != 2 && params. len ( ) != 1 {
466+ return Err ( RpcErr :: BadParams ( "Expected 2 or 1 params" . to_owned ( ) ) ) ;
467+ }
468+
469+ let forkchoice_state: ForkChoiceState = serde_json:: from_value ( params[ 0 ] . clone ( ) ) ?;
470+ let mut payload_attributes: Option < PayloadAttributesV4 > = None ;
471+ if params. len ( ) == 2 {
472+ payload_attributes =
473+ match serde_json:: from_value :: < Option < PayloadAttributesV4 > > ( params[ 1 ] . clone ( ) ) {
474+ Ok ( attributes) => attributes,
475+ Err ( error) => {
476+ warn ! ( "Could not parse payload attributes {}" , error) ;
477+ None
478+ }
479+ } ;
480+ }
481+ Ok ( ( forkchoice_state, payload_attributes) )
482+ }
483+
484+ fn validate_attributes_v4 (
485+ attributes : & PayloadAttributesV4 ,
486+ head_block : & BlockHeader ,
487+ context : & RpcApiContext ,
488+ ) -> Result < ( ) , RpcErr > {
489+ // Similar validation to V3
490+ let chain_config = context. storage . get_chain_config ( ) ;
491+ if !chain_config. is_amsterdam_activated ( attributes. timestamp ) {
492+ return Err ( RpcErr :: InvalidPayloadAttributes (
493+ "V4 payload attributes used for pre-Amsterdam timestamp" . to_string ( ) ,
494+ ) ) ;
495+ }
496+ if attributes. withdrawals . is_none ( ) {
497+ return Err ( RpcErr :: InvalidPayloadAttributes (
498+ "V4 payload attributes missing withdrawals" . to_string ( ) ,
499+ ) ) ;
500+ }
501+ if attributes. parent_beacon_block_root . is_none ( ) {
502+ return Err ( RpcErr :: InvalidPayloadAttributes (
503+ "V4 payload attributes missing parent_beacon_block_root" . to_string ( ) ,
504+ ) ) ;
505+ }
506+ validate_timestamp_v4 ( attributes, head_block)
507+ }
508+
509+ fn validate_timestamp_v4 (
510+ attributes : & PayloadAttributesV4 ,
511+ head_block : & BlockHeader ,
512+ ) -> Result < ( ) , RpcErr > {
513+ if attributes. timestamp <= head_block. timestamp {
514+ return Err ( RpcErr :: InvalidPayloadAttributes (
515+ "invalid timestamp" . to_string ( ) ,
516+ ) ) ;
517+ }
518+ Ok ( ( ) )
519+ }
520+
521+ async fn build_payload_v4 (
522+ attributes : & PayloadAttributesV4 ,
523+ context : RpcApiContext ,
524+ fork_choice_state : & ForkChoiceState ,
525+ ) -> Result < u64 , RpcErr > {
526+ let args = BuildPayloadArgs {
527+ parent : fork_choice_state. head_block_hash ,
528+ timestamp : attributes. timestamp ,
529+ fee_recipient : attributes. suggested_fee_recipient ,
530+ random : attributes. prev_randao ,
531+ withdrawals : attributes. withdrawals . clone ( ) ,
532+ beacon_root : attributes. parent_beacon_block_root ,
533+ slot_number : Some ( attributes. slot_number ) ,
534+ version : 4 ,
535+ elasticity_multiplier : ELASTICITY_MULTIPLIER ,
536+ gas_ceil : context. gas_ceil ,
537+ } ;
538+ let payload_id = args
539+ . id ( )
540+ . map_err ( |error| RpcErr :: Internal ( error. to_string ( ) ) ) ?;
541+
542+ info ! (
543+ id = payload_id,
544+ slot = attributes. slot_number,
545+ "Fork choice updated V4 includes payload attributes. Creating a new payload"
546+ ) ;
547+ let payload = match create_payload ( & args, & context. storage , context. node_data . extra_data ) {
548+ Ok ( payload) => payload,
549+ Err ( ChainError :: EvmError ( error) ) => return Err ( error. into ( ) ) ,
550+ Err ( error) => return Err ( RpcErr :: Internal ( error. to_string ( ) ) ) ,
551+ } ;
552+ context
553+ . blockchain
554+ . initiate_payload_build ( payload, payload_id)
555+ . await ;
556+ Ok ( payload_id)
557+ }
0 commit comments