fix(wallet_ffi): suggestions fix 2

This commit is contained in:
Pravdyvy 2026-06-03 14:51:15 +03:00
parent e82c0f3658
commit 7c0701bb7d
5 changed files with 153 additions and 15 deletions

View File

@ -22,8 +22,8 @@ const MAX_NUM_CYCLES_PUBLIC_EXECUTION: u64 = 1024 * 1024 * 32; // 32M cycles
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Program {
pub id: ProgramId,
pub elf: Vec<u8>,
id: ProgramId,
elf: Vec<u8>,
}
impl Program {

View File

@ -14,13 +14,13 @@ use crate::{
};
#[repr(C)]
pub struct SerializationHelperResult {
pub struct FfiInstructionWords {
pub instruction_words: *mut u32,
pub instruction_words_size: usize,
pub error: WalletFfiError,
}
impl SerializationHelperResult {
impl FfiInstructionWords {
const fn from_err(error: WalletFfiError) -> Self {
Self {
instruction_words: std::ptr::null_mut(),
@ -57,8 +57,9 @@ impl TryFrom<&FfiProgram> for Program {
impl From<Program> for FfiProgram {
fn from(value: Program) -> Self {
let elf_size = value.elf.len();
let elf_data = Box::into_raw(value.elf.into_boxed_slice()) as *const u8;
let elf_clone = value.elf().to_vec();
let elf_size = elf_clone.len();
let elf_data = Box::into_raw(elf_clone.into_boxed_slice()) as *const u8;
Self { elf_data, elf_size }
}
@ -157,10 +158,10 @@ impl Default for FfiTransactionResult {
pub unsafe extern "C" fn wallet_ffi_serialization_helper(
input_instruction_data: *const u8,
input_instruction_data_size: usize,
) -> SerializationHelperResult {
) -> FfiInstructionWords {
if input_instruction_data.is_null() {
print_error("Null input pointer for instruction_data");
return SerializationHelperResult::from_err(WalletFfiError::NullPointer);
return FfiInstructionWords::from_err(WalletFfiError::NullPointer);
}
let input_slice =
@ -172,7 +173,7 @@ pub unsafe extern "C" fn wallet_ffi_serialization_helper(
WalletFfiError::SerializationError
}) {
Ok(res) => res,
Err(err) => return SerializationHelperResult::from_err(err),
Err(err) => return FfiInstructionWords::from_err(err),
};
// The resulting vec contains len as prefix
@ -182,7 +183,7 @@ pub unsafe extern "C" fn wallet_ffi_serialization_helper(
let res_boxed = res_vec_u32.into_boxed_slice();
let res_ptr = Box::into_raw(res_boxed).cast::<u32>();
SerializationHelperResult {
FfiInstructionWords {
instruction_words: res_ptr,
instruction_words_size: res_len,
error: WalletFfiError::Success,
@ -418,3 +419,51 @@ pub unsafe extern "C" fn wallet_ffi_free_transaction_result(result: *mut FfiTran
}
}
}
/// Free a instruction words returned by `wallet_ffi_serialization_helper`.
///
/// # Safety
/// The result must be either null or a valid result from a serialization helper function.
#[no_mangle]
pub unsafe extern "C" fn wallet_ffi_free_instruction_words(words: *mut FfiInstructionWords) {
if words.is_null() {
return;
}
unsafe {
let words = &*words;
if !words.instruction_words.is_null() {
let words = std::slice::from_raw_parts_mut(
words.instruction_words,
words.instruction_words_size,
);
drop(Box::from_raw(std::ptr::from_mut::<[u32]>(words)));
}
}
}
#[cfg(test)]
mod tests {
use nssa::program::Program;
use crate::generic_transaction::FfiProgram;
#[test]
fn program_cast_consistency() {
let prog = Program::amm();
let first_5_bytes = prog.elf()[..5].to_vec();
let ffi_prog: FfiProgram = prog.into();
assert!(!ffi_prog.elf_data.is_null());
let mut ffi_first_5_bytes = vec![];
for i in 0..5 {
ffi_first_5_bytes.push(unsafe { *ffi_prog.elf_data.add(i) });
}
assert_eq!(ffi_first_5_bytes, first_5_bytes);
}
}

View File

@ -376,3 +376,23 @@ pub unsafe extern "C" fn wallet_ffi_free_account_identity(
}
}
}
#[cfg(test)]
mod tests {
use nssa::AccountId;
use wallet::AccountIdentity;
use crate::{keys::wallet_ffi_free_account_identity, FfiAccountIdentity};
#[test]
fn acc_identity_correct_free() {
let acc_identity = AccountIdentity::Public(AccountId::new([42; 32]));
let mut ffi_acc_identity: FfiAccountIdentity = acc_identity.into();
unsafe {
wallet_ffi_free_account_identity(&raw mut ffi_acc_identity);
}
assert!(ffi_acc_identity.viewing_public_key.is_null());
}
}

View File

@ -218,11 +218,11 @@ typedef struct FfiAccount {
struct FfiU128 nonce;
} FfiAccount;
typedef struct SerializationHelperResult {
typedef struct FfiInstructionWords {
uint32_t *instruction_words;
uintptr_t instruction_words_size;
enum WalletFfiError error;
} SerializationHelperResult;
} FfiInstructionWords;
/**
* Struct representing an account identity, given to `AccountManager` at intialization.
@ -541,8 +541,8 @@ enum WalletFfiError wallet_ffi_import_private_account(struct WalletHandle *handl
* # Safety
* - `input_instruction_data` must be a valid pointer
*/
struct SerializationHelperResult wallet_ffi_serialization_helper(const uint8_t *input_instruction_data,
uintptr_t input_instruction_data_size);
struct FfiInstructionWords wallet_ffi_serialization_helper(const uint8_t *input_instruction_data,
uintptr_t input_instruction_data_size);
/**
* Send generic public transaction.
@ -607,6 +607,14 @@ enum WalletFfiError wallet_ffi_send_generic_private_transaction(struct WalletHan
*/
void wallet_ffi_free_transaction_result(struct FfiTransactionResult *result);
/**
* Free a instruction words returned by `wallet_ffi_serialization_helper`.
*
* # Safety
* The result must be either null or a valid result from a serialization helper function.
*/
void wallet_ffi_free_instruction_words(struct FfiInstructionWords *words);
/**
* Get the public key for a public account.
*

View File

@ -1,3 +1,5 @@
use core::fmt;
use anyhow::Result;
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
use nssa::{AccountId, PrivateKey};
@ -10,7 +12,7 @@ use nssa_core::{
use crate::{ExecutionFailureKind, WalletCore};
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq)]
pub enum AccountIdentity {
Public(AccountId),
/// A public account without signing. Would not try to sign, even if account is owned.
@ -52,6 +54,65 @@ pub enum AccountIdentity {
},
}
impl fmt::Debug for AccountIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Public(id) => f.debug_tuple("Public").field(id).finish(),
Self::PublicNoSign(id) => f.debug_tuple("PublicNoSign").field(id).finish(),
Self::PrivateOwned(id) => f.debug_tuple("PrivateOwned").field(id).finish(),
Self::PrivateForeign {
npk,
vpk,
identifier,
} => f
.debug_struct("PrivateForeign")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivatePdaOwned(id) => f.debug_tuple("PrivatePdaOwned").field(id).finish(),
Self::PrivatePdaForeign {
account_id,
npk,
vpk,
identifier,
} => f
.debug_struct("PrivatePdaForeign")
.field("account_id", account_id)
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivateShared {
npk,
vpk,
identifier,
..
} => f
.debug_struct("PrivateShared")
.field("nsk", &"<redacted>")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivatePdaShared {
account_id,
npk,
vpk,
identifier,
..
} => f
.debug_struct("PrivatePdaShared")
.field("account_id", account_id)
.field("nsk", &"<redacted>")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
}
}
}
impl AccountIdentity {
#[must_use]
/// Note: `PublicNoSign` still counts as public, the variant just suppresses the signing-key