@@ -39,6 +39,16 @@ use crate::ipc::{engine_ipc::EngineIPC, ethereum_ipc::EthereumIPC};
3939use crate :: json_structures:: ExecutionBlock ;
4040use crate :: rpc:: { engine_rpc:: EngineRpc , ethereum_rpc:: EthereumRPC } ;
4141
42+ /// A subscription-capable transport endpoint for the execution layer.
43+ ///
44+ /// Exposed so that callers (e.g. startup replay, RPC sync) can establish
45+ /// auxiliary subscriptions without Engine needing to know about those concerns.
46+ #[ derive( Clone , Debug ) ]
47+ pub enum SubscriptionEndpoint {
48+ Ipc { socket_path : String } ,
49+ Ws { url : Url } ,
50+ }
51+
4252#[ cfg_attr( any( test, feature = "mocks" ) , mockall:: automock) ]
4353#[ async_trait]
4454pub trait EngineAPI : Send + Sync {
@@ -105,7 +115,10 @@ impl Engine {
105115 pub async fn new_ipc ( execution_socket : & str , eth_socket : & str ) -> eyre:: Result < Self > {
106116 let api = Box :: new ( EngineIPC :: new ( execution_socket) . await ?) ;
107117 let eth = Box :: new ( EthereumIPC :: new ( eth_socket) . await ?) ;
108- Ok ( Self :: new ( api, eth) )
118+ let sub_endpoint = SubscriptionEndpoint :: Ipc {
119+ socket_path : eth_socket. to_owned ( ) ,
120+ } ;
121+ Ok ( Self ( Arc :: new ( Inner :: new ( api, eth, Some ( sub_endpoint) ) ) ) )
109122 }
110123
111124 /// Create a new engine using RPC.
@@ -115,6 +128,7 @@ impl Engine {
115128 pub async fn new_rpc (
116129 execution_endpoint : Url ,
117130 eth_endpoint : Url ,
131+ ws_endpoint : Option < Url > ,
118132 execution_jwt : & str ,
119133 ) -> eyre:: Result < Self > {
120134 let api = Box :: new ( EngineRpc :: new (
@@ -126,12 +140,13 @@ impl Engine {
126140 // Probe the RPC server to confirm it is reachable.
127141 eth. check_connectivity ( ) . await ?;
128142
129- Ok ( Self :: new ( api, eth) )
143+ let sub_endpoint = ws_endpoint. map ( |url| SubscriptionEndpoint :: Ws { url } ) ;
144+ Ok ( Self ( Arc :: new ( Inner :: new ( api, eth, sub_endpoint) ) ) )
130145 }
131146
132147 /// Create a new engine with custom API implementations.
133148 pub fn new ( api : Box < dyn EngineAPI > , eth : Box < dyn EthereumAPI > ) -> Self {
134- Self ( Arc :: new ( Inner :: new ( api, eth) ) )
149+ Self ( Arc :: new ( Inner :: new ( api, eth, None ) ) )
135150 }
136151
137152 /// Set the function that determines whether Osaka is active at a given timestamp.
@@ -209,6 +224,13 @@ impl Engine {
209224 . expect ( "Clock is before UNIX epoch!" )
210225 . as_secs ( )
211226 }
227+
228+ /// Returns the subscription-capable endpoint for the execution layer,
229+ /// if one was configured. `None` for test/mock engines or RPC without
230+ /// `--execution-ws-endpoint`.
231+ pub fn subscription_endpoint ( & self ) -> Option < & SubscriptionEndpoint > {
232+ self . 0 . subscription_endpoint . as_ref ( )
233+ }
212234}
213235
214236impl Deref for Engine {
@@ -224,18 +246,24 @@ pub struct Inner {
224246 pub api : Box < dyn EngineAPI > ,
225247 /// Client for Ethereum API.
226248 pub eth : Box < dyn EthereumAPI > ,
249+ /// Subscription-capable endpoint for the execution layer.
250+ subscription_endpoint : Option < SubscriptionEndpoint > ,
227251 /// Optional function to check if Osaka is active at a given timestamp.
228252 /// Set after construction via [`Engine::set_is_osaka_active`].
229253 /// When `None`, defaults to `false` (use V4).
230254 is_osaka_active : OnceLock < IsOsakaActiveFn > ,
231255}
232256
233257impl Inner {
234- /// Create a new engine with custom API implementations.
235- fn new ( api : Box < dyn EngineAPI > , eth : Box < dyn EthereumAPI > ) -> Self {
258+ fn new (
259+ api : Box < dyn EngineAPI > ,
260+ eth : Box < dyn EthereumAPI > ,
261+ subscription_endpoint : Option < SubscriptionEndpoint > ,
262+ ) -> Self {
236263 Self {
237264 api,
238265 eth,
266+ subscription_endpoint,
239267 is_osaka_active : OnceLock :: new ( ) ,
240268 }
241269 }
0 commit comments