mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-02-21 22:03:07 +00:00
261 lines
6.6 KiB
Rust
261 lines
6.6 KiB
Rust
//! C-compatible type definitions for the FFI layer.
|
|
|
|
use core::slice;
|
|
use std::{ffi::c_char, ptr};
|
|
|
|
use nssa::{Account, Data};
|
|
use nssa_core::encryption::shared_key_derivation::Secp256k1Point;
|
|
|
|
use crate::error::WalletFfiError;
|
|
|
|
/// Opaque pointer to the Wallet instance.
|
|
///
|
|
/// This type is never instantiated directly - it's used as an opaque handle
|
|
/// to hide the internal wallet structure from C code.
|
|
#[repr(C)]
|
|
pub struct WalletHandle {
|
|
_private: [u8; 0],
|
|
}
|
|
|
|
/// 32-byte array type for AccountId, keys, hashes, etc.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Default)]
|
|
pub struct FfiBytes32 {
|
|
pub data: [u8; 32],
|
|
}
|
|
|
|
/// 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,
|
|
}
|
|
|
|
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 FfiAccountListEntry {
|
|
pub account_id: FfiBytes32,
|
|
pub is_public: bool,
|
|
}
|
|
|
|
/// List of accounts returned by wallet_ffi_list_accounts.
|
|
#[repr(C)]
|
|
pub struct FfiAccountList {
|
|
pub entries: *mut FfiAccountListEntry,
|
|
pub count: usize,
|
|
}
|
|
|
|
impl Default for FfiAccountList {
|
|
fn default() -> Self {
|
|
Self {
|
|
entries: std::ptr::null_mut(),
|
|
count: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Result of a transfer operation.
|
|
#[repr(C)]
|
|
pub struct FfiTransferResult {
|
|
// TODO: Replace with HashType FFI representation
|
|
/// Transaction hash (null-terminated string, or null on failure)
|
|
pub tx_hash: *mut c_char,
|
|
/// Whether the transfer succeeded
|
|
pub success: bool,
|
|
}
|
|
|
|
impl Default for FfiTransferResult {
|
|
fn default() -> Self {
|
|
Self {
|
|
tx_hash: std::ptr::null_mut(),
|
|
success: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper functions to convert between Rust and FFI types
|
|
|
|
impl FfiBytes32 {
|
|
/// Create from a 32-byte array.
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Self {
|
|
Self { data: bytes }
|
|
}
|
|
|
|
/// Create from an AccountId.
|
|
pub fn from_account_id(id: &nssa::AccountId) -> Self {
|
|
Self { data: *id.value() }
|
|
}
|
|
}
|
|
|
|
impl FfiPrivateAccountKeys {
|
|
pub fn npk(&self) -> nssa_core::NullifierPublicKey {
|
|
nssa_core::NullifierPublicKey(self.nullifier_public_key.data)
|
|
}
|
|
|
|
pub fn vpk(&self) -> Result<nssa_core::encryption::ViewingPublicKey, WalletFfiError> {
|
|
if self.viewing_public_key_len == 33 {
|
|
let slice = unsafe {
|
|
slice::from_raw_parts(self.viewing_public_key, self.viewing_public_key_len)
|
|
};
|
|
Ok(Secp256k1Point(slice.to_vec()))
|
|
} else {
|
|
Err(WalletFfiError::InvalidKeyValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
u128::from_le_bytes(value.data)
|
|
}
|
|
}
|
|
|
|
impl From<&nssa::AccountId> for FfiBytes32 {
|
|
fn from(id: &nssa::AccountId) -> Self {
|
|
Self::from_account_id(id)
|
|
}
|
|
}
|
|
|
|
impl From<FfiBytes32> for nssa::AccountId {
|
|
fn from(bytes: FfiBytes32) -> Self {
|
|
nssa::AccountId::new(bytes.data)
|
|
}
|
|
}
|
|
|
|
impl From<nssa::Account> for FfiAccount {
|
|
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 program_owner = FfiProgramId {
|
|
data: value.program_owner,
|
|
};
|
|
FfiAccount {
|
|
program_owner,
|
|
balance: value.balance.into(),
|
|
data,
|
|
data_len,
|
|
nonce: value.nonce.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&FfiAccount> for nssa::Account {
|
|
type Error = WalletFfiError;
|
|
|
|
fn try_from(value: &FfiAccount) -> Result<Self, Self::Error> {
|
|
let data = if value.data_len > 0 {
|
|
unsafe {
|
|
let slice = slice::from_raw_parts(value.data, value.data_len);
|
|
Data::try_from(slice.to_vec()).map_err(|_| WalletFfiError::InvalidTypeConversion)?
|
|
}
|
|
} else {
|
|
Data::default()
|
|
};
|
|
Ok(Account {
|
|
program_owner: value.program_owner.data,
|
|
balance: value.balance.into(),
|
|
data,
|
|
nonce: value.nonce.into(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<nssa::PublicKey> for FfiPublicAccountKey {
|
|
fn from(value: nssa::PublicKey) -> Self {
|
|
Self {
|
|
public_key: FfiBytes32::from_bytes(*value.value()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&FfiPublicAccountKey> for nssa::PublicKey {
|
|
type Error = WalletFfiError;
|
|
|
|
fn try_from(value: &FfiPublicAccountKey) -> Result<Self, Self::Error> {
|
|
let public_key = nssa::PublicKey::try_new(value.public_key.data)
|
|
.map_err(|_| WalletFfiError::InvalidTypeConversion)?;
|
|
Ok(public_key)
|
|
}
|
|
}
|