mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-01 19:39:50 +00:00
Merge pull request #577 from logos-blockchain/Pravdyvy/wallet-ffi-labels
Wallet ffi labels
This commit is contained in:
commit
97aa85d123
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@ -2,6 +2,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
@ -94,7 +95,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Lint workspace
|
||||
env:
|
||||
@ -125,7 +126,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install nextest
|
||||
run: cargo install --locked cargo-nextest
|
||||
@ -156,7 +157,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install nextest
|
||||
run: cargo install --locked cargo-nextest
|
||||
@ -245,7 +246,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Test valid proof
|
||||
env:
|
||||
@ -268,7 +269,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install just
|
||||
run: cargo install --locked just
|
||||
|
||||
@ -57,12 +57,16 @@ Before merging a PR, consider squashing non-meaningful commits. E.g.:
|
||||
|
||||
Could be squashed to an empty commit if they belong to the same PR.
|
||||
|
||||
## Default branch
|
||||
|
||||
By default all PRs must be directed into the `dev` branch. This helps us to keep releases stable.
|
||||
|
||||
## Branch workflow
|
||||
|
||||
When bringing your feature branch up to date, prefer rebasing on top of `main`.
|
||||
When bringing your feature branch up to date, prefer rebasing on top of `dev`.
|
||||
|
||||
- Preferred: `git rebase main`
|
||||
- Avoid: `git merge main` in feature branches
|
||||
- Preferred: `git rebase dev`
|
||||
- Avoid: `git merge dev` in feature branches
|
||||
|
||||
This keeps commit history cleaner and makes reviews easier.
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ use std::{
|
||||
ffi::{CStr, CString, c_char},
|
||||
io::Write as _,
|
||||
path::Path,
|
||||
str::FromStr as _,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@ -30,9 +31,11 @@ use log::info;
|
||||
use tempfile::tempdir;
|
||||
use wallet::{account::HumanReadableAccount, program_facades::vault::Vault};
|
||||
use wallet_ffi::{
|
||||
FfiAccount, FfiAccountIdentity, FfiAccountList, FfiBytes32, FfiPrivateAccountKeys,
|
||||
FfiProgramId, FfiPublicAccountKey, FfiTransferResult, FfiU128, WalletHandle, error,
|
||||
FfiAccount, FfiAccountIdWithPrivacy, FfiAccountIdentity, FfiAccountList, FfiBytes32,
|
||||
FfiPrivateAccountKeys, FfiProgramId, FfiPublicAccountKey, FfiTransferResult, FfiU128,
|
||||
WalletHandle, error,
|
||||
generic_transaction::{FfiProgramWithDependencies, FfiTransactionResult},
|
||||
label::{AccountIdResolvedFromLabel, LabelAvailability, LabelList},
|
||||
wallet::FfiCreateWalletOutput,
|
||||
};
|
||||
|
||||
@ -257,6 +260,29 @@ unsafe extern "C" {
|
||||
fn wallet_ffi_free_transaction_result(result: *mut FfiTransactionResult);
|
||||
|
||||
fn wallet_ffi_free_account_identity(account_identity: *mut FfiAccountIdentity);
|
||||
|
||||
fn wallet_ffi_check_label_available(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
) -> LabelAvailability;
|
||||
|
||||
fn wallet_ffi_add_label(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
account_id_with_privacy: FfiAccountIdWithPrivacy,
|
||||
) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_resolve_label(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
) -> AccountIdResolvedFromLabel;
|
||||
|
||||
fn wallet_ffi_get_all_labels_for_account(
|
||||
handle: *mut WalletHandle,
|
||||
account_id_with_privacy: FfiAccountIdWithPrivacy,
|
||||
) -> LabelList;
|
||||
|
||||
fn wallet_ffi_free_label_list(label_list: *mut LabelList) -> error::WalletFfiError;
|
||||
}
|
||||
|
||||
fn new_wallet_ffi_with_test_context_config(
|
||||
@ -1926,3 +1952,125 @@ fn test_wallet_ffi_vault_balance_and_claim_private() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_single_label() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
let FfiCreateWalletOutput {
|
||||
wallet: wallet_ffi_handle,
|
||||
mnemonic: _,
|
||||
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
|
||||
let mut out_account_id_1 = FfiBytes32::from_bytes([0; 32]);
|
||||
unsafe {
|
||||
wallet_ffi_create_account_public(wallet_ffi_handle, &raw mut out_account_id_1).unwrap();
|
||||
}
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
let lab_1 = CString::from_str("LABEL1").unwrap().into_raw();
|
||||
|
||||
let lab_1_availability = unsafe { wallet_ffi_check_label_available(wallet_ffi_handle, lab_1) };
|
||||
|
||||
assert_eq!(lab_1_availability.error, error::WalletFfiError::Success);
|
||||
assert!(lab_1_availability.is_available);
|
||||
|
||||
let acc_1_id_with_privacy = FfiAccountIdWithPrivacy {
|
||||
account_id: out_account_id_1,
|
||||
is_private: false,
|
||||
};
|
||||
|
||||
let err = unsafe { wallet_ffi_add_label(wallet_ffi_handle, lab_1, acc_1_id_with_privacy) };
|
||||
|
||||
assert_eq!(err, error::WalletFfiError::Success);
|
||||
|
||||
let lab_1_availability = unsafe { wallet_ffi_check_label_available(wallet_ffi_handle, lab_1) };
|
||||
|
||||
assert!(!lab_1_availability.is_available);
|
||||
|
||||
let acc_resolved = unsafe { wallet_ffi_resolve_label(wallet_ffi_handle, lab_1) };
|
||||
|
||||
assert_eq!(acc_resolved.account_id, acc_1_id_with_privacy);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_string(lab_1);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_more_labels() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
let FfiCreateWalletOutput {
|
||||
wallet: wallet_ffi_handle,
|
||||
mnemonic: _,
|
||||
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
|
||||
let mut out_account_id_1 = FfiBytes32::from_bytes([0; 32]);
|
||||
unsafe {
|
||||
wallet_ffi_create_account_public(wallet_ffi_handle, &raw mut out_account_id_1).unwrap();
|
||||
}
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
let lab_1 = CString::from_str("LABEL1").unwrap().into_raw();
|
||||
let lab_2 = CString::from_str("LABEL2").unwrap().into_raw();
|
||||
let lab_3 = CString::from_str("LABEL3").unwrap().into_raw();
|
||||
|
||||
let acc_1_id_with_privacy = FfiAccountIdWithPrivacy {
|
||||
account_id: out_account_id_1,
|
||||
is_private: false,
|
||||
};
|
||||
|
||||
let err = unsafe { wallet_ffi_add_label(wallet_ffi_handle, lab_1, acc_1_id_with_privacy) };
|
||||
|
||||
assert_eq!(err, error::WalletFfiError::Success);
|
||||
|
||||
let err = unsafe { wallet_ffi_add_label(wallet_ffi_handle, lab_2, acc_1_id_with_privacy) };
|
||||
|
||||
assert_eq!(err, error::WalletFfiError::Success);
|
||||
|
||||
let err = unsafe { wallet_ffi_add_label(wallet_ffi_handle, lab_3, acc_1_id_with_privacy) };
|
||||
|
||||
assert_eq!(err, error::WalletFfiError::Success);
|
||||
|
||||
let mut label_list_for_out_acc =
|
||||
unsafe { wallet_ffi_get_all_labels_for_account(wallet_ffi_handle, acc_1_id_with_privacy) };
|
||||
|
||||
assert_eq!(label_list_for_out_acc.error, error::WalletFfiError::Success);
|
||||
assert_eq!(label_list_for_out_acc.labels_size, 3);
|
||||
|
||||
let lab_ref_1 = unsafe { &*label_list_for_out_acc.labels_data.add(0) };
|
||||
let lab_ref_c_str_1 = unsafe { CStr::from_ptr(*lab_ref_1) };
|
||||
|
||||
assert_eq!(lab_ref_c_str_1.to_str().unwrap(), "LABEL1");
|
||||
|
||||
let lab_ref_2 = unsafe { &*label_list_for_out_acc.labels_data.add(1) };
|
||||
let lab_ref_c_str_2 = unsafe { CStr::from_ptr(*lab_ref_2) };
|
||||
|
||||
assert_eq!(lab_ref_c_str_2.to_str().unwrap(), "LABEL2");
|
||||
|
||||
let lab_ref_3 = unsafe { &*label_list_for_out_acc.labels_data.add(2) };
|
||||
let lab_ref_c_str_3 = unsafe { CStr::from_ptr(*lab_ref_3) };
|
||||
|
||||
assert_eq!(lab_ref_c_str_3.to_str().unwrap(), "LABEL3");
|
||||
|
||||
let err = unsafe { wallet_ffi_free_label_list(&raw mut label_list_for_out_acc) };
|
||||
|
||||
assert_eq!(err, error::WalletFfiError::Success);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_string(lab_1);
|
||||
wallet_ffi_free_string(lab_2);
|
||||
wallet_ffi_free_string(lab_3);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
316
lez/wallet-ffi/src/label.rs
Normal file
316
lez/wallet-ffi/src/label.rs
Normal file
@ -0,0 +1,316 @@
|
||||
use std::{
|
||||
ffi::{c_char, CString},
|
||||
str::FromStr as _,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
c_str_to_string,
|
||||
error::{print_error, WalletFfiError},
|
||||
wallet::get_wallet,
|
||||
FfiAccountIdWithPrivacy, WalletHandle,
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LabelAvailability {
|
||||
pub is_available: bool,
|
||||
pub error: WalletFfiError,
|
||||
}
|
||||
|
||||
impl LabelAvailability {
|
||||
#[must_use]
|
||||
pub const fn availability(is_available: bool) -> Self {
|
||||
Self {
|
||||
is_available,
|
||||
error: WalletFfiError::Success,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn error(error: WalletFfiError) -> Self {
|
||||
Self {
|
||||
is_available: false,
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct AccountIdResolvedFromLabel {
|
||||
pub account_id: FfiAccountIdWithPrivacy,
|
||||
pub error: WalletFfiError,
|
||||
}
|
||||
|
||||
impl AccountIdResolvedFromLabel {
|
||||
#[must_use]
|
||||
pub const fn account_id(account_id: FfiAccountIdWithPrivacy) -> Self {
|
||||
Self {
|
||||
account_id,
|
||||
error: WalletFfiError::Success,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn error(error: WalletFfiError) -> Self {
|
||||
Self {
|
||||
account_id: FfiAccountIdWithPrivacy::default(),
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LabelList {
|
||||
pub labels_data: *mut *const c_char,
|
||||
pub labels_size: usize,
|
||||
pub error: WalletFfiError,
|
||||
}
|
||||
|
||||
impl LabelList {
|
||||
#[must_use]
|
||||
pub fn from_labels(labels: Vec<*const c_char>) -> Self {
|
||||
let labels_size = labels.len();
|
||||
let boxed_slice = labels.into_boxed_slice();
|
||||
let labels_data = Box::into_raw(boxed_slice).cast::<*const c_char>();
|
||||
|
||||
Self {
|
||||
labels_data,
|
||||
labels_size,
|
||||
error: WalletFfiError::Success,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn error(error: WalletFfiError) -> Self {
|
||||
Self {
|
||||
labels_data: std::ptr::null_mut(),
|
||||
labels_size: 0,
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if label is available.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `label`: Input null terminated C string for a label
|
||||
///
|
||||
/// # Returns
|
||||
/// - `LabelAvailability` struct
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
/// - `label` must be a valid pointer to a null-terminated C string
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_check_label_available(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
) -> LabelAvailability {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return LabelAvailability::error(e),
|
||||
};
|
||||
|
||||
let label = match c_str_to_string(label, "label") {
|
||||
Ok(value) => value,
|
||||
Err(e) => return LabelAvailability::error(e),
|
||||
};
|
||||
|
||||
let wallet = match wrapper.core.lock() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
print_error(format!("Failed to lock wallet: {e}"));
|
||||
return LabelAvailability::error(WalletFfiError::InternalError);
|
||||
}
|
||||
};
|
||||
|
||||
let is_available = wallet
|
||||
.storage()
|
||||
.check_label_availability(&label.into())
|
||||
.is_ok();
|
||||
|
||||
LabelAvailability::availability(is_available)
|
||||
}
|
||||
|
||||
/// Add new label.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `label`: Input null terminated C string for a label
|
||||
/// - `account_id_with_privacy`: The account ID (32 bytes) and its privacy.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` on successful query
|
||||
/// - Error code on failure
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
/// - `label` must be a valid pointer to a null-terminated C string
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_add_label(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
account_id_with_privacy: FfiAccountIdWithPrivacy,
|
||||
) -> WalletFfiError {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let label = match c_str_to_string(label, "label") {
|
||||
Ok(value) => value,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let mut wallet = match wrapper.core.lock() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
print_error(format!("Failed to lock wallet: {e}"));
|
||||
return WalletFfiError::InternalError;
|
||||
}
|
||||
};
|
||||
|
||||
match wallet
|
||||
.storage_mut()
|
||||
.add_label(label.into(), account_id_with_privacy.into())
|
||||
{
|
||||
Ok(()) => WalletFfiError::Success,
|
||||
Err(err) => {
|
||||
print_error(format!("Failed to add label : {err}"));
|
||||
WalletFfiError::InternalError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a label.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `label`: Input null terminated C string for a label
|
||||
///
|
||||
/// # Returns
|
||||
/// - `AccountIdResolvedFromLabel` struct
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
/// - `label` must be a valid pointer to a null-terminated C string
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_resolve_label(
|
||||
handle: *mut WalletHandle,
|
||||
label: *const c_char,
|
||||
) -> AccountIdResolvedFromLabel {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return AccountIdResolvedFromLabel::error(e),
|
||||
};
|
||||
|
||||
let label = match c_str_to_string(label, "label") {
|
||||
Ok(value) => value,
|
||||
Err(e) => return AccountIdResolvedFromLabel::error(e),
|
||||
};
|
||||
|
||||
let mut wallet = match wrapper.core.lock() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
print_error(format!("Failed to lock wallet: {e}"));
|
||||
return AccountIdResolvedFromLabel::error(WalletFfiError::InternalError);
|
||||
}
|
||||
};
|
||||
|
||||
wallet
|
||||
.storage_mut()
|
||||
.resolve_label(&label.into())
|
||||
.map_or_else(
|
||||
|| {
|
||||
print_error("Failed to resolve label");
|
||||
AccountIdResolvedFromLabel::error(WalletFfiError::InternalError)
|
||||
},
|
||||
|acc_id| AccountIdResolvedFromLabel::account_id(acc_id.into()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get all labels for account.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `handle`: Valid wallet handle
|
||||
/// - `account_id_with_privacy`: The account ID (32 bytes) and its privacy.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `LabelList` struct
|
||||
///
|
||||
/// # Safety
|
||||
/// - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_get_all_labels_for_account(
|
||||
handle: *mut WalletHandle,
|
||||
account_id_with_privacy: FfiAccountIdWithPrivacy,
|
||||
) -> LabelList {
|
||||
let wrapper = match get_wallet(handle) {
|
||||
Ok(w) => w,
|
||||
Err(e) => return LabelList::error(e),
|
||||
};
|
||||
|
||||
let wallet = match wrapper.core.lock() {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
print_error(format!("Failed to lock wallet: {e}"));
|
||||
return LabelList::error(WalletFfiError::InternalError);
|
||||
}
|
||||
};
|
||||
|
||||
let mut labels = vec![];
|
||||
|
||||
for label in wallet
|
||||
.storage()
|
||||
.labels_for_account(account_id_with_privacy.into())
|
||||
{
|
||||
let Ok(label_c) = CString::from_str(label.as_ref()) else {
|
||||
print_error(format!("Failed to cast label into C string: {label}"));
|
||||
return LabelList::error(WalletFfiError::InternalError);
|
||||
};
|
||||
|
||||
let label_raw = label_c.into_raw().cast_const();
|
||||
|
||||
labels.push(label_raw);
|
||||
}
|
||||
|
||||
LabelList::from_labels(labels)
|
||||
}
|
||||
|
||||
/// Free label list.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `label_list`: Input list of labels
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Success` on successful query
|
||||
/// - Error code on failure
|
||||
///
|
||||
/// # Safety
|
||||
/// - `label_list` must be a valid pointer to `LabelList`, received from
|
||||
/// `wallet_ffi_get_all_labels_for_account`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wallet_ffi_free_label_list(label_list: *mut LabelList) -> WalletFfiError {
|
||||
if label_list.is_null() {
|
||||
return WalletFfiError::NullPointer;
|
||||
}
|
||||
|
||||
let labels_raw = unsafe { &*label_list };
|
||||
|
||||
if !labels_raw.labels_data.is_null() && labels_raw.labels_size > 0 {
|
||||
let labels_slice =
|
||||
std::slice::from_raw_parts_mut(labels_raw.labels_data, labels_raw.labels_size);
|
||||
|
||||
for label_ptr in labels_slice.iter() {
|
||||
if !(*label_ptr).is_null() {
|
||||
drop(CString::from_raw((*label_ptr).cast_mut()));
|
||||
}
|
||||
}
|
||||
|
||||
let boxed_slice = Box::from_raw(std::ptr::from_mut::<[*const c_char]>(labels_slice));
|
||||
drop(boxed_slice);
|
||||
}
|
||||
|
||||
WalletFfiError::Success
|
||||
}
|
||||
@ -46,6 +46,7 @@ pub mod bridge;
|
||||
pub mod error;
|
||||
pub mod generic_transaction;
|
||||
pub mod keys;
|
||||
pub mod label;
|
||||
pub mod pinata;
|
||||
pub mod program_deployment;
|
||||
pub mod sync;
|
||||
|
||||
@ -9,7 +9,7 @@ use std::{
|
||||
|
||||
use lee::{Data, ProgramId, SharedSecretKey};
|
||||
use lee_core::{encryption::MlKem768EncapsulationKey, NullifierPublicKey};
|
||||
use wallet::AccountIdentity;
|
||||
use wallet::{account::AccountIdWithPrivacy, AccountIdentity};
|
||||
|
||||
use crate::error::WalletFfiError;
|
||||
|
||||
@ -24,7 +24,7 @@ pub struct WalletHandle {
|
||||
|
||||
/// 32-byte array type for `AccountId`, keys, hashes, etc.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
|
||||
pub struct FfiBytes32 {
|
||||
pub data: [u8; 32],
|
||||
}
|
||||
@ -593,6 +593,38 @@ impl From<FfiProgramId> for ProgramId {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub struct FfiAccountIdWithPrivacy {
|
||||
pub account_id: FfiBytes32,
|
||||
pub is_private: bool,
|
||||
}
|
||||
|
||||
impl From<AccountIdWithPrivacy> for FfiAccountIdWithPrivacy {
|
||||
fn from(value: AccountIdWithPrivacy) -> Self {
|
||||
match value {
|
||||
AccountIdWithPrivacy::Public(acc) => Self {
|
||||
account_id: acc.into(),
|
||||
is_private: false,
|
||||
},
|
||||
AccountIdWithPrivacy::Private(acc) => Self {
|
||||
account_id: acc.into(),
|
||||
is_private: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FfiAccountIdWithPrivacy> for AccountIdWithPrivacy {
|
||||
fn from(value: FfiAccountIdWithPrivacy) -> Self {
|
||||
if value.is_private {
|
||||
Self::Private(value.account_id.into())
|
||||
} else {
|
||||
Self::Public(value.account_id.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lee::{AccountId, PrivateKey, PublicKey};
|
||||
|
||||
@ -299,6 +299,27 @@ typedef struct FfiPublicAccountKey {
|
||||
struct FfiBytes32 public_key;
|
||||
} FfiPublicAccountKey;
|
||||
|
||||
typedef struct LabelAvailability {
|
||||
bool is_available;
|
||||
enum WalletFfiError error;
|
||||
} LabelAvailability;
|
||||
|
||||
typedef struct FfiAccountIdWithPrivacy {
|
||||
struct FfiBytes32 account_id;
|
||||
bool is_private;
|
||||
} FfiAccountIdWithPrivacy;
|
||||
|
||||
typedef struct AccountIdResolvedFromLabel {
|
||||
struct FfiAccountIdWithPrivacy account_id;
|
||||
enum WalletFfiError error;
|
||||
} AccountIdResolvedFromLabel;
|
||||
|
||||
typedef struct LabelList {
|
||||
const char **labels_data;
|
||||
uintptr_t labels_size;
|
||||
enum WalletFfiError error;
|
||||
} LabelList;
|
||||
|
||||
typedef struct FfiCreateWalletOutput {
|
||||
struct WalletHandle *wallet;
|
||||
/**
|
||||
@ -807,6 +828,92 @@ enum WalletFfiError wallet_ffi_resolve_private_account(struct WalletHandle *hand
|
||||
*/
|
||||
void wallet_ffi_free_account_identity(struct FfiAccountIdentity *account_identity);
|
||||
|
||||
/**
|
||||
* Check if label is available.
|
||||
*
|
||||
* # Parameters
|
||||
* - `handle`: Valid wallet handle
|
||||
* - `label`: Input null terminated C string for a label
|
||||
*
|
||||
* # Returns
|
||||
* - `LabelAvailability` struct
|
||||
*
|
||||
* # Safety
|
||||
* - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
* - `label` must be a valid pointer to a null-terminated C string
|
||||
*/
|
||||
struct LabelAvailability wallet_ffi_check_label_available(struct WalletHandle *handle,
|
||||
const char *label);
|
||||
|
||||
/**
|
||||
* Add new label.
|
||||
*
|
||||
* # Parameters
|
||||
* - `handle`: Valid wallet handle
|
||||
* - `label`: Input null terminated C string for a label
|
||||
* - `account_id_with_privacy`: The account ID (32 bytes) and its privacy.
|
||||
*
|
||||
* # Returns
|
||||
* - `Success` on successful query
|
||||
* - Error code on failure
|
||||
*
|
||||
* # Safety
|
||||
* - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
* - `label` must be a valid pointer to a null-terminated C string
|
||||
*/
|
||||
enum WalletFfiError wallet_ffi_add_label(struct WalletHandle *handle,
|
||||
const char *label,
|
||||
struct FfiAccountIdWithPrivacy account_id_with_privacy);
|
||||
|
||||
/**
|
||||
* Resolve a label.
|
||||
*
|
||||
* # Parameters
|
||||
* - `handle`: Valid wallet handle
|
||||
* - `label`: Input null terminated C string for a label
|
||||
*
|
||||
* # Returns
|
||||
* - `AccountIdResolvedFromLabel` struct
|
||||
*
|
||||
* # Safety
|
||||
* - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
* - `label` must be a valid pointer to a null-terminated C string
|
||||
*/
|
||||
struct AccountIdResolvedFromLabel wallet_ffi_resolve_label(struct WalletHandle *handle,
|
||||
const char *label);
|
||||
|
||||
/**
|
||||
* Get all labels for account.
|
||||
*
|
||||
* # Parameters
|
||||
* - `handle`: Valid wallet handle
|
||||
* - `account_id_with_privacy`: The account ID (32 bytes) and its privacy.
|
||||
*
|
||||
* # Returns
|
||||
* - `LabelList` struct
|
||||
*
|
||||
* # Safety
|
||||
* - `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`
|
||||
*/
|
||||
struct LabelList wallet_ffi_get_all_labels_for_account(struct WalletHandle *handle,
|
||||
struct FfiAccountIdWithPrivacy account_id_with_privacy);
|
||||
|
||||
/**
|
||||
* Free label list.
|
||||
*
|
||||
* # Parameters
|
||||
* - `label_list`: Input list of labels
|
||||
*
|
||||
* # Returns
|
||||
* - `Success` on successful query
|
||||
* - Error code on failure
|
||||
*
|
||||
* # Safety
|
||||
* - `label_list` must be a valid pointer to `LabelList`, received from
|
||||
* `wallet_ffi_get_all_labels_for_account`
|
||||
*/
|
||||
enum WalletFfiError wallet_ffi_free_label_list(struct LabelList *label_list);
|
||||
|
||||
/**
|
||||
* Claim a pinata reward using a public transaction.
|
||||
*
|
||||
|
||||
@ -21,6 +21,12 @@ impl Label {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Label {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Label {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user