mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-02 07:09:29 +00:00
feat(wallet_ffi): generic private transactions added
This commit is contained in:
parent
b80c89848c
commit
ac0c4363b6
@ -21,12 +21,17 @@ use std::{
|
||||
use anyhow::Result;
|
||||
use integration_tests::{BlockingTestContext, TIME_TO_WAIT_FOR_BLOCK_SECONDS};
|
||||
use log::info;
|
||||
use nssa::{Account, AccountId, PrivateKey, PublicKey, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program};
|
||||
use nssa::{
|
||||
Account, AccountId, PrivateKey, PublicKey,
|
||||
privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
|
||||
};
|
||||
use nssa_core::program::DEFAULT_PROGRAM_ID;
|
||||
use tempfile::tempdir;
|
||||
use wallet::account::HumanReadableAccount;
|
||||
use wallet_ffi::{
|
||||
FfiAccount, FfiAccountIdentity, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys, FfiPublicAccountKey, FfiTransferResult, FfiU128, WalletHandle, error, generic_transaction::FfiProgramWithDependencies
|
||||
FfiAccount, FfiAccountIdentity, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys,
|
||||
FfiPublicAccountKey, FfiTransferResult, FfiU128, WalletHandle, error,
|
||||
generic_transaction::FfiProgramWithDependencies,
|
||||
};
|
||||
|
||||
unsafe extern "C" {
|
||||
@ -1096,27 +1101,18 @@ fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
|
||||
let mut from_account_identity = FfiAccountIdentity::default();
|
||||
let mut to_account_identity = FfiAccountIdentity::default();
|
||||
|
||||
unsafe{
|
||||
wallet_ffi_resolve_public_account(
|
||||
from,
|
||||
true,
|
||||
&raw mut from_account_identity
|
||||
)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
wallet_ffi_resolve_public_account(from, true, &raw mut from_account_identity).unwrap();
|
||||
}
|
||||
|
||||
unsafe{
|
||||
wallet_ffi_resolve_public_account(
|
||||
to,
|
||||
true,
|
||||
&raw mut to_account_identity
|
||||
)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
wallet_ffi_resolve_public_account(to, true, &raw mut to_account_identity).unwrap();
|
||||
}
|
||||
|
||||
let ffi_accs = vec![from_account_identity, to_account_identity];
|
||||
let account_identities_size = ffi_accs.len();
|
||||
let account_identities = Box::into_raw(ffi_accs.into_boxed_slice()) as *const FfiAccountIdentity;
|
||||
let account_identities =
|
||||
Box::into_raw(ffi_accs.into_boxed_slice()) as *const FfiAccountIdentity;
|
||||
|
||||
let instruction_data =
|
||||
Program::serialize_instruction(authenticated_transfer_core::Instruction::Transfer {
|
||||
@ -1129,15 +1125,15 @@ fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
|
||||
let program: ProgramWithDependencies = Program::authenticated_transfer_program().into();
|
||||
let program_with_dependencies = program.into();
|
||||
|
||||
unsafe{
|
||||
unsafe {
|
||||
wallet_ffi_send_generic_public_transaction(
|
||||
wallet_ffi_handle,
|
||||
account_identities,
|
||||
account_identities_size,
|
||||
instruction_words,
|
||||
instruction_words_size,
|
||||
program_with_dependencies,
|
||||
&raw mut transfer_result
|
||||
wallet_ffi_handle,
|
||||
account_identities,
|
||||
account_identities_size,
|
||||
instruction_words,
|
||||
instruction_words_size,
|
||||
program_with_dependencies,
|
||||
&raw mut transfer_result,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ use crate::{
|
||||
WalletHandle,
|
||||
},
|
||||
wallet::get_wallet,
|
||||
FfiAccountIdentity, FfiU128,
|
||||
FfiU128,
|
||||
};
|
||||
|
||||
/// Create a new public account.
|
||||
@ -653,29 +653,3 @@ pub unsafe extern "C" fn wallet_ffi_import_private_account(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Free account identity returned by `wallet_ffi_resolve_private_account` or
|
||||
/// `wallet_ffi_resolve_public_account`.
|
||||
///
|
||||
/// # Safety
|
||||
/// The account must be either null or a valid account returned by
|
||||
/// `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_free_account_identity(
|
||||
account_identity: *mut FfiAccountIdentity,
|
||||
) {
|
||||
if account_identity.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let account_identity = &*account_identity;
|
||||
if !account_identity.viewing_public_key.is_null() {
|
||||
let slice = std::slice::from_raw_parts_mut(
|
||||
account_identity.viewing_public_key.cast_mut(),
|
||||
account_identity.viewing_public_key_len,
|
||||
);
|
||||
drop(Box::from_raw(std::ptr::from_mut::<[u8]>(slice)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
use std::{collections::HashMap, ffi::CString};
|
||||
use std::{collections::HashMap, ffi::{CString, c_char}};
|
||||
|
||||
use nssa::{privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program};
|
||||
|
||||
use crate::{
|
||||
block_on,
|
||||
error::{print_error, WalletFfiError},
|
||||
map_execution_error,
|
||||
wallet::get_wallet,
|
||||
FfiAccountIdentity, FfiTransferResult, WalletHandle,
|
||||
FfiAccountIdentity, FfiBytes32, WalletHandle, block_on, error::{WalletFfiError, print_error}, map_execution_error, wallet::get_wallet
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
@ -141,19 +137,45 @@ impl From<ProgramWithDependencies> for FfiProgramWithDependencies {
|
||||
fn from(value: ProgramWithDependencies) -> Self {
|
||||
let ffi_program = value.program.into();
|
||||
|
||||
let ffi_deps: Vec<FfiProgram> = value.dependencies.into_values().map(Into::into).collect::<Vec<_>>();
|
||||
let ffi_deps: Vec<FfiProgram> = value
|
||||
.dependencies
|
||||
.into_values()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let deps_size = ffi_deps.len();
|
||||
let deps = Box::into_raw(ffi_deps.into_boxed_slice()) as *const FfiProgram;
|
||||
|
||||
Self { program: ffi_program, deps, deps_size }
|
||||
Self {
|
||||
program: ffi_program,
|
||||
deps,
|
||||
deps_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of a generic transaction operation.
|
||||
#[repr(C)]
|
||||
pub enum FfiExecutionFlow {
|
||||
Public = 0,
|
||||
PrivacyPreserving = 1,
|
||||
pub struct FfiTransactionResult {
|
||||
// TODO: Replace with HashType FFI representation
|
||||
/// Transaction hash (null-terminated string, or null on failure).
|
||||
pub tx_hash: *mut c_char,
|
||||
/// Whether the transaction succeeded.
|
||||
pub success: bool,
|
||||
pub secrets_data: *const FfiBytes32,
|
||||
/// Public transaction have 0 secrets
|
||||
pub secrets_size: usize,
|
||||
}
|
||||
|
||||
impl Default for FfiTransactionResult {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tx_hash: std::ptr::null_mut(),
|
||||
success: false,
|
||||
secrets_data: std::ptr::null(),
|
||||
secrets_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send generic public transaction
|
||||
@ -162,7 +184,7 @@ pub enum FfiExecutionFlow {
|
||||
/// - `handle`: Valid pointer to wallet handle
|
||||
/// - `account_identities`: Valid pointer to list of `FfiAccountIdentity`
|
||||
/// - `instruction_words`: Valid pointer to instruction words
|
||||
/// - `out_result`: Valid pointer to `FfiTransferResult`
|
||||
/// - `out_result`: Valid pointer to `FfiTransactionResult`
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` on successful creation
|
||||
@ -181,7 +203,7 @@ pub unsafe extern "C" fn wallet_ffi_send_generic_public_transaction(
|
||||
instruction_words: *const u32,
|
||||
instruction_words_size: usize,
|
||||
program_with_dependencies: FfiProgramWithDependencies,
|
||||
out_result: *mut FfiTransferResult,
|
||||
out_result: *mut FfiTransactionResult,
|
||||
) -> WalletFfiError {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
@ -267,3 +289,121 @@ pub unsafe extern "C" fn wallet_ffi_send_generic_public_transaction(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send generic private transaction
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid pointer to wallet handle
|
||||
/// - `account_identities`: Valid pointer to list of `FfiAccountIdentity`
|
||||
/// - `instruction_words`: Valid pointer to instruction words
|
||||
/// - `out_result`: Valid pointer to `FfiTransactionResult`
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` on successful creation
|
||||
/// - Error code on failure
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid pointer
|
||||
/// - `account_identities` must be a valid pointer
|
||||
/// - `instruction_words` must be a valid pointer
|
||||
/// - `out_result` must be a valid pointer
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_send_generic_private_transaction(
|
||||
handle: *mut WalletHandle,
|
||||
account_identities: *const FfiAccountIdentity,
|
||||
account_identities_size: usize,
|
||||
instruction_words: *const u32,
|
||||
instruction_words_size: usize,
|
||||
program_with_dependencies: FfiProgramWithDependencies,
|
||||
out_result: *mut FfiTransactionResult,
|
||||
) -> WalletFfiError {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
if account_identities.is_null() {
|
||||
print_error("Null output pointer for account identities list");
|
||||
return WalletFfiError::NullPointer;
|
||||
}
|
||||
|
||||
if instruction_words.is_null() {
|
||||
print_error("Null output pointer for instruction data");
|
||||
return WalletFfiError::NullPointer;
|
||||
}
|
||||
|
||||
if out_result.is_null() {
|
||||
print_error("Null output pointer return hash");
|
||||
return WalletFfiError::NullPointer;
|
||||
}
|
||||
|
||||
let wallet = match wrapper.core.lock() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
print_error(format!("Failed to lock wallet: {e}"));
|
||||
return WalletFfiError::InternalError;
|
||||
}
|
||||
};
|
||||
|
||||
let mut accounts = Vec::with_capacity(account_identities_size);
|
||||
let mut instruction_data = Vec::with_capacity(instruction_words_size);
|
||||
|
||||
// Alignment will be different, we need to read elements one-by-one
|
||||
for i in 0..account_identities_size {
|
||||
accounts.push(
|
||||
match match unsafe { account_identities.add(i).as_ref() }
|
||||
.ok_or(WalletFfiError::NullPointer)
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
print_error(format!(
|
||||
"account_identities_size does not match actual size of account_identities"
|
||||
));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
.try_into()
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(err) => return err,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Alignment will be different, we need to read elements one-by-one
|
||||
for i in 0..instruction_words_size {
|
||||
instruction_data.push(unsafe { *instruction_words.add(i) });
|
||||
}
|
||||
|
||||
let program = match program_with_dependencies.try_into() {
|
||||
Ok(v) => v,
|
||||
Err(err) => return err,
|
||||
};
|
||||
|
||||
match block_on(wallet.send_privacy_preserving_tx(accounts, instruction_data, &program)) {
|
||||
Ok((tx_hash, secrets)) => {
|
||||
let tx_hash = CString::new(tx_hash.to_string())
|
||||
.map_or(std::ptr::null_mut(), std::ffi::CString::into_raw);
|
||||
|
||||
unsafe {
|
||||
(*out_result).tx_hash = tx_hash;
|
||||
(*out_result).success = true;
|
||||
|
||||
let secrets_size = secrets.len();
|
||||
let boxed_slice = secrets.into_iter().map(Into::into).collect::<Vec<FfiBytes32>>().into_boxed_slice();
|
||||
let secrets_data = Box::into_raw(boxed_slice) as *const FfiBytes32;
|
||||
|
||||
(*out_result).secrets_size = secrets_size;
|
||||
(*out_result).secrets_data = secrets_data;
|
||||
}
|
||||
WalletFfiError::Success
|
||||
}
|
||||
Err(e) => {
|
||||
print_error(format!("Public send failed: {e:?}"));
|
||||
unsafe {
|
||||
*out_result = FfiTransactionResult::default();
|
||||
}
|
||||
map_execution_error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,3 +334,38 @@ pub unsafe extern "C" fn wallet_ffi_resolve_private_account(
|
||||
|
||||
WalletFfiError::Success
|
||||
}
|
||||
|
||||
/// Free account identity returned by `wallet_ffi_resolve_private_account` or
|
||||
/// `wallet_ffi_resolve_public_account`.
|
||||
///
|
||||
/// # Safety
|
||||
/// The account must be either null or a valid account returned by
|
||||
/// `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_free_account_identity(
|
||||
account_identity: *mut FfiAccountIdentity,
|
||||
) {
|
||||
if account_identity.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let FfiAccountIdentity {
|
||||
kind: _,
|
||||
account_id: _,
|
||||
nullifier_secret_key: _,
|
||||
nullifier_public_key: _,
|
||||
viewing_public_key,
|
||||
viewing_public_key_len,
|
||||
identifier: _,
|
||||
} = *account_identity;
|
||||
|
||||
if !viewing_public_key.is_null() {
|
||||
let slice = std::slice::from_raw_parts_mut(
|
||||
viewing_public_key.cast_mut(),
|
||||
viewing_public_key_len,
|
||||
);
|
||||
drop(Box::from_raw(std::ptr::from_mut::<[u8]>(slice)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
use core::slice;
|
||||
use std::{ffi::c_char, ptr};
|
||||
|
||||
use nssa::Data;
|
||||
use nssa::{Data, SharedSecretKey};
|
||||
use nssa_core::{encryption::shared_key_derivation::Secp256k1Point, NullifierPublicKey};
|
||||
use wallet::AccountIdentity;
|
||||
|
||||
@ -155,6 +155,12 @@ impl FfiBytes32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedSecretKey> for FfiBytes32 {
|
||||
fn from(value: SharedSecretKey) -> Self {
|
||||
Self { data: value.0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl FfiPrivateAccountKeys {
|
||||
#[must_use]
|
||||
pub const fn npk(&self) -> nssa_core::NullifierPublicKey {
|
||||
@ -189,7 +195,7 @@ pub enum FfiAccountIdentityKind {
|
||||
/// Struct representing of account identity, given to `AccountManager` at intialization
|
||||
#[repr(C)]
|
||||
pub struct FfiAccountIdentity {
|
||||
kind: FfiAccountIdentityKind,
|
||||
pub kind: FfiAccountIdentityKind,
|
||||
pub account_id: FfiBytes32,
|
||||
pub nullifier_secret_key: FfiBytes32,
|
||||
pub nullifier_public_key: FfiBytes32,
|
||||
|
||||
@ -218,6 +218,12 @@ typedef struct FfiAccount {
|
||||
struct FfiU128 nonce;
|
||||
} FfiAccount;
|
||||
|
||||
typedef struct SerializationHelperResult {
|
||||
uint32_t *instruction_words;
|
||||
uintptr_t instruction_words_size;
|
||||
enum WalletFfiError error;
|
||||
} SerializationHelperResult;
|
||||
|
||||
/**
|
||||
* Struct representing of account identity, given to `AccountManager` at intialization
|
||||
*/
|
||||
@ -231,12 +237,6 @@ typedef struct FfiAccountIdentity {
|
||||
struct FfiU128 identifier;
|
||||
} FfiAccountIdentity;
|
||||
|
||||
typedef struct SerializationHelperResult {
|
||||
uint32_t *instruction_words;
|
||||
uintptr_t instruction_words_size;
|
||||
enum WalletFfiError error;
|
||||
} SerializationHelperResult;
|
||||
|
||||
/**
|
||||
* Intended to be created manually
|
||||
*/
|
||||
@ -254,6 +254,32 @@ typedef struct FfiProgramWithDependencies {
|
||||
uintptr_t deps_size;
|
||||
} FfiProgramWithDependencies;
|
||||
|
||||
/**
|
||||
* Result of a generic transaction operation.
|
||||
*/
|
||||
typedef struct FfiTransactionResult {
|
||||
/**
|
||||
* Transaction hash (null-terminated string, or null on failure).
|
||||
*/
|
||||
char *tx_hash;
|
||||
/**
|
||||
* Whether the transaction succeeded.
|
||||
*/
|
||||
bool success;
|
||||
const struct FfiBytes32 *secrets_data;
|
||||
/**
|
||||
* Public transaction have 0 secrets
|
||||
*/
|
||||
uintptr_t secrets_size;
|
||||
} FfiTransactionResult;
|
||||
|
||||
/**
|
||||
* Public key info for a public account.
|
||||
*/
|
||||
typedef struct FfiPublicAccountKey {
|
||||
struct FfiBytes32 public_key;
|
||||
} FfiPublicAccountKey;
|
||||
|
||||
/**
|
||||
* Result of a transfer operation.
|
||||
*/
|
||||
@ -268,13 +294,6 @@ typedef struct FfiTransferResult {
|
||||
bool success;
|
||||
} FfiTransferResult;
|
||||
|
||||
/**
|
||||
* Public key info for a public account.
|
||||
*/
|
||||
typedef struct FfiPublicAccountKey {
|
||||
struct FfiBytes32 public_key;
|
||||
} FfiPublicAccountKey;
|
||||
|
||||
/**
|
||||
* Create a new public account.
|
||||
*
|
||||
@ -508,16 +527,6 @@ enum WalletFfiError wallet_ffi_import_private_account(struct WalletHandle *handl
|
||||
const struct FfiU128 *identifier,
|
||||
const char *account_state_json);
|
||||
|
||||
/**
|
||||
* Free account identity returned by `wallet_ffi_resolve_private_account` or
|
||||
* `wallet_ffi_resolve_public_account`.
|
||||
*
|
||||
* # Safety
|
||||
* The account must be either null or a valid account returned by
|
||||
* `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`.
|
||||
*/
|
||||
void wallet_ffi_free_account_identity(struct FfiAccountIdentity *account_identity);
|
||||
|
||||
/**
|
||||
* Serialize sequence of bytes into RISC0 readable words
|
||||
*
|
||||
@ -542,7 +551,7 @@ struct SerializationHelperResult wallet_ffi_serialization_helper(const uint8_t *
|
||||
* - `handle`: Valid pointer to wallet handle
|
||||
* - `account_identities`: Valid pointer to list of `FfiAccountIdentity`
|
||||
* - `instruction_words`: Valid pointer to instruction words
|
||||
* - `out_result`: Valid pointer to `FfiTransferResult`
|
||||
* - `out_result`: Valid pointer to `FfiTransactionResult`
|
||||
*
|
||||
* # Returns
|
||||
* - `Success` on successful creation
|
||||
@ -560,7 +569,34 @@ enum WalletFfiError wallet_ffi_send_generic_public_transaction(struct WalletHand
|
||||
const uint32_t *instruction_words,
|
||||
uintptr_t instruction_words_size,
|
||||
struct FfiProgramWithDependencies program_with_dependencies,
|
||||
struct FfiTransferResult *out_result);
|
||||
struct FfiTransactionResult *out_result);
|
||||
|
||||
/**
|
||||
* Send generic private transaction
|
||||
*
|
||||
* # Parameters
|
||||
* - `handle`: Valid pointer to wallet handle
|
||||
* - `account_identities`: Valid pointer to list of `FfiAccountIdentity`
|
||||
* - `instruction_words`: Valid pointer to instruction words
|
||||
* - `out_result`: Valid pointer to `FfiTransactionResult`
|
||||
*
|
||||
* # Returns
|
||||
* - `Success` on successful creation
|
||||
* - Error code on failure
|
||||
*
|
||||
* # Safety
|
||||
* - `handle` must be a valid pointer
|
||||
* - `account_identities` must be a valid pointer
|
||||
* - `instruction_words` must be a valid pointer
|
||||
* - `out_result` must be a valid pointer
|
||||
*/
|
||||
enum WalletFfiError wallet_ffi_send_generic_private_transaction(struct WalletHandle *handle,
|
||||
const struct FfiAccountIdentity *account_identities,
|
||||
uintptr_t account_identities_size,
|
||||
const uint32_t *instruction_words,
|
||||
uintptr_t instruction_words_size,
|
||||
struct FfiProgramWithDependencies program_with_dependencies,
|
||||
struct FfiTransactionResult *out_result);
|
||||
|
||||
/**
|
||||
* Get the public key for a public account.
|
||||
@ -699,6 +735,16 @@ enum WalletFfiError wallet_ffi_resolve_private_account(struct WalletHandle *hand
|
||||
struct FfiBytes32 account_id,
|
||||
struct FfiAccountIdentity *out_account_identity);
|
||||
|
||||
/**
|
||||
* Free account identity returned by `wallet_ffi_resolve_private_account` or
|
||||
* `wallet_ffi_resolve_public_account`.
|
||||
*
|
||||
* # Safety
|
||||
* The account must be either null or a valid account returned by
|
||||
* `wallet_ffi_resolve_private_account` or `wallet_ffi_resolve_public_account`.
|
||||
*/
|
||||
void wallet_ffi_free_account_identity(struct FfiAccountIdentity *account_identity);
|
||||
|
||||
/**
|
||||
* Claim a pinata reward using a public transaction.
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user