mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-02-17 11:53:14 +00:00
add get private account and init private account ffi methods
This commit is contained in:
parent
d8a8bdfc97
commit
707ea7d379
@ -2,6 +2,7 @@ use std::{
|
||||
collections::HashSet,
|
||||
ffi::{CStr, CString, c_char},
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@ -24,6 +25,11 @@ unsafe extern "C" {
|
||||
password: *const c_char,
|
||||
) -> *mut WalletHandle;
|
||||
|
||||
fn wallet_ffi_open(
|
||||
config_path: *const c_char,
|
||||
storage_path: *const c_char,
|
||||
) -> *mut WalletHandle;
|
||||
|
||||
fn wallet_ffi_destroy(handle: *mut WalletHandle);
|
||||
|
||||
fn wallet_ffi_create_account_public(
|
||||
@ -56,6 +62,12 @@ unsafe extern "C" {
|
||||
out_account: *mut FfiAccount,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_get_account_private(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_account: *mut FfiAccount,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_free_account_data(account: *mut FfiAccount);
|
||||
|
||||
fn wallet_ffi_get_public_account_key(
|
||||
@ -96,12 +108,29 @@ unsafe extern "C" {
|
||||
account_id: *const FfiBytes32,
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_register_private_account(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_save(handle: *mut WalletHandle) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_sync_to_block(handle: *mut WalletHandle, block_id: u64) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_get_current_block_height(
|
||||
handle: *mut WalletHandle,
|
||||
out_block_height: *mut u64,
|
||||
) -> error::WalletFfiError;
|
||||
}
|
||||
|
||||
fn new_wallet_ffi_with_test_context_config(ctx: &BlockingTestContext) -> *mut WalletHandle {
|
||||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let config_path = tempdir.path().join("wallet_config.json");
|
||||
let storage_path = tempdir.path().join("storage.json");
|
||||
fn new_wallet_ffi_with_test_context_config(
|
||||
ctx: &BlockingTestContext,
|
||||
home: &Path,
|
||||
) -> *mut WalletHandle {
|
||||
let config_path = home.join("wallet_config.json");
|
||||
let storage_path = home.join("storage.json");
|
||||
let mut config = ctx.ctx().wallet().config().to_owned();
|
||||
if let Some(config_overrides) = ctx.ctx().wallet().config_overrides().clone() {
|
||||
config.apply_overrides(config_overrides);
|
||||
@ -161,6 +190,15 @@ fn new_wallet_rust_with_default_config(password: &str) -> WalletCore {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn load_existing_ffi_wallet(home: &Path) -> *mut WalletHandle {
|
||||
let config_path = home.join("wallet_config.json");
|
||||
let storage_path = home.join("storage.json");
|
||||
let config_path = CString::new(config_path.to_str().unwrap()).unwrap();
|
||||
let storage_path = CString::new(storage_path.to_str().unwrap()).unwrap();
|
||||
|
||||
unsafe { wallet_ffi_open(config_path.as_ptr(), storage_path.as_ptr()) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_create_public_accounts() {
|
||||
let password = "password_for_tests";
|
||||
@ -232,6 +270,56 @@ fn test_wallet_ffi_create_private_accounts() {
|
||||
|
||||
assert_eq!(new_private_account_ids_ffi, new_private_account_ids_rust)
|
||||
}
|
||||
#[test]
|
||||
fn test_wallet_ffi_save_and_load_persistent_storage() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let mut out_private_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
|
||||
// Create a private account with the wallet FFI and save it
|
||||
unsafe {
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
wallet_ffi_create_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&mut out_private_account_id) as *mut FfiBytes32,
|
||||
);
|
||||
|
||||
wallet_ffi_save(wallet_ffi_handle);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
let private_account_keys = unsafe {
|
||||
let wallet_ffi_handle = load_existing_ffi_wallet(home.path());
|
||||
|
||||
let mut private_account = FfiAccount::default();
|
||||
|
||||
let result = wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&out_private_account_id) as *const FfiBytes32,
|
||||
(&mut private_account) as *mut FfiAccount,
|
||||
);
|
||||
assert_eq!(result, error::WalletFfiError::Success);
|
||||
|
||||
let mut out_keys = FfiPrivateAccountKeys::default();
|
||||
let result = wallet_ffi_get_private_account_keys(
|
||||
wallet_ffi_handle,
|
||||
(&out_private_account_id) as *const FfiBytes32,
|
||||
(&mut out_keys) as *mut FfiPrivateAccountKeys,
|
||||
);
|
||||
assert_eq!(result, error::WalletFfiError::Success);
|
||||
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
|
||||
out_keys
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
nssa::AccountId::from(&private_account_keys.npk()),
|
||||
out_private_account_id.into()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_list_accounts() {
|
||||
@ -326,7 +414,8 @@ fn test_wallet_ffi_list_accounts() {
|
||||
fn test_wallet_ffi_get_balance_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
|
||||
let balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
@ -354,7 +443,8 @@ fn test_wallet_ffi_get_balance_public() -> Result<()> {
|
||||
fn test_wallet_ffi_get_account_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
let mut out_account = FfiAccount::default();
|
||||
|
||||
let account: Account = unsafe {
|
||||
@ -385,11 +475,48 @@ fn test_wallet_ffi_get_account_public() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_account_private() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ctx.ctx().existing_private_accounts()[0];
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
let mut out_account = FfiAccount::default();
|
||||
|
||||
let account: Account = unsafe {
|
||||
let ffi_account_id = FfiBytes32::from(&account_id);
|
||||
let _result = wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&ffi_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
assert_eq!(account.balance, 10000);
|
||||
assert!(account.data.is_empty());
|
||||
assert_eq!(account.nonce, 0);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_account_data((&mut out_account) as *mut FfiAccount);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
info!("Successfully retrieved account with correct details");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_get_public_account_keys() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ctx.ctx().existing_public_accounts()[0];
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
let mut out_key = FfiPublicAccountKey::default();
|
||||
|
||||
let key: PublicKey = unsafe {
|
||||
@ -426,7 +553,8 @@ fn test_wallet_ffi_get_public_account_keys() -> Result<()> {
|
||||
fn test_wallet_ffi_get_private_account_keys() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let account_id: AccountId = ctx.ctx().existing_private_accounts()[0];
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
let mut keys = FfiPrivateAccountKeys::default();
|
||||
|
||||
unsafe {
|
||||
@ -504,7 +632,8 @@ fn test_wallet_ffi_base58_to_account_id() {
|
||||
#[test]
|
||||
fn test_wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
|
||||
// Create a new uninitialized public account
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
@ -563,10 +692,80 @@ fn test_wallet_ffi_init_public_account_auth_transfer() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_init_private_account_auth_transfer() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new().unwrap();
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
|
||||
// Create a new uninitialized public account
|
||||
let mut out_account_id = FfiBytes32::from_bytes([0; 32]);
|
||||
unsafe {
|
||||
wallet_ffi_create_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&mut out_account_id) as *mut FfiBytes32,
|
||||
);
|
||||
}
|
||||
|
||||
// Check its program owner is the default program id
|
||||
let account: Account = unsafe {
|
||||
let mut out_account = FfiAccount::default();
|
||||
wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
assert_eq!(account.program_owner, DEFAULT_PROGRAM_ID);
|
||||
|
||||
// Call the init funciton
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
wallet_ffi_register_private_account(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut transfer_result) as *mut FfiTransferResult,
|
||||
);
|
||||
}
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
unsafe {
|
||||
let mut current_height = 0;
|
||||
wallet_ffi_get_current_block_height(wallet_ffi_handle, (&mut current_height) as *mut u64);
|
||||
wallet_ffi_sync_to_block(wallet_ffi_handle, current_height);
|
||||
};
|
||||
|
||||
// Check that the program owner is now the authenticated transfer program
|
||||
let account: Account = unsafe {
|
||||
let mut out_account = FfiAccount::default();
|
||||
let _result = wallet_ffi_get_account_private(
|
||||
wallet_ffi_handle,
|
||||
(&out_account_id) as *const FfiBytes32,
|
||||
(&mut out_account) as *mut FfiAccount,
|
||||
);
|
||||
(&out_account).try_into().unwrap()
|
||||
};
|
||||
assert_eq!(
|
||||
account.program_owner,
|
||||
Program::authenticated_transfer_program().id()
|
||||
);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_transfer_result((&mut transfer_result) as *mut FfiTransferResult);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_transfer_public() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx);
|
||||
let home = tempfile::tempdir().unwrap();
|
||||
let wallet_ffi_handle = new_wallet_ffi_with_test_context_config(&ctx, home.path());
|
||||
let from: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[0]).into();
|
||||
let to: FfiBytes32 = (&ctx.ctx().existing_public_accounts()[1]).into();
|
||||
let amount: [u8; 16] = 100u128.to_le_bytes();
|
||||
|
||||
@ -354,6 +354,61 @@ pub unsafe extern "C" fn wallet_ffi_get_account_public(
|
||||
WalletFfiError::Success
|
||||
}
|
||||
|
||||
/// Get full private account data from the local storage.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `account_id`: The account ID (32 bytes)
|
||||
/// - `out_account`: Output pointer for account data
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` on successful query
|
||||
/// - Error code on failure
|
||||
///
|
||||
/// # Memory
|
||||
/// The account data must be freed with `wallet_ffi_free_account_data()`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
/// - `account_id` must be a valid pointer to a `FfiBytes32` struct
|
||||
/// - `out_account` must be a valid pointer to a `FfiAccount` struct
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_get_account_private(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_account: *mut FfiAccount,
|
||||
) -> WalletFfiError {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
if account_id.is_null() || out_account.is_null() {
|
||||
print_error("Null pointer argument");
|
||||
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 account_id = AccountId::new(unsafe { (*account_id).data });
|
||||
|
||||
let Some(account) = wallet.get_account_private(account_id) else {
|
||||
return WalletFfiError::AccountNotFound;
|
||||
};
|
||||
|
||||
unsafe {
|
||||
*out_account = account.into();
|
||||
}
|
||||
|
||||
WalletFfiError::Success
|
||||
}
|
||||
|
||||
/// Free account data returned by `wallet_ffi_get_account_public`.
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
@ -179,6 +179,85 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Register a private account on the network.
|
||||
///
|
||||
/// This initializes a private account. The account must be
|
||||
/// owned by this wallet.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `account_id`: Account ID to register
|
||||
/// - `out_result`: Output pointer for registration result
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` if the registration was submitted successfully
|
||||
/// - Error code on failure
|
||||
///
|
||||
/// # Memory
|
||||
/// The result must be freed with `wallet_ffi_free_transfer_result()`.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
/// - `account_id` must be a valid pointer to a `FfiBytes32` struct
|
||||
/// - `out_result` must be a valid pointer to a `FfiTransferResult` struct
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_register_private_account(
|
||||
handle: *mut WalletHandle,
|
||||
account_id: *const FfiBytes32,
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> WalletFfiError {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
if account_id.is_null() || out_result.is_null() {
|
||||
print_error("Null pointer argument");
|
||||
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 account_id = AccountId::new(unsafe { (*account_id).data });
|
||||
|
||||
let transfer = NativeTokenTransfer(&wallet);
|
||||
|
||||
match block_on(transfer.register_account_private(account_id)) {
|
||||
Ok(Ok((res, _secret))) => {
|
||||
let tx_hash = CString::new(res.tx_hash)
|
||||
.map(|s| s.into_raw())
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
unsafe {
|
||||
(*out_result).tx_hash = tx_hash;
|
||||
(*out_result).success = true;
|
||||
}
|
||||
WalletFfiError::Success
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
print_error(format!("Registration failed: {:?}", e));
|
||||
unsafe {
|
||||
(*out_result).tx_hash = ptr::null_mut();
|
||||
(*out_result).success = false;
|
||||
}
|
||||
match e {
|
||||
ExecutionFailureKind::KeyNotFoundError => WalletFfiError::KeyNotFound,
|
||||
ExecutionFailureKind::SequencerError => WalletFfiError::NetworkError,
|
||||
ExecutionFailureKind::SequencerClientError(_) => WalletFfiError::NetworkError,
|
||||
_ => WalletFfiError::InternalError,
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a transfer result returned by `wallet_ffi_transfer_public` or
|
||||
/// `wallet_ffi_register_public_account`.
|
||||
///
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user