diff --git a/Cargo.lock b/Cargo.lock index 769b5fb3..6517f789 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3486,6 +3486,8 @@ dependencies = [ "indexer_service", "indexer_service_rpc", "log", + "nssa", + "nssa_core", "sequencer_core", "tokio", "url", diff --git a/indexer_ffi/Cargo.toml b/indexer_ffi/Cargo.toml index 8099ad80..425e322a 100644 --- a/indexer_ffi/Cargo.toml +++ b/indexer_ffi/Cargo.toml @@ -5,6 +5,8 @@ name = "indexer_ffi" version = "0.1.0" [dependencies] +nssa_core.workspace = true +nssa.workspace = true indexer_service.workspace = true sequencer_core.workspace = true indexer_service_rpc.workspace = true diff --git a/indexer_ffi/indexer_ffi.h b/indexer_ffi/indexer_ffi.h index b734b59f..cce80837 100644 --- a/indexer_ffi/indexer_ffi.h +++ b/indexer_ffi/indexer_ffi.h @@ -10,24 +10,6 @@ typedef enum OperationStatus { ClientError = 3, } OperationStatus; -typedef enum TransactionKind { - Public = 0, - Private, - ProgramDeploy, -} TransactionKind; - -typedef enum BedrockStatus { - Pending = 0, - Safe, - Finalized, -} BedrockStatus; - -typedef struct Vec_AccountId Vec_AccountId; - -typedef struct Vec_Nonce Vec_Nonce; - -typedef struct Vec_u32 Vec_u32; - typedef struct IndexerServiceFFI { void *indexer_handle; void *runtime; @@ -58,69 +40,6 @@ typedef struct PointerResult_u64__OperationStatus { enum OperationStatus error; } PointerResult_u64__OperationStatus; -typedef uint64_t BlockId; - -typedef uint8_t HashType[32]; - -typedef uint64_t Timestamp; - -typedef uint8_t Signature[64]; - -typedef struct BlockHeader { - BlockId block_id; - HashType prev_block_hash; - HashType hash; - Timestamp timestamp; - Signature signature; -} BlockHeader; - -typedef uint32_t ProgramId[8]; - -typedef struct PublicMessage { - ProgramId program_id; - struct Vec_AccountId account_ids; - struct Vec_Nonce nonces; - struct Vec_u32 instruction_data; -} PublicMessage; - -typedef struct WitnessSet { - -} WitnessSet; - -typedef struct PublicTransactionBody { - HashType hash; - struct PublicMessage message; - struct WitnessSet witness_set; -} PublicTransactionBody; - -typedef struct TransactionBody { - struct PublicTransactionBody *public_body; -} TransactionBody; - -typedef struct Transaction { - struct TransactionBody body; - enum TransactionKind kind; -} Transaction; - -typedef struct BlockBody { - struct Transaction *txs; - uintptr_t len; -} BlockBody; - -typedef uint8_t MsgId[32]; - -typedef struct Block { - struct BlockHeader header; - struct BlockBody body; - enum BedrockStatus bedrock_status; - MsgId bedrock_parent_id; -} Block; - -typedef struct BlockOpt { - struct Block *block; - bool is_ok; -} BlockOpt; - /** * Simple wrapper around a pointer to a value or an error. * @@ -128,7 +47,7 @@ typedef struct BlockOpt { * dereferencing the pointer. */ typedef struct PointerResult_BlockOpt__OperationStatus { - struct BlockOpt *value; + BlockOpt *value; enum OperationStatus error; } PointerResult_BlockOpt__OperationStatus; diff --git a/indexer_ffi/src/api/convert.rs b/indexer_ffi/src/api/convert.rs index d970de38..f5636b89 100644 --- a/indexer_ffi/src/api/convert.rs +++ b/indexer_ffi/src/api/convert.rs @@ -3,10 +3,14 @@ use crate::api::types::BlockOpt; impl From> for BlockOpt { fn from(value: Option) -> Self { match value { - None => BlockOpt { block: std::ptr::null_mut(), is_ok: false }, - Some(block_orig) => BlockOpt { block: block_orig.into(), is_ok: true } + None => BlockOpt { + block: std::ptr::null_mut(), + is_ok: false, + }, + Some(block_orig) => BlockOpt { + block: block_orig.into(), + is_ok: true, + }, } } } - -impl From<> \ No newline at end of file diff --git a/indexer_ffi/src/api/mod.rs b/indexer_ffi/src/api/mod.rs index 9221a65c..4c80629b 100644 --- a/indexer_ffi/src/api/mod.rs +++ b/indexer_ffi/src/api/mod.rs @@ -1,9 +1,9 @@ pub use result::PointerResult; pub mod client; +pub mod convert; pub mod lifecycle; pub mod memory; pub mod query; pub mod result; pub mod types; -pub mod convert; diff --git a/indexer_ffi/src/api/query.rs b/indexer_ffi/src/api/query.rs index c4dd26b1..510f1383 100644 --- a/indexer_ffi/src/api/query.rs +++ b/indexer_ffi/src/api/query.rs @@ -1,6 +1,13 @@ use indexer_service_rpc::RpcClient; -use crate::{IndexerServiceFFI, api::{PointerResult, types::{Block, BlockId, BlockOpt}}, errors::OperationStatus}; +use crate::{ + IndexerServiceFFI, + api::{ + PointerResult, + types::{Block, BlockId, BlockOpt}, + }, + errors::OperationStatus, +}; /// Stops and frees the resources associated with the given indexer service. /// diff --git a/indexer_ffi/src/api/types.rs b/indexer_ffi/src/api/types.rs deleted file mode 100644 index 920b4764..00000000 --- a/indexer_ffi/src/api/types.rs +++ /dev/null @@ -1,99 +0,0 @@ -pub type HashType = [u8; 32]; -pub type MsgId = [u8; 32]; -pub type BlockId = u64; -pub type Timestamp = u64; -pub type Signature = [u8; 64]; -pub type ProgramId = [u32; 8]; -pub type AccountId = [u8; 32]; -pub type Nonce = u128; -pub type PublicKey = [u8; 32]; - -#[repr(C)] -pub struct Block { - pub header: BlockHeader, - pub body: BlockBody, - pub bedrock_status: BedrockStatus, - pub bedrock_parent_id: MsgId, -} - -#[repr(C)] -pub struct BlockOpt { - pub block: *const Block, - pub is_ok: bool, -} - -#[repr(C)] -pub struct PublicMessage { - pub program_id: ProgramId, - pub account_ids: Vec, - pub nonces: Vec, - pub instruction_data: Vec, -} - -#[repr(C)] -pub struct PublicTransactionBody { - pub hash: HashType, - pub message: PublicMessage, - pub witness_set: Vec<(Signature, PublicKey)>, -} - -#[repr(C)] -pub struct PrivateTransactionBody { - -} - -#[repr(C)] -pub struct ProgramDeploymentTransactionBody { - -} - -#[repr(C)] -pub struct TransactionBody { - pub public_body: *const PublicTransactionBody, - pub private_body: *const PrivateTransactionBody, - pub program_deployment_body: *const ProgramDeploymentTransactionBody, -} - -#[repr(C)] -pub struct Transaction { - pub body: TransactionBody, - pub kind: TransactionKind, -} - -#[repr(C)] -pub struct BlockBody { - pub txs: *const Transaction, - pub len: usize, -} - -impl Default for BlockBody { - fn default() -> Self { - Self { - txs: std::ptr::null_mut(), - len: 0, - } - } -} - -#[repr(C)] -pub struct BlockHeader { - pub block_id: BlockId, - pub prev_block_hash: HashType, - pub hash: HashType, - pub timestamp: Timestamp, - pub signature: Signature, -} - -#[repr(C)] -pub enum BedrockStatus { - Pending = 0x0, - Safe, - Finalized, -} - -#[repr(C)] -pub enum TransactionKind { - Public = 0x0, - Private, - ProgramDeploy, -} \ No newline at end of file diff --git a/indexer_ffi/src/api/types/account.rs b/indexer_ffi/src/api/types/account.rs new file mode 100644 index 00000000..e36230ae --- /dev/null +++ b/indexer_ffi/src/api/types/account.rs @@ -0,0 +1,195 @@ +//! C-compatible type definitions for the FFI layer. + +use std::ptr; + +use crate::api::types::FfiVec; + +/// 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], +} + +/// U128 - 16 bytes little endian. +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct FfiU128 { + pub data: [u8; 16], +} + +/// Account data structure - C-compatible version of nssa Account. +/// +/// Note: `balance` and `nonce` are u128 values represented as little-endian +/// byte arrays since C doesn't have native u128 support. +#[repr(C)] +pub struct FfiAccount { + pub program_owner: FfiProgramId, + /// Balance as little-endian [u8; 16]. + pub balance: FfiU128, + /// Pointer to account data bytes. + pub data: *const u8, + /// Length of account data. + pub data_len: usize, + /// Nonce as little-endian [u8; 16]. + pub nonce: FfiU128, +} + +#[repr(C)] +pub struct FfiAccountList { + pub entries: *const FfiAccount, + pub len: usize, +} + +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, +} + +/// Single entry in the account list. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct FfiAccountIdListEntry { + pub account_id: FfiBytes32, + pub is_public: bool, +} + +/// List of accounts returned by `wallet_ffi_list_accounts`. +#[repr(C)] +pub struct FfiAccountIdList { + pub entries: *mut FfiAccountIdListEntry, + pub count: usize, +} + +impl Default for FfiAccountIdList { + fn default() -> Self { + Self { + entries: std::ptr::null_mut(), + count: 0, + } + } +} + +pub type FfiVecBytes32 = FfiVec; + +// 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 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) + } +} + +impl From 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 = 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 program_owner = FfiProgramId { + data: value.program_owner, + }; + Self { + program_owner, + balance: value.balance.into(), + data, + data_len, + nonce: value.nonce.0.into(), + } + } +} + +impl From for FfiPublicAccountKey { + fn from(value: nssa::PublicKey) -> Self { + Self { + public_key: FfiBytes32::from_bytes(*value.value()), + } + } +} diff --git a/indexer_ffi/src/api/types/block.rs b/indexer_ffi/src/api/types/block.rs new file mode 100644 index 00000000..a31bd495 --- /dev/null +++ b/indexer_ffi/src/api/types/block.rs @@ -0,0 +1,36 @@ +use crate::api::types::{ + FfiBlockId, FfiHashType, FfiMsgId, FfiSignature, FfiTimestamp, FfiVec, + transaction::FfiTransaction, +}; + +#[repr(C)] +pub struct FfiBlock { + pub header: FfiBlockHeader, + pub body: FfiBlockBody, + pub bedrock_status: FfiBedrockStatus, + pub bedrock_parent_id: FfiMsgId, +} + +#[repr(C)] +pub struct FfiBlockOpt { + pub block: *const FfiBlock, + pub is_some: bool, +} + +pub type FfiBlockBody = FfiVec; + +#[repr(C)] +pub struct FfiBlockHeader { + pub block_id: FfiBlockId, + pub prev_block_hash: FfiHashType, + pub hash: FfiHashType, + pub timestamp: FfiTimestamp, + pub signature: FfiSignature, +} + +#[repr(C)] +pub enum FfiBedrockStatus { + Pending = 0x0, + Safe, + Finalized, +} diff --git a/indexer_ffi/src/api/types/mod.rs b/indexer_ffi/src/api/types/mod.rs new file mode 100644 index 00000000..73336c05 --- /dev/null +++ b/indexer_ffi/src/api/types/mod.rs @@ -0,0 +1,29 @@ +use crate::api::types::account::{FfiBytes32, FfiBytes64, FfiU128}; + +pub mod account; +pub mod block; +pub mod transaction; + +pub type FfiHashType = FfiBytes32; +pub type FfiMsgId = FfiBytes32; +pub type FfiBlockId = u64; +pub type FfiTimestamp = u64; +pub type FfiSignature = FfiBytes64; +pub type FfiAccountId = FfiBytes32; +pub type FfiNonce = FfiU128; +pub type FfiPublicKey = FfiBytes32; + +#[repr(C)] +pub struct FfiVec { + pub entries: *const T, + pub len: usize, +} + +impl Default for FfiVec { + fn default() -> Self { + Self { + entries: std::ptr::null(), + len: 0, + } + } +} diff --git a/indexer_ffi/src/api/types/transaction.rs b/indexer_ffi/src/api/types/transaction.rs new file mode 100644 index 00000000..4f92d492 --- /dev/null +++ b/indexer_ffi/src/api/types/transaction.rs @@ -0,0 +1,101 @@ +use crate::api::types::{ + FfiHashType, FfiNonce, FfiPublicKey, FfiSignature, FfiVec, + account::{FfiAccountIdList, FfiAccountList, FfiBytes32, FfiProgramId, FfiVecBytes32}, +}; + +#[repr(C)] +pub struct FfiPublicTransactionBody { + pub hash: FfiHashType, + pub message: FfiPublicMessage, + pub witness_set: FfiSignaturePubKeyList, +} + +pub type FfiNonceList = FfiVec; + +pub type FfiInstructionDataList = FfiVec; + +#[repr(C)] +pub struct FfiPublicMessage { + pub program_id: FfiProgramId, + pub account_ids: FfiAccountIdList, + pub nonces: FfiNonceList, + pub instruction_data: FfiInstructionDataList, +} + +#[repr(C)] +pub struct FfiPrivateTransactionBody { + pub hash: FfiHashType, + pub message: FfiPrivacyPreservingMessage, + pub witness_set: FfiSignaturePubKeyList, + pub proof: FfiProofOpt, +} + +#[repr(C)] +pub struct FfiPrivacyPreservingMessage { + pub public_account_ids: FfiAccountIdList, + pub nonces: FfiNonceList, + pub public_post_states: FfiAccountList, + pub encrypted_private_post_states: FfiVec, + pub new_commitments: FfiVecBytes32, + pub new_nullifiers: FfiVec, + pub block_validity_window: [u64; 2], + pub timestamp_validity_window: [u64; 2], +} + +#[repr(C)] +pub struct NullifierCommitmentSet { + pub nullifier: FfiBytes32, + pub commitment_set_digest: FfiBytes32, +} + +#[repr(C)] +pub struct FfiEncryptedAccountData { + pub ciphertext: FfiVec, + pub epk: FfiVec, + pub view_tag: u8, +} + +#[repr(C)] +pub struct FfiSignaturePubKeyEntry { + pub signature: FfiSignature, + pub public_key: FfiPublicKey, +} + +pub struct FfiSignaturePubKeyList { + pub entries: *const FfiSignaturePubKeyEntry, + pub len: usize, +} + +#[repr(C)] +pub struct FfiProofOpt { + pub proof: FfiVec, + pub is_some: bool, +} + +#[repr(C)] +pub struct FfiProgramDeploymentTransactionBody { + pub hash: FfiHashType, + pub message: FfiProgramDeploymentMessage, +} + +pub type FfiProgramDeploymentMessage = FfiVec; + +#[repr(C)] +pub struct FfiTransactionBody { + pub public_body: *const FfiPublicTransactionBody, + pub private_body: *const FfiPrivateTransactionBody, + pub program_deployment_body: *const FfiProgramDeploymentTransactionBody, +} + +#[repr(C)] +pub struct FfiTransaction { + pub body: FfiTransactionBody, + pub kind: FfiTransactionKind, +} + +#[repr(C)] +pub enum FfiTransactionKind { + Public = 0x0, + Private, + ProgramDeploy, +} diff --git a/wallet-ffi/Cargo.toml b/wallet-ffi/Cargo.toml index 0af20a54..37d552ec 100644 --- a/wallet-ffi/Cargo.toml +++ b/wallet-ffi/Cargo.toml @@ -15,6 +15,7 @@ wallet.workspace = true nssa.workspace = true nssa_core.workspace = true sequencer_service_rpc = { workspace = true, features = ["client"] } + tokio.workspace = true [build-dependencies]