fix: correct free

This commit is contained in:
Pravdyvy 2026-04-29 13:55:44 +03:00
parent 1338dec951
commit 113a68c22c
8 changed files with 1051 additions and 163 deletions

14
Cargo.lock generated
View File

@ -629,9 +629,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "astral-tokio-tar"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c23f3af104b40a3430ccb90ed5f7bd877a8dc5c26fc92fde51a22b40890dcf9"
checksum = "4ce73b17c62717c4b6a9af10b43e87c578b0cac27e00666d48304d3b7d2c0693"
dependencies = [
"filetime",
"futures-core",
@ -2108,7 +2108,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
"windows-sys 0.59.0",
]
[[package]]
@ -2409,7 +2409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@ -7090,7 +7090,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@ -8023,7 +8023,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]
@ -9306,7 +9306,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
"windows-sys 0.52.0",
]
[[package]]

View File

@ -167,11 +167,15 @@ typedef struct FfiAccount {
/**
* Pointer to account data bytes.
*/
const uint8_t *data;
uint8_t *data;
/**
* Length of account data.
*/
uintptr_t data_len;
/**
* Capacity of account data.
*/
uintptr_t data_cap;
/**
* Nonce as little-endian [u8; 16].
*/
@ -302,6 +306,66 @@ typedef struct PointerResult_FfiBlockOpt__OperationStatus {
enum OperationStatus error;
} PointerResult_FfiBlockOpt__OperationStatus;
/**
* 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_FfiAccount__OperationStatus {
struct FfiAccount *value;
enum OperationStatus error;
} PointerResult_FfiAccount__OperationStatus;
typedef struct FfiOption_FfiTransaction {
struct FfiTransaction *value;
bool is_some;
} FfiOption_FfiTransaction;
/**
* 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_FfiOption_FfiTransaction_____OperationStatus {
struct FfiOption_FfiTransaction *value;
enum OperationStatus error;
} PointerResult_FfiOption_FfiTransaction_____OperationStatus;
typedef struct FfiVec_FfiBlock {
struct FfiBlock *entries;
uintptr_t len;
uintptr_t capacity;
} FfiVec_FfiBlock;
/**
* 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_FfiVec_FfiBlock_____OperationStatus {
struct FfiVec_FfiBlock *value;
enum OperationStatus error;
} PointerResult_FfiVec_FfiBlock_____OperationStatus;
typedef struct FfiOption_u64 {
uint64_t *value;
bool is_some;
} FfiOption_u64;
/**
* 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_FfiVec_FfiTransaction_____OperationStatus {
struct FfiVec_FfiTransaction *value;
enum OperationStatus error;
} PointerResult_FfiVec_FfiTransaction_____OperationStatus;
/**
* Creates and starts an indexer based on the provided
* configuration file path.
@ -347,46 +411,275 @@ enum OperationStatus stop_indexer(struct IndexerServiceFFI *indexer);
void free_cstring(char *block);
/**
* Stops and frees the resources associated with the given indexer service.
* Query the last block id from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
*
* # Returns
*
* An `OperationStatus` indicating success or failure.
* A `PointerResult<u64, 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);
/**
* Stops and frees the resources associated with the given indexer service.
* Query the block by id from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `block_id`: `u64` number of block id
*
* # Returns
*
* An `OperationStatus` indicating success or failure.
* A `PointerResult<FfiBlockOpt, 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_FfiBlockOpt__OperationStatus query_block(const struct IndexerServiceFFI *indexer,
FfiBlockId block_id);
/**
* Query the block by id from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `hash`: `FfiHashType` - hash of block
*
* # Returns
*
* A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
*/
struct PointerResult_FfiBlockOpt__OperationStatus query_block_by_hash(const struct IndexerServiceFFI *indexer,
FfiHashType hash);
/**
* Query the account by id from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `account_id`: `FfiAccountId` - id of queried account
*
* # Returns
*
* A `PointerResult<FfiAccount, OperationStatus>` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
*/
struct PointerResult_FfiAccount__OperationStatus query_account(const struct IndexerServiceFFI *indexer,
FfiAccountId account_id);
/**
* Query the trasnaction by hash from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `hash`: `FfiHashType` - hash of transaction
*
* # Returns
*
* A `PointerResult<FfiOption<FfiTransaction>, OperationStatus>` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
*/
struct PointerResult_FfiOption_FfiTransaction_____OperationStatus query_transaction(const struct IndexerServiceFFI *indexer,
FfiHashType hash);
/**
* Query the blocks by block range from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `before`: `FfiOption<u64>` - end block of query
* - `limit`: `u64` - number of blocks to query before `before`
*
* # Returns
*
* A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
*/
struct PointerResult_FfiVec_FfiBlock_____OperationStatus query_block_vec(const struct IndexerServiceFFI *indexer,
struct FfiOption_u64 before,
uint64_t limit);
/**
* Query the transactions range by account id from indexer.
*
* # Arguments
*
* - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
* - `account_id`: `FfiAccountId` - id of queried account
* - `offset`: `u64` - first tx id of query
* - `limit`: `u64` - number of tx ids to query after `offset`
*
* # Returns
*
* A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
*
* # Safety
*
* The caller must ensure that:
* - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
*/
struct PointerResult_FfiVec_FfiTransaction_____OperationStatus query_transactions_by_account(const struct IndexerServiceFFI *indexer,
FfiAccountId account_id,
uint64_t offset,
uint64_t limit);
/**
* Frees the resources associated with the given ffi account.
*
* # Arguments
*
* - `val`: An instance of `FfiAccount`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiAccount`.
*/
void free_ffi_account(struct FfiAccount val);
/**
* Frees the resources associated with the given ffi block.
*
* # Arguments
*
* - `val`: An instance of `FfiBlock`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiBlock`.
*/
void free_ffi_block(struct FfiBlock val);
/**
* Frees the resources associated with the given ffi block option.
*
* # Arguments
*
* - `val`: An instance of `FfiBlockOpt`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiBlockOpt`.
*/
void free_ffi_block_opt(FfiBlockOpt val);
/**
* Frees the resources associated with the given ffi block vector.
*
* # Arguments
*
* - `val`: An instance of `FfiVec<FfiBlock>`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiVec<FfiBlock>`.
*/
void free_ffi_block_vec(struct FfiVec_FfiBlock val);
/**
* Frees the resources associated with the given ffi transaction.
*
* # Arguments
*
* - `val`: An instance of `FfiTransaction`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiTransaction`.
*/
void free_ffi_transaction(struct FfiTransaction val);
/**
* Frees the resources associated with the given ffi transaction option.
*
* # Arguments
*
* - `val`: An instance of `FfiOption<FfiTransaction>`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiOption<FfiTransaction>`.
*/
void free_ffi_transaction_opt(struct FfiOption_FfiTransaction val);
/**
* Frees the resources associated with the given vector of ffi transactions.
*
* # Arguments
*
* - `val`: An instance of `FfiVec<FfiTransaction>`.
*
* # Returns
*
* void.
*
* # Safety
*
* The caller must ensure that:
* - `val` is a valid instance of `FfiVec<FfiTransaction>`.
*/
void free_ffi_transaction_vec(struct FfiVec_FfiTransaction val);
bool is_ok(const enum OperationStatus *self);
bool is_error(const enum OperationStatus *self);

View File

@ -1,30 +1,34 @@
use indexer_service_protocol::{AccountId, HashType};
use indexer_service_rpc::RpcClient as _;
use crate::{
IndexerServiceFFI,
api::{
PointerResult,
types::{FfiBlockId, block::FfiBlockOpt},
types::{
FfiAccountId, FfiBlockId, FfiHashType, FfiOption, FfiVec,
account::FfiAccount,
block::{FfiBlock, FfiBlockOpt},
transaction::FfiTransaction,
},
},
errors::OperationStatus,
};
/// Stops and frees the resources associated with the given indexer service.
/// Query the last block id from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
///
/// # Returns
///
/// An `OperationStatus` indicating success or failure.
/// A `PointerResult<u64, 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,
@ -47,22 +51,21 @@ pub unsafe extern "C" fn query_last_block(
)
}
/// Stops and frees the resources associated with the given indexer service.
/// Query the block by id from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `block_id`: `u64` number of block id
///
/// # Returns
///
/// An `OperationStatus` indicating success or failure.
/// A `PointerResult<FfiBlockOpt, 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_block(
indexer: *const IndexerServiceFFI,
@ -91,3 +94,241 @@ pub unsafe extern "C" fn query_block(
},
)
}
/// Query the block by id from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `hash`: `FfiHashType` - hash of block
///
/// # Returns
///
/// A `PointerResult<FfiBlockOpt, OperationStatus>` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_block_by_hash(
indexer: *const IndexerServiceFFI,
hash: FfiHashType,
) -> PointerResult<FfiBlockOpt, 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_block_by_hash(HashType(hash.data)))
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
|block_opt| {
let block_ffi = block_opt.map_or_else(FfiBlockOpt::from_none, |block| {
FfiBlockOpt::from_value(block.into())
});
PointerResult::from_value(block_ffi)
},
)
}
/// Query the account by id from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `account_id`: `FfiAccountId` - id of queried account
///
/// # Returns
///
/// A `PointerResult<FfiAccount, OperationStatus>` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_account(
indexer: *const IndexerServiceFFI,
account_id: FfiAccountId,
) -> PointerResult<FfiAccount, 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_account(AccountId {
value: account_id.data,
}))
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
|acc| {
let acc_nssa: nssa::Account =
acc.try_into().expect("Source is in blocks, must fit");
PointerResult::from_value(acc_nssa.into())
},
)
}
/// Query the trasnaction by hash from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `hash`: `FfiHashType` - hash of transaction
///
/// # Returns
///
/// A `PointerResult<FfiOption<FfiTransaction>, OperationStatus>` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_transaction(
indexer: *const IndexerServiceFFI,
hash: FfiHashType,
) -> PointerResult<FfiOption<FfiTransaction>, 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_transaction(HashType(hash.data)))
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
|tx_opt| {
let tx_ffi = tx_opt.map_or_else(FfiOption::<FfiTransaction>::from_none, |tx| {
FfiOption::<FfiTransaction>::from_value(tx.into())
});
PointerResult::from_value(tx_ffi)
},
)
}
/// Query the blocks by block range from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `before`: `FfiOption<u64>` - end block of query
/// - `limit`: `u64` - number of blocks to query before `before`
///
/// # Returns
///
/// A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_block_vec(
indexer: *const IndexerServiceFFI,
before: FfiOption<u64>,
limit: u64,
) -> PointerResult<FfiVec<FfiBlock>, 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() };
let before_std = before.is_some.then(|| unsafe { *before.value });
runtime
.block_on(client.get_blocks(before_std, limit))
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
|block_vec| {
PointerResult::from_value(
block_vec
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
.into(),
)
},
)
}
/// Query the transactions range by account id from indexer.
///
/// # Arguments
///
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be queried.
/// - `account_id`: `FfiAccountId` - id of queried account
/// - `offset`: `u64` - first tx id of query
/// - `limit`: `u64` - number of tx ids to query after `offset`
///
/// # Returns
///
/// A `PointerResult<FfiVec<FfiBlock>, OperationStatus>` indicating success or failure.
///
/// # Safety
///
/// The caller must ensure that:
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
#[unsafe(no_mangle)]
pub unsafe extern "C" fn query_transactions_by_account(
indexer: *const IndexerServiceFFI,
account_id: FfiAccountId,
offset: u64,
limit: u64,
) -> PointerResult<FfiVec<FfiTransaction>, 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_transactions_by_account(
AccountId {
value: account_id.data,
},
offset,
limit,
))
.map_or_else(
|_| PointerResult::from_error(OperationStatus::ClientError),
|tx_vec| {
PointerResult::from_value(
tx_vec
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
.into(),
)
},
)
}

View File

@ -1,48 +1,6 @@
//! C-compatible type definitions for the FFI layer.
use std::ptr;
use indexer_service_protocol::ProgramId;
/// 32-byte array type for `AccountId`, keys, hashes, etc.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiBytes32 {
pub data: [u8; 32],
}
/// 64-byte array type for signatures, etc.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FfiBytes64 {
pub data: [u8; 64],
}
impl Default for FfiBytes64 {
fn default() -> Self {
Self { data: [0; 64] }
}
}
/// Program ID - 8 u32 values (32 bytes total).
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiProgramId {
pub data: [u32; 8],
}
impl From<ProgramId> for FfiProgramId {
fn from(value: ProgramId) -> Self {
Self { data: value.0 }
}
}
/// U128 - 16 bytes little endian.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiU128 {
pub data: [u8; 16],
}
use crate::api::types::{FfiBytes32, FfiProgramId, FfiU128};
/// Account data structure - C-compatible version of nssa Account.
///
@ -54,77 +12,17 @@ pub struct FfiAccount {
/// Balance as little-endian [u8; 16].
pub balance: FfiU128,
/// Pointer to account data bytes.
pub data: *const u8,
pub data: *mut u8,
/// Length of account data.
pub data_len: usize,
/// Capacity of account data.
pub data_cap: usize,
/// Nonce as little-endian [u8; 16].
pub nonce: FfiU128,
}
impl Default for FfiAccount {
fn default() -> Self {
Self {
program_owner: FfiProgramId::default(),
balance: FfiU128::default(),
data: std::ptr::null(),
data_len: 0,
nonce: FfiU128::default(),
}
}
}
/// Public keys for a private account (safe to expose).
#[repr(C)]
pub struct FfiPrivateAccountKeys {
/// Nullifier public key (32 bytes).
pub nullifier_public_key: FfiBytes32,
/// viewing public key (compressed secp256k1 point).
pub viewing_public_key: *const u8,
/// Length of viewing public key (typically 33 bytes).
pub viewing_public_key_len: usize,
}
impl Default for FfiPrivateAccountKeys {
fn default() -> Self {
Self {
nullifier_public_key: FfiBytes32::default(),
viewing_public_key: std::ptr::null(),
viewing_public_key_len: 0,
}
}
}
/// Public key info for a public account.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiPublicAccountKey {
pub public_key: FfiBytes32,
}
// Helper functions to convert between Rust and FFI types
impl FfiBytes32 {
/// Create from a 32-byte array.
#[must_use]
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self { data: bytes }
}
/// Create from an `AccountId`.
#[must_use]
pub const fn from_account_id(id: &nssa::AccountId) -> Self {
Self { data: *id.value() }
}
}
impl From<u128> for FfiU128 {
fn from(value: u128) -> Self {
Self {
data: value.to_le_bytes(),
}
}
}
impl From<&nssa::AccountId> for FfiBytes32 {
fn from(id: &nssa::AccountId) -> Self {
Self::from_account_id(id)
@ -132,20 +30,8 @@ impl From<&nssa::AccountId> for FfiBytes32 {
}
impl From<nssa::Account> for FfiAccount {
#[expect(
clippy::as_conversions,
reason = "We need to convert to byte arrays for FFI"
)]
fn from(value: nssa::Account) -> Self {
// Convert account data to FFI type
let data_vec: Vec<u8> = value.data.into();
let data_len = data_vec.len();
let data = if data_len > 0 {
let data_boxed = data_vec.into_boxed_slice();
Box::into_raw(data_boxed) as *const u8
} else {
ptr::null()
};
let (data, data_len, data_cap) = value.data.into_inner().into_raw_parts();
let program_owner = FfiProgramId {
data: value.program_owner,
@ -155,15 +41,41 @@ impl From<nssa::Account> for FfiAccount {
balance: value.balance.into(),
data,
data_len,
data_cap,
nonce: value.nonce.0.into(),
}
}
}
impl From<nssa::PublicKey> for FfiPublicAccountKey {
fn from(value: nssa::PublicKey) -> Self {
impl From<FfiAccount> for indexer_service_protocol::Account {
fn from(value: FfiAccount) -> Self {
Self {
public_key: FfiBytes32::from_bytes(*value.value()),
program_owner: ProgramId(value.program_owner.data),
balance: value.balance.into(),
data: indexer_service_protocol::Data(unsafe {
Vec::from_raw_parts(value.data, value.data_len, value.data_cap)
}),
nonce: value.nonce.into(),
}
}
}
/// Frees the resources associated with the given ffi account.
///
/// # Arguments
///
/// - `val`: An instance of `FfiAccount`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiAccount`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_account(val: FfiAccount) {
let orig_val: indexer_service_protocol::Account = val.into();
drop(orig_val);
}

View File

@ -1,7 +1,10 @@
use indexer_service_protocol::{BedrockStatus, Block, BlockHeader};
use indexer_service_protocol::{
BedrockStatus, Block, BlockHeader, HashType, MantleMsgId, Signature,
};
use crate::api::types::{
FfiBlockId, FfiHashType, FfiMsgId, FfiOption, FfiSignature, FfiTimestamp, vectors::FfiBlockBody,
FfiBlockId, FfiHashType, FfiMsgId, FfiOption, FfiSignature, FfiTimestamp, FfiVec,
transaction::free_ffi_transaction_vec, vectors::FfiBlockBody,
};
#[repr(C)]
@ -29,6 +32,23 @@ impl From<Block> for FfiBlock {
}
}
// impl From<Box<FfiBlock>> for Block {
// fn from(value: Box<FfiBlock>) -> Self {
// Self {
// header: BlockHeader {
// block_id: value.header.block_id,
// prev_block_hash: HashType(value.header.prev_block_hash.data),
// hash: HashType(value.header.hash.data),
// timestamp: value.header.timestamp,
// signature: Signature(value.header.signature.data),
// },
// body: (),
// bedrock_status: value.bedrock_status.into(),
// bedrock_parent_id: MantleMsgId(value.bedrock_parent_id.data),
// }
// }
// }
pub type FfiBlockOpt = FfiOption<FfiBlock>;
#[repr(C)]
@ -68,3 +88,115 @@ impl From<BedrockStatus> for FfiBedrockStatus {
}
}
}
impl From<FfiBedrockStatus> for BedrockStatus {
fn from(value: FfiBedrockStatus) -> Self {
match value {
FfiBedrockStatus::Finalized => Self::Finalized,
FfiBedrockStatus::Pending => Self::Pending,
FfiBedrockStatus::Safe => Self::Safe,
}
}
}
/// Frees the resources associated with the given ffi block.
///
/// # Arguments
///
/// - `val`: An instance of `FfiBlock`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiBlock`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_block(val: FfiBlock) {
// We don't really need all the casts, but just in case
// All except `ffi_tx_ffi_vec` is Copy types, so no need for Drop
let _ = BlockHeader {
block_id: val.header.block_id,
prev_block_hash: HashType(val.header.prev_block_hash.data),
hash: HashType(val.header.hash.data),
timestamp: val.header.timestamp,
signature: Signature(val.header.signature.data),
};
let ffi_tx_ffi_vec = val.body;
#[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")]
let _: BedrockStatus = val.bedrock_status.into();
let _ = MantleMsgId(val.bedrock_parent_id.data);
unsafe {
free_ffi_transaction_vec(ffi_tx_ffi_vec);
};
}
/// Frees the resources associated with the given ffi block option.
///
/// # Arguments
///
/// - `val`: An instance of `FfiBlockOpt`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiBlockOpt`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_block_opt(val: FfiBlockOpt) {
if val.is_some {
let value = unsafe { Box::from_raw(val.value) };
// We don't really need all the casts, but just in case
// All except `ffi_tx_ffi_vec` is Copy types, so no need for Drop
let _ = BlockHeader {
block_id: value.header.block_id,
prev_block_hash: HashType(value.header.prev_block_hash.data),
hash: HashType(value.header.hash.data),
timestamp: value.header.timestamp,
signature: Signature(value.header.signature.data),
};
let ffi_tx_ffi_vec = value.body;
#[expect(clippy::let_underscore_must_use, reason = "No use for this Copy type")]
let _: BedrockStatus = value.bedrock_status.into();
let _ = MantleMsgId(value.bedrock_parent_id.data);
unsafe {
free_ffi_transaction_vec(ffi_tx_ffi_vec);
};
}
}
/// Frees the resources associated with the given ffi block vector.
///
/// # Arguments
///
/// - `val`: An instance of `FfiVec<FfiBlock>`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiVec<FfiBlock>`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_block_vec(val: FfiVec<FfiBlock>) {
let ffi_block_std_vec: Vec<_> = val.into();
for block in ffi_block_std_vec {
unsafe {
free_ffi_block(block);
}
}
}

View File

@ -1,12 +1,72 @@
use indexer_service_protocol::{AccountId, HashType, MantleMsgId, PublicKey, Signature};
use crate::api::types::account::{FfiBytes32, FfiBytes64, FfiU128};
use indexer_service_protocol::{AccountId, HashType, MantleMsgId, ProgramId, PublicKey, Signature};
pub mod account;
pub mod block;
pub mod transaction;
pub mod vectors;
/// 32-byte array type for `AccountId`, keys, hashes, etc.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiBytes32 {
pub data: [u8; 32],
}
/// 64-byte array type for signatures, etc.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FfiBytes64 {
pub data: [u8; 64],
}
/// Program ID - 8 u32 values (32 bytes total).
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiProgramId {
pub data: [u32; 8],
}
impl From<ProgramId> for FfiProgramId {
fn from(value: ProgramId) -> Self {
Self { data: value.0 }
}
}
/// U128 - 16 bytes little endian.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct FfiU128 {
pub data: [u8; 16],
}
impl FfiBytes32 {
/// Create from a 32-byte array.
#[must_use]
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self { data: bytes }
}
/// Create from an `AccountId`.
#[must_use]
pub const fn from_account_id(id: &nssa::AccountId) -> Self {
Self { data: *id.value() }
}
}
impl From<u128> for FfiU128 {
fn from(value: u128) -> Self {
Self {
data: value.to_le_bytes(),
}
}
}
impl From<FfiU128> for u128 {
fn from(value: FfiU128) -> Self {
Self::from_le_bytes(value.data)
}
}
pub type FfiHashType = FfiBytes32;
pub type FfiMsgId = FfiBytes32;
pub type FfiBlockId = u64;
@ -64,6 +124,12 @@ impl<T> From<Vec<T>> for FfiVec<T> {
}
}
impl<T> From<FfiVec<T>> for Vec<T> {
fn from(value: FfiVec<T>) -> Self {
unsafe { Self::from_raw_parts(value.entries, value.len, value.capacity) }
}
}
#[repr(C)]
pub struct FfiOption<T> {
pub value: *mut T,

View File

@ -1,12 +1,13 @@
use indexer_service_protocol::{
CommitmentSetDigest, EncryptedAccountData, Nullifier, PrivacyPreservingMessage,
PrivacyPreservingTransaction, ProgramDeploymentTransaction, PublicKey, PublicMessage,
PublicTransaction, Signature, Transaction, ValidityWindow,
AccountId, Ciphertext, Commitment, CommitmentSetDigest, EncryptedAccountData,
EphemeralPublicKey, HashType, Nullifier, PrivacyPreservingMessage,
PrivacyPreservingTransaction, ProgramDeploymentMessage, ProgramDeploymentTransaction,
ProgramId, Proof, PublicKey, PublicMessage, PublicTransaction, Signature, Transaction,
ValidityWindow, WitnessSet,
};
use crate::api::types::{
FfiHashType, FfiPublicKey, FfiSignature,
account::{FfiBytes32, FfiProgramId},
FfiBytes32, FfiHashType, FfiOption, FfiProgramId, FfiPublicKey, FfiSignature, FfiVec,
vectors::{
FfiAccountIdList, FfiAccountList, FfiEncryptedAccountDataList, FfiInstructionDataList,
FfiNonceList, FfiNullifierCommitmentSetList, FfiProgramDeploymentMessage, FfiProof,
@ -37,6 +38,46 @@ impl From<PublicTransaction> for FfiPublicTransactionBody {
}
}
impl From<Box<FfiPublicTransactionBody>> for PublicTransaction {
fn from(value: Box<FfiPublicTransactionBody>) -> Self {
Self {
hash: HashType(value.hash.data),
message: PublicMessage {
program_id: ProgramId(value.message.program_id.data),
account_ids: {
let std_vec: Vec<_> = value.message.account_ids.into();
std_vec
.into_iter()
.map(|ffi_val| AccountId {
value: ffi_val.data,
})
.collect()
},
nonces: {
let std_vec: Vec<_> = value.message.nonces.into();
std_vec.into_iter().map(Into::into).collect()
},
instruction_data: value.message.instruction_data.into(),
},
witness_set: WitnessSet {
signatures_and_public_keys: {
let std_vec: Vec<_> = value.witness_set.into();
std_vec
.into_iter()
.map(|ffi_val| {
(
Signature(ffi_val.signature.data),
PublicKey(ffi_val.public_key.data),
)
})
.collect()
},
proof: None,
},
}
}
}
#[repr(C)]
pub struct FfiPublicMessage {
pub program_id: FfiProgramId,
@ -96,6 +137,84 @@ impl From<PrivacyPreservingTransaction> for FfiPrivateTransactionBody {
}
}
impl From<Box<FfiPrivateTransactionBody>> for PrivacyPreservingTransaction {
fn from(value: Box<FfiPrivateTransactionBody>) -> Self {
Self {
hash: HashType(value.hash.data),
message: PrivacyPreservingMessage {
public_account_ids: {
let std_vec: Vec<_> = value.message.public_account_ids.into();
std_vec
.into_iter()
.map(|ffi_val| AccountId {
value: ffi_val.data,
})
.collect()
},
nonces: {
let std_vec: Vec<_> = value.message.nonces.into();
std_vec.into_iter().map(Into::into).collect()
},
public_post_states: {
let std_vec: Vec<_> = value.message.public_post_states.into();
std_vec.into_iter().map(Into::into).collect()
},
encrypted_private_post_states: {
let std_vec: Vec<_> = value.message.encrypted_private_post_states.into();
std_vec
.into_iter()
.map(|ffi_val| EncryptedAccountData {
ciphertext: Ciphertext(ffi_val.ciphertext.into()),
epk: EphemeralPublicKey(ffi_val.epk.into()),
view_tag: ffi_val.view_tag,
})
.collect()
},
new_commitments: {
let std_vec: Vec<_> = value.message.new_commitments.into();
std_vec
.into_iter()
.map(|ffi_val| Commitment(ffi_val.data))
.collect()
},
new_nullifiers: {
let std_vec: Vec<_> = value.message.new_nullifiers.into();
std_vec
.into_iter()
.map(|ffi_val| {
(
Nullifier(ffi_val.nullifier.data),
CommitmentSetDigest(ffi_val.commitment_set_digest.data),
)
})
.collect()
},
block_validity_window: cast_ffi_validity_window(
value.message.block_validity_window,
),
timestamp_validity_window: cast_ffi_validity_window(
value.message.timestamp_validity_window,
),
},
witness_set: WitnessSet {
signatures_and_public_keys: {
let std_vec: Vec<_> = value.witness_set.into();
std_vec
.into_iter()
.map(|ffi_val| {
(
Signature(ffi_val.signature.data),
PublicKey(ffi_val.public_key.data),
)
})
.collect()
},
proof: Some(Proof(value.proof.into())),
},
}
}
}
#[repr(C)]
pub struct FfiPrivacyPreservingMessage {
pub public_account_ids: FfiAccountIdList,
@ -209,6 +328,17 @@ pub struct FfiProgramDeploymentTransactionBody {
pub message: FfiProgramDeploymentMessage,
}
impl From<Box<FfiProgramDeploymentTransactionBody>> for ProgramDeploymentTransaction {
fn from(value: Box<FfiProgramDeploymentTransactionBody>) -> Self {
Self {
hash: HashType(value.hash.data),
message: ProgramDeploymentMessage {
bytecode: value.message.into(),
},
}
}
}
impl From<ProgramDeploymentTransaction> for FfiProgramDeploymentTransactionBody {
fn from(value: ProgramDeploymentTransaction) -> Self {
Self {
@ -269,9 +399,123 @@ pub enum FfiTransactionKind {
ProgramDeploy,
}
/// Frees the resources associated with the given ffi transaction.
///
/// # Arguments
///
/// - `val`: An instance of `FfiTransaction`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiTransaction`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_transaction(val: FfiTransaction) {
match val.kind {
FfiTransactionKind::Public => {
let body = unsafe { Box::from_raw(val.body.public_body) };
let std_body: PublicTransaction = body.into();
drop(std_body);
}
FfiTransactionKind::Private => {
let body = unsafe { Box::from_raw(val.body.private_body) };
let std_body: PrivacyPreservingTransaction = body.into();
drop(std_body);
}
FfiTransactionKind::ProgramDeploy => {
let body = unsafe { Box::from_raw(val.body.program_deployment_body) };
let std_body: ProgramDeploymentTransaction = body.into();
drop(std_body);
}
}
}
/// Frees the resources associated with the given ffi transaction option.
///
/// # Arguments
///
/// - `val`: An instance of `FfiOption<FfiTransaction>`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiOption<FfiTransaction>`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_transaction_opt(val: FfiOption<FfiTransaction>) {
if val.is_some {
let value = unsafe { Box::from_raw(val.value) };
match value.kind {
FfiTransactionKind::Public => {
let body = unsafe { Box::from_raw(value.body.public_body) };
let std_body: PublicTransaction = body.into();
drop(std_body);
}
FfiTransactionKind::Private => {
let body = unsafe { Box::from_raw(value.body.private_body) };
let std_body: PrivacyPreservingTransaction = body.into();
drop(std_body);
}
FfiTransactionKind::ProgramDeploy => {
let body = unsafe { Box::from_raw(value.body.program_deployment_body) };
let std_body: ProgramDeploymentTransaction = body.into();
drop(std_body);
}
}
}
}
/// Frees the resources associated with the given vector of ffi transactions.
///
/// # Arguments
///
/// - `val`: An instance of `FfiVec<FfiTransaction>`.
///
/// # Returns
///
/// void.
///
/// # Safety
///
/// The caller must ensure that:
/// - `val` is a valid instance of `FfiVec<FfiTransaction>`.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_ffi_transaction_vec(val: FfiVec<FfiTransaction>) {
let ffi_tx_std_vec: Vec<_> = val.into();
for tx in ffi_tx_std_vec {
unsafe {
free_ffi_transaction(tx);
}
}
}
fn cast_validity_window(window: ValidityWindow) -> [u64; 2] {
[
window.0.0.unwrap_or_default(),
window.0.1.unwrap_or(u64::MAX),
]
}
const fn cast_ffi_validity_window(ffi_window: [u64; 2]) -> ValidityWindow {
let left = if ffi_window[0] == 0 {
None
} else {
Some(ffi_window[0])
};
let right = if ffi_window[1] == u64::MAX {
None
} else {
Some(ffi_window[1])
};
ValidityWindow((left, right))
}

View File

@ -1,6 +1,6 @@
use crate::api::types::{
FfiAccountId, FfiNonce, FfiVec,
account::{FfiAccount, FfiBytes32},
FfiAccountId, FfiBytes32, FfiNonce, FfiVec,
account::FfiAccount,
transaction::{
FfiEncryptedAccountData, FfiNullifierCommitmentSet, FfiSignaturePubKeyEntry, FfiTransaction,
},