feat: first query api

This commit is contained in:
Pravdyvy 2026-04-23 17:07:19 +03:00
parent 9880a46bdc
commit 9fc2e39c10
9 changed files with 114 additions and 0 deletions

1
Cargo.lock generated
View File

@ -3484,6 +3484,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"cbindgen", "cbindgen",
"indexer_service", "indexer_service",
"indexer_service_rpc",
"log", "log",
"sequencer_core", "sequencer_core",
"tokio", "tokio",

View File

@ -7,6 +7,7 @@ version = "0.1.0"
[dependencies] [dependencies]
indexer_service.workspace = true indexer_service.workspace = true
sequencer_core.workspace = true sequencer_core.workspace = true
indexer_service_rpc.workspace = true
url.workspace = true url.workspace = true
log = { workspace = true } log = { workspace = true }

View File

@ -7,6 +7,7 @@ typedef enum OperationStatus {
Ok = 0, Ok = 0,
NullPointer = 1, NullPointer = 1,
InitializationError = 2, InitializationError = 2,
ClientError = 3,
} OperationStatus; } OperationStatus;
typedef struct IndexerServiceFFI { typedef struct IndexerServiceFFI {
@ -28,6 +29,17 @@ typedef struct PointerResult_IndexerServiceFFI__OperationStatus {
typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult; typedef struct PointerResult_IndexerServiceFFI__OperationStatus InitializedIndexerServiceFFIResult;
/**
* Simple wrapper around a pointer to a value or an error.
*
* Pointer is not guaranteed. You should check the error field before
* dereferencing the pointer.
*/
typedef struct PointerResult_u64__OperationStatus {
uint64_t *value;
enum OperationStatus error;
} PointerResult_u64__OperationStatus;
/** /**
* Creates and starts an indexer based on the provided * Creates and starts an indexer based on the provided
* configuration file path. * configuration file path.
@ -72,6 +84,26 @@ enum OperationStatus stop_indexer(struct IndexerServiceFFI *indexer);
*/ */
void free_cstring(char *block); void free_cstring(char *block);
/**
* Stops and frees the resources associated with the given indexer service.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
*
* # Returns
*
* An `OperationStatus` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
* - The `IndexerServiceFFI` instance was created by this library
* - The pointer will not be used after this function returns
*/
struct PointerResult_u64__OperationStatus query_last_block(const struct IndexerServiceFFI *indexer);
bool is_ok(const enum OperationStatus *self); bool is_ok(const enum OperationStatus *self);
bool is_error(const enum OperationStatus *self); bool is_error(const enum OperationStatus *self);

View File

@ -3,4 +3,5 @@ pub use result::PointerResult;
pub mod client; pub mod client;
pub mod lifecycle; pub mod lifecycle;
pub mod memory; pub mod memory;
pub mod query;
pub mod result; pub mod result;

View File

@ -0,0 +1,41 @@
use indexer_service_rpc::RpcClient;
use crate::{IndexerServiceFFI, api::PointerResult, errors::OperationStatus};
/// Stops and frees the resources associated with the given indexer service.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
///
/// # Returns
///
/// An `OperationStatus` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
/// - The `IndexerServiceFFI` instance was created by this library
/// - The pointer will not be used after this function returns
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_last_block(
indexer: *const IndexerServiceFFI,
) -> PointerResult<u64, OperationStatus> {
if indexer.is_null() {
log::error!("Attempted to query a null indexer pointer. This is a bug. Aborting.");
return PointerResult::from_error(OperationStatus::NullPointer);
}
let indexer = unsafe { &*indexer };
let client = unsafe { indexer.client() };
let runtime = unsafe { indexer.runtime() };
runtime
.block_on(client.get_last_finalized_block_id())
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
PointerResult::from_value,
)
}

View File

@ -5,6 +5,7 @@ pub enum OperationStatus {
Ok = 0x0, Ok = 0x0,
NullPointer = 0x1, NullPointer = 0x1,
InitializationError = 0x2, InitializationError = 0x2,
ClientError = 0x3,
} }
impl OperationStatus { impl OperationStatus {

View File

@ -88,6 +88,22 @@ impl IndexerServiceFFI {
.expect("Indexer Client must be non-null pointer") .expect("Indexer Client must be non-null pointer")
} }
} }
/// Helper to get indexer runtime ref.
///
/// # Safety
///
/// The caller must ensure that:
/// - `self` is a valid object(contains valid pointers in all fields)
#[must_use]
pub const unsafe fn runtime(&self) -> &Runtime {
unsafe {
self.runtime
.cast::<Runtime>()
.as_ref()
.expect("Indexer Runtime must be non-null pointer")
}
}
} }
// Implement Drop to prevent memory leaks // Implement Drop to prevent memory leaks

View File

@ -268,6 +268,11 @@ impl BlockingTestContextFFI {
pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> { pub fn runtime_clone(&self) -> Arc<tokio::runtime::Runtime> {
Arc::<tokio::runtime::Runtime>::clone(&self.runtime) Arc::<tokio::runtime::Runtime>::clone(&self.runtime)
} }
#[must_use]
pub const fn indexer_ffi(&self) -> *const IndexerServiceFFI {
&(self.indexer_ffi)
}
} }
impl Drop for BlockingTestContextFFI { impl Drop for BlockingTestContextFFI {

View File

@ -5,6 +5,7 @@
)] )]
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use indexer_ffi::{IndexerServiceFFI, OperationStatus, api::PointerResult};
use indexer_service_rpc::RpcClient as _; use indexer_service_rpc::RpcClient as _;
use integration_tests::{ use integration_tests::{
TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id, TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id,
@ -17,6 +18,12 @@ use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcomma
/// Maximum time to wait for the indexer to catch up to the sequencer. /// Maximum time to wait for the indexer to catch up to the sequencer.
const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000; const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000;
unsafe extern "C" {
unsafe fn query_last_block(
indexer: *const IndexerServiceFFI,
) -> PointerResult<u64, OperationStatus>;
}
#[test] #[test]
fn indexer_test_run_ffi() -> Result<()> { fn indexer_test_run_ffi() -> Result<()> {
let blocking_ctx = BlockingTestContextFFI::new()?; let blocking_ctx = BlockingTestContextFFI::new()?;
@ -28,10 +35,19 @@ fn indexer_test_run_ffi() -> Result<()> {
}); });
let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?; let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?;
let last_block_indexer_ffi_res = unsafe { query_last_block(blocking_ctx.indexer_ffi()) };
assert!(last_block_indexer_ffi_res.error.is_ok());
let last_block_indexer_ffi = unsafe { *last_block_indexer_ffi_res.value };
info!("Last block on ind now is {last_block_indexer}"); info!("Last block on ind now is {last_block_indexer}");
info!("Last block on ind ffi now is {last_block_indexer_ffi}");
assert!(last_block_indexer > 1); assert!(last_block_indexer > 1);
assert!(last_block_indexer_ffi > 1);
assert_eq!(last_block_indexer, last_block_indexer_ffi);
Ok(()) Ok(())
} }