From 3432c77ef4a9470c85bf1c99ef96669424a7031e Mon Sep 17 00:00:00 2001 From: Kemperino <33121795+Kemperino@users.noreply.github.com> Date: Sat, 23 May 2026 15:12:09 +0200 Subject: [PATCH] Add engine exchange capabilities support --- crates/rollup-boost/src/client/rpc.rs | 21 ++++++++ crates/rollup-boost/src/server.rs | 78 +++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/crates/rollup-boost/src/client/rpc.rs b/crates/rollup-boost/src/client/rpc.rs index b2f97160..48d77a17 100644 --- a/crates/rollup-boost/src/client/rpc.rs +++ b/crates/rollup-boost/src/client/rpc.rs @@ -170,6 +170,27 @@ impl RpcClient { }) } + #[instrument( + skip_all, + err, + fields( + otel.kind = ?SpanKind::Client, + target = self.payload_source.to_string(), + url = %self.auth_rpc, + ) + )] + pub async fn exchange_capabilities( + &self, + capabilities: Vec, + ) -> ClientResult> { + info!("Sending exchange_capabilities to {}", self.payload_source); + Ok(self + .auth_client + .exchange_capabilities(capabilities) + .await + .set_code()?) + } + #[instrument( skip_all, err, diff --git a/crates/rollup-boost/src/server.rs b/crates/rollup-boost/src/server.rs index f4ba4430..a058a406 100644 --- a/crates/rollup-boost/src/server.rs +++ b/crates/rollup-boost/src/server.rs @@ -486,6 +486,9 @@ pub trait FlashblocksEngineApi { #[rpc(server, client)] pub trait EngineApi { + #[method(name = "engine_exchangeCapabilities")] + async fn exchange_capabilities(&self, capabilities: Vec) -> RpcResult>; + #[method(name = "engine_forkchoiceUpdatedV3")] async fn fork_choice_updated_v3( &self, @@ -528,6 +531,17 @@ pub trait EngineApi { #[async_trait] impl EngineApiServer for RollupBoostServer { + #[instrument( + skip_all, + err, + fields( + otel.kind = ?SpanKind::Server, + ) + )] + async fn exchange_capabilities(&self, capabilities: Vec) -> RpcResult> { + Ok(self.l2_client.exchange_capabilities(capabilities).await?) + } + #[instrument( skip_all, err, @@ -802,9 +816,11 @@ pub mod tests { #[derive(Debug, Clone)] pub struct MockEngineServer { fcu_requests: Arc)>>>, + pub exchange_capabilities_requests: Arc>>>, pub get_payload_requests: Arc>>, new_payload_requests: Arc, B256)>>>, fcu_response: RpcResult, + exchange_capabilities_response: RpcResult>, get_payload_responses: Vec>, new_payload_response: RpcResult, @@ -815,9 +831,18 @@ pub mod tests { pub fn new() -> Self { Self { fcu_requests: Arc::new(Mutex::new(vec![])), + exchange_capabilities_requests: Arc::new(Mutex::new(vec![])), get_payload_requests: Arc::new(Mutex::new(vec![])), new_payload_requests: Arc::new(Mutex::new(vec![])), fcu_response: Ok(ForkchoiceUpdated::new(PayloadStatus::from_status(PayloadStatusEnum::Valid))), + exchange_capabilities_response: Ok(vec![ + "engine_forkchoiceUpdatedV3".to_string(), + "engine_getPayloadV3".to_string(), + "engine_newPayloadV3".to_string(), + "engine_getPayloadV4".to_string(), + "engine_newPayloadV4".to_string(), + "engine_exchangeCapabilities".to_string(), + ]), get_payload_responses: vec![Ok(OpExecutionPayloadEnvelopeV3{ execution_payload: ExecutionPayloadV3 { payload_inner: ExecutionPayloadV2 { @@ -989,6 +1014,48 @@ pub mod tests { } } + #[tokio::test] + async fn engine_exchange_capabilities_success() { + let test_harness = TestHarness::new(None, None).await; + + let capabilities = vec![ + "engine_forkchoiceUpdatedV3".to_string(), + "engine_getPayloadV3".to_string(), + "engine_newPayloadV3".to_string(), + "engine_exchangeCapabilities".to_string(), + ]; + let exchange_capabilities_response = test_harness + .rpc_client + .exchange_capabilities(capabilities.clone()) + .await; + assert!(exchange_capabilities_response.is_ok()); + assert_eq!( + exchange_capabilities_response.unwrap(), + test_harness + .l2_mock + .exchange_capabilities_response + .clone() + .unwrap() + ); + assert_eq!( + test_harness + .l2_mock + .exchange_capabilities_requests + .lock() + .as_slice(), + &[capabilities] + ); + assert!( + test_harness + .builder_mock + .exchange_capabilities_requests + .lock() + .is_empty() + ); + + test_harness.cleanup().await; + } + #[tokio::test] async fn engine_success() { let test_harness = TestHarness::new(None, None).await; @@ -1119,6 +1186,17 @@ pub mod tests { let mut module: RpcModule<()> = RpcModule::new(()); + module + .register_method("engine_exchangeCapabilities", move |params, _, _| { + let params: (Vec,) = params.parse()?; + let mut exchange_capabilities_requests = + mock_engine_server.exchange_capabilities_requests.lock(); + exchange_capabilities_requests.push(params.0); + + mock_engine_server.exchange_capabilities_response.clone() + }) + .unwrap(); + module .register_method("engine_forkchoiceUpdatedV3", move |params, _, _| { let params: (ForkchoiceState, Option) = params.parse()?;