diff --git a/Cargo.lock b/Cargo.lock index 3d46ad65..0d997442 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3485,7 +3485,9 @@ dependencies = [ "cbindgen", "indexer_service", "log", + "sequencer_core", "tokio", + "url", ] [[package]] diff --git a/indexer_ffi/Cargo.toml b/indexer_ffi/Cargo.toml index b55230c6..ed3de7ae 100644 --- a/indexer_ffi/Cargo.toml +++ b/indexer_ffi/Cargo.toml @@ -6,6 +6,9 @@ version = "0.1.0" [dependencies] indexer_service.workspace = true +sequencer_core.workspace = true + +url.workspace = true log = { workspace = true } tokio = { features = ["rt-multi-thread"], workspace = true } diff --git a/indexer_ffi/indexer_ffi.h b/indexer_ffi/indexer_ffi.h index 7c7d9a4d..d764d116 100644 --- a/indexer_ffi/indexer_ffi.h +++ b/indexer_ffi/indexer_ffi.h @@ -12,6 +12,7 @@ typedef enum OperationStatus { typedef struct IndexerServiceFFI { void *indexer_handle; void *runtime; + void *indexer_client; } IndexerServiceFFI; /** diff --git a/indexer_ffi/src/api/client.rs b/indexer_ffi/src/api/client.rs new file mode 100644 index 00000000..825a57de --- /dev/null +++ b/indexer_ffi/src/api/client.rs @@ -0,0 +1,36 @@ +use std::net::SocketAddr; + +use url::Url; + +use crate::OperationStatus; + +#[derive(Debug, Clone, Copy)] +pub enum UrlProtocol { + Http, + Ws, +} + +impl std::fmt::Display for UrlProtocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Http => write!(f, "http"), + Self::Ws => write!(f, "ws"), + } + } +} + +pub(crate) fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result { + // Convert 0.0.0.0 to 127.0.0.1 for client connections + // When binding to port 0, the server binds to 0.0.0.0: + // but clients need to connect to 127.0.0.1: to work reliably + let url_string = if addr.ip().is_unspecified() { + format!("{protocol}://127.0.0.1:{}", addr.port()) + } else { + format!("{protocol}://{addr}") + }; + + url_string.parse().map_err(|e| { + log::error!("Could not parse indexer url: {e}"); + OperationStatus::InitializationError + }) +} diff --git a/indexer_ffi/src/api/lifecycle.rs b/indexer_ffi/src/api/lifecycle.rs index 735efd4d..087f7803 100644 --- a/indexer_ffi/src/api/lifecycle.rs +++ b/indexer_ffi/src/api/lifecycle.rs @@ -1,8 +1,16 @@ use std::{ffi::c_char, path::PathBuf}; +use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait}; use tokio::runtime::Runtime; -use crate::{IndexerServiceFFI, api::PointerResult, errors::OperationStatus}; +use crate::{ + IndexerServiceFFI, + api::{ + PointerResult, + client::{UrlProtocol, addr_to_url}, + }, + errors::OperationStatus, +}; pub type InitializedIndexerServiceFFIResult = PointerResult; @@ -67,7 +75,13 @@ fn setup_indexer( OperationStatus::InitializationError })?; - Ok(IndexerServiceFFI::new(indexer_handle, rt)) + let indexer_url = addr_to_url(UrlProtocol::Ws, indexer_handle.addr())?; + let indexer_client = rt.block_on(IndexerClient::new(&indexer_url)).map_err(|e| { + log::error!("Could not start indexer client: {e}"); + OperationStatus::InitializationError + })?; + + Ok(IndexerServiceFFI::new(indexer_handle, rt, indexer_client)) } /// Stops and frees the resources associated with the given indexer service. diff --git a/indexer_ffi/src/api/mod.rs b/indexer_ffi/src/api/mod.rs index e84a3913..a20cb6a5 100644 --- a/indexer_ffi/src/api/mod.rs +++ b/indexer_ffi/src/api/mod.rs @@ -1,5 +1,6 @@ pub use result::PointerResult; +pub mod client; pub mod lifecycle; pub mod memory; pub mod result; diff --git a/indexer_ffi/src/indexer.rs b/indexer_ffi/src/indexer.rs index a3991388..be01f7f9 100644 --- a/indexer_ffi/src/indexer.rs +++ b/indexer_ffi/src/indexer.rs @@ -1,20 +1,27 @@ use std::{ffi::c_void, net::SocketAddr}; use indexer_service::IndexerHandle; +use sequencer_core::indexer_client::IndexerClient; use tokio::runtime::Runtime; #[repr(C)] pub struct IndexerServiceFFI { indexer_handle: *mut c_void, runtime: *mut c_void, + indexer_client: *mut c_void, } impl IndexerServiceFFI { - pub fn new(indexer_handle: indexer_service::IndexerHandle, runtime: Runtime) -> Self { + pub fn new( + indexer_handle: indexer_service::IndexerHandle, + runtime: Runtime, + indexer_client: IndexerClient, + ) -> Self { Self { // Box the complex types and convert to opaque pointers indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::(), runtime: Box::into_raw(Box::new(runtime)).cast::(), + indexer_client: Box::into_raw(Box::new(indexer_client)).cast::(), } } @@ -25,10 +32,11 @@ impl IndexerServiceFFI { /// The caller must ensure that: /// - `self` is a valid object(contains valid pointers in all fields) #[must_use] - pub unsafe fn into_parts(self) -> (Box, Box) { + pub unsafe fn into_parts(self) -> (Box, Box, Box) { let indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::()) }; let runtime = unsafe { Box::from_raw(self.runtime.cast::()) }; - (indexer_handle, runtime) + let indexer_client = unsafe { Box::from_raw(self.indexer_client.cast::()) }; + (indexer_handle, runtime, indexer_client) } /// Helper to get indexer handle addr. @@ -49,7 +57,7 @@ impl IndexerServiceFFI { indexer_handle.addr() } - /// Helper to get indexer handle addr. + /// Helper to get indexer handle ref. /// /// # Safety /// @@ -64,6 +72,22 @@ impl IndexerServiceFFI { .expect("Indexer Handle must be non-null pointer") } } + + /// Helper to get indexer client ref. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - `self` is a valid object(contains valid pointers in all fields) + #[must_use] + pub const unsafe fn client(&self) -> &IndexerClient { + unsafe { + self.indexer_client + .cast::() + .as_ref() + .expect("Indexer Client must be non-null pointer") + } + } } // Implement Drop to prevent memory leaks