fix(wallet_ffi): wallet_ffi fix

This commit is contained in:
Pravdyvy 2026-07-10 16:21:03 +03:00
parent 562696929c
commit 675e845237
26 changed files with 323 additions and 155 deletions

View File

@ -27,7 +27,7 @@ use wallet::WalletCore;
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the program binary
@ -59,7 +59,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();

View File

@ -23,7 +23,7 @@ use wallet::{AccountIdentity, WalletCore};
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let mut wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the program binary

View File

@ -27,7 +27,7 @@ use wallet::WalletCore;
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the program binary
@ -55,7 +55,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();

View File

@ -26,7 +26,7 @@ use wallet::{AccountIdentity, WalletCore};
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let mut wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the simple_tail_call program binary

View File

@ -29,7 +29,7 @@ use wallet::WalletCore;
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let mut wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the program binary
@ -52,7 +52,8 @@ async fn main() {
.storage()
.key_chain()
.pub_account_signing_key(account_id)
.expect("Input account should be a self owned public account");
.expect("Input account should be a self owned public account")
.clone();
// Define the desired greeting in ASCII
let greeting: Vec<u8> = vec![72, 111, 108, 97, 32, 109, 117, 110, 100, 111, 33];
@ -63,7 +64,7 @@ async fn main() {
.get_accounts_nonces(vec![account_id])
.await
.expect("Node should be reachable to query account data");
let signing_keys = [signing_key];
let signing_keys = [&signing_key];
let message = Message::try_new(program.id(), vec![account_id], nonces, greeting).unwrap();
// Pass the signing key to sign the message. This will be used by the node
// to flag the pre_state as `is_authorized` when executing the program
@ -72,7 +73,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();

View File

@ -35,7 +35,7 @@ const PDA_SEED: PdaSeed = PdaSeed::new([37; 32]);
#[tokio::main]
async fn main() {
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let wallet_core = WalletCore::from_env().await.unwrap();
// Parse arguments
// First argument is the path to the program binary
@ -57,7 +57,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();

View File

@ -66,7 +66,7 @@ async fn main() {
let program = Program::new(bytecode.into()).unwrap();
// Initialize wallet
let wallet_core = WalletCore::from_env().unwrap();
let mut wallet_core = WalletCore::from_env().await.unwrap();
match cli.command {
Command::WritePublic {
@ -88,7 +88,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();
@ -127,7 +127,7 @@ async fn main() {
// Submit the transaction
let _response = wallet_core
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::Public(tx))
.await
.unwrap();

View File

@ -440,7 +440,7 @@ async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Res
// Now claim funds from vault back to recipient
let nonces = ctx
.wallet()
.wallet_mut()
.get_accounts_nonces(vec![recipient_id])
.await
.context("Failed to get nonce for vault claim")?;

View File

@ -37,7 +37,7 @@ use wallet::{AccountIdentity, WalletCore};
reason = "test helper — grouping args would obscure intent"
)]
async fn fund_private_pda(
wallet: &WalletCore,
wallet: &mut WalletCore,
sender: AccountId,
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
@ -91,7 +91,7 @@ async fn fund_private_pda(
let tx = PrivacyPreservingTransaction::new(message, witness_set);
wallet
.sequencer_client
.leader_owned()
.send_transaction(LeeTransaction::PrivacyPreserving(tx))
.await
.map_err(|e| anyhow::anyhow!("send transaction failed: {e}"))?;
@ -107,7 +107,7 @@ async fn fund_private_pda(
reason = "test helper — grouping args would obscure intent"
)]
async fn spend_private_pda(
wallet: &WalletCore,
wallet: &mut WalletCore,
pda_account_id: AccountId,
recipient_npk: NullifierPublicKey,
recipient_vpk: ViewingPublicKey,
@ -180,7 +180,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
info!("Sending to alice_pda_0 (identifier=0)");
fund_private_pda(
ctx.wallet(),
ctx.wallet_mut(),
sender_0,
alice_npk,
alice_vpk.clone(),
@ -194,7 +194,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
info!("Sending to alice_pda_1 (identifier=1)");
fund_private_pda(
ctx.wallet(),
ctx.wallet_mut(),
sender_1,
alice_npk,
alice_vpk.clone(),
@ -262,7 +262,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
info!("Alice spending from alice_pda_0");
spend_private_pda(
ctx.wallet(),
ctx.wallet_mut(),
alice_pda_0_id,
recipient_npk_0,
recipient_vpk_0,
@ -275,7 +275,7 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
info!("Alice spending from alice_pda_1");
spend_private_pda(
ctx.wallet(),
ctx.wallet_mut(),
alice_pda_1_id,
recipient_npk_1,
recipient_vpk_1,

View File

@ -31,7 +31,10 @@ async fn deploy_and_execute_program() -> Result<()> {
let account_id = new_account(&mut ctx, false, None).await?;
let nonces = ctx.wallet().get_accounts_nonces(vec![account_id]).await?;
let nonces = ctx
.wallet_mut()
.get_accounts_nonces(vec![account_id])
.await?;
let private_key = ctx
.wallet()
.get_account_public_signing_key(account_id)

View File

@ -1794,7 +1794,7 @@ fn test_wallet_ffi_transfer_generic_private() -> Result<()> {
#[test]
fn test_wallet_ffi_vault_balance_and_claim_public() -> Result<()> {
let ctx = BlockingTestContext::new()?;
let mut ctx = BlockingTestContext::new()?;
let home = tempfile::tempdir()?;
let FfiCreateWalletOutput {
wallet: wallet_ffi_handle,
@ -1808,7 +1808,7 @@ fn test_wallet_ffi_vault_balance_and_claim_public() -> Result<()> {
// Fund the owner's vault, simulating an L1 bridge deposit.
ctx.block_on(|ctx| async move {
Vault(ctx.wallet())
Vault(ctx.wallet_mut())
.send_transfer(sender, owner, amount)
.await
})
@ -1894,7 +1894,7 @@ fn test_wallet_ffi_vault_balance_and_claim_private() -> Result<()> {
// Fund the owner's vault. Real deposits always land via a public transfer (the bridge
// program crediting the vault PDA), regardless of whether the owner is private.
ctx.block_on(|ctx| async move {
Vault(ctx.wallet())
Vault(ctx.wallet_mut())
.send_transfer(sender, owner, amount)
.await
})

View File

@ -323,7 +323,7 @@ pub unsafe extern "C" fn wallet_ffi_get_balance(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -389,7 +389,7 @@ pub unsafe extern "C" fn wallet_ffi_get_account_public(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));

View File

@ -55,7 +55,7 @@ pub unsafe extern "C" fn wallet_ffi_bridge_withdraw(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -66,7 +66,7 @@ pub unsafe extern "C" fn wallet_ffi_bridge_withdraw(
let from_id = AccountId::new(unsafe { (*from).data });
let bedrock_account_pk = unsafe { (*bedrock_account_pk).data };
let bridge = Bridge(&wallet);
let mut bridge = Bridge(&mut wallet);
match block_on(bridge.send_withdraw(from_id, amount, bedrock_account_pk)) {
Ok(tx_hash) => {

View File

@ -237,7 +237,7 @@ pub unsafe extern "C" fn wallet_ffi_send_generic_public_transaction(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -329,7 +329,7 @@ pub unsafe extern "C" fn wallet_ffi_send_generic_private_transaction(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));

View File

@ -60,7 +60,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -72,7 +72,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata(
let winner_id = AccountId::new(unsafe { (*winner_account_id).data });
let solution = u128::from_le_bytes(unsafe { *solution });
let pinata = Pinata(&wallet);
let mut pinata = Pinata(&mut wallet);
match block_on(pinata.claim(pinata_id, winner_id, solution)) {
Ok(tx_hash) => {
@ -155,7 +155,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata_private_owned_already_initializ
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -174,7 +174,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata_private_owned_already_initializ
};
let proof: MembershipProof = (winner_proof_index, siblings);
let pinata = Pinata(&wallet);
let mut pinata = Pinata(&mut wallet);
match block_on(
pinata
@ -250,7 +250,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata_private_owned_not_initialized(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -262,7 +262,7 @@ pub unsafe extern "C" fn wallet_ffi_claim_pinata_private_owned_not_initialized(
let winner_id = AccountId::new(unsafe { (*winner_account_id).data });
let solution = u128::from_le_bytes(unsafe { *solution });
let pinata = Pinata(&wallet);
let mut pinata = Pinata(&mut wallet);
match block_on(pinata.claim_private_owned_account(pinata_id, winner_id, solution)) {
Ok((tx_hash, _shared_key)) => {

View File

@ -1,9 +1,5 @@
use std::{ffi::CString, ptr, slice};
use common::transaction::LeeTransaction;
use lee::ProgramDeploymentTransaction;
use sequencer_service_rpc::RpcClient as _;
use crate::{
block_on,
error::{print_error, WalletFfiError},
@ -50,7 +46,7 @@ pub unsafe extern "C" fn wallet_ffi_program_deployment(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -60,14 +56,7 @@ pub unsafe extern "C" fn wallet_ffi_program_deployment(
let elf = unsafe { slice::from_raw_parts(elf_data, elf_size) }.to_vec();
let message = lee::program_deployment_transaction::Message::new(elf);
let transaction = ProgramDeploymentTransaction::new(message);
match block_on(
wallet
.sequencer_client
.send_transaction(LeeTransaction::ProgramDeployment(transaction)),
) {
match block_on(wallet.send_program_deployment_transaction(elf)) {
Ok(tx_hash) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);

View File

@ -1,7 +1,5 @@
//! Block synchronization functions.
use sequencer_service_rpc::RpcClient as _;
use crate::{
block_on,
error::{print_error, WalletFfiError},
@ -128,7 +126,7 @@ pub unsafe extern "C" fn wallet_ffi_get_current_block_height(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -136,7 +134,7 @@ pub unsafe extern "C" fn wallet_ffi_get_current_block_height(
}
};
match block_on(wallet.sequencer_client.get_last_block_id()) {
match block_on(wallet.get_last_block_id()) {
Ok(last_block_id) => {
unsafe {
*out_block_height = last_block_id;

View File

@ -72,7 +72,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_public(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -84,7 +84,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_public(
let to_id = AccountId::new(unsafe { (*to).data });
let amount = u128::from_le_bytes(unsafe { *amount });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_public_transfer(
AccountIdentity::Public(from_id),
@ -164,7 +164,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -188,7 +188,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded(
CliAccountMention::KeyPath,
);
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_shielded_transfer_to_outer_account(
from_mention.into_public_identity(from_id),
@ -262,7 +262,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_deshielded(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -273,7 +273,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_deshielded(
let from_id = AccountId::new(unsafe { (*from).data });
let to_id = AccountId::new(unsafe { (*to).data });
let amount = u128::from_le_bytes(unsafe { *amount });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount)) {
Ok((tx_hash, _shared_key)) => {
@ -348,7 +348,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -367,7 +367,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private(
};
let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data });
let amount = u128::from_le_bytes(unsafe { *amount });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_private_transfer_to_outer_account(
from_id,
@ -445,7 +445,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -461,7 +461,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned(
CliAccountMention::KeyPath,
);
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_shielded_transfer(
from_mention.into_public_identity(from_id),
@ -536,7 +536,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private_owned(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -547,7 +547,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private_owned(
let from_id = AccountId::new(unsafe { (*from).data });
let to_id = AccountId::new(unsafe { (*to).data });
let amount = u128::from_le_bytes(unsafe { *amount });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.send_private_transfer_to_owned_account(from_id, to_id, amount)) {
Ok((tx_hash, _shared_keys)) => {
@ -608,7 +608,7 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -618,7 +618,7 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account(
let account_id = AccountId::new(unsafe { (*account_id).data });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.register_account(AccountIdentity::Public(account_id))) {
Ok(tx_hash) => {
@ -679,7 +679,7 @@ pub unsafe extern "C" fn wallet_ffi_register_private_account(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -688,7 +688,7 @@ pub unsafe extern "C" fn wallet_ffi_register_private_account(
};
let account_id = AccountId::new(unsafe { (*account_id).data });
let transfer = NativeTokenTransfer(&wallet);
let mut transfer = NativeTokenTransfer(&mut wallet);
match block_on(transfer.register_account_private(account_id)) {
Ok((tx_hash, _secret)) => {

View File

@ -48,7 +48,7 @@ pub unsafe extern "C" fn wallet_ffi_get_vault_balance(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -113,7 +113,7 @@ pub unsafe extern "C" fn wallet_ffi_vault_claim(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -124,7 +124,7 @@ pub unsafe extern "C" fn wallet_ffi_vault_claim(
let owner_id = AccountId::new(unsafe { (*owner).data });
let amount = u128::from_le_bytes(unsafe { *amount });
match block_on(Vault(&wallet).send_claim(owner_id, amount)) {
match block_on(Vault(&mut wallet).send_claim(owner_id, amount)) {
Ok(tx_hash) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);
@ -185,7 +185,7 @@ pub unsafe extern "C" fn wallet_ffi_vault_claim_private(
return WalletFfiError::NullPointer;
}
let wallet = match wrapper.core.lock() {
let mut wallet = match wrapper.core.lock() {
Ok(w) => w,
Err(e) => {
print_error(format!("Failed to lock wallet: {e}"));
@ -196,7 +196,7 @@ pub unsafe extern "C" fn wallet_ffi_vault_claim_private(
let owner_id = AccountId::new(unsafe { (*owner).data });
let amount = u128::from_le_bytes(unsafe { *amount });
match block_on(Vault(&wallet).send_claim_private_owner(owner_id, amount)) {
match block_on(Vault(&mut wallet).send_claim_private_owner(owner_id, amount)) {
Ok((tx_hash, _shared_key)) => {
let tx_hash = CString::new(tx_hash.to_string())
.map_or(ptr::null_mut(), std::ffi::CString::into_raw);

View File

@ -86,6 +86,7 @@ fn c_str_to_path(ptr: *const c_char, name: &str) -> Result<PathBuf, WalletFfiErr
/// # Parameters
/// - `config_path`: Path to the wallet configuration file (JSON)
/// - `storage_path`: Path where wallet data will be stored
/// - `metrics_path`: Path to the wallet metrics file (JSON)
/// - `password`: Password for encrypting the wallet seed
///
/// # Returns
@ -98,6 +99,7 @@ fn c_str_to_path(ptr: *const c_char, name: &str) -> Result<PathBuf, WalletFfiErr
pub unsafe extern "C" fn wallet_ffi_create_new(
config_path: *const c_char,
storage_path: *const c_char,
metrics_path: *const c_char,
password: *const c_char,
) -> FfiCreateWalletOutput {
let Ok(config_path) = c_str_to_path(config_path, "config_path") else {
@ -112,7 +114,17 @@ pub unsafe extern "C" fn wallet_ffi_create_new(
return FfiCreateWalletOutput::default();
};
match WalletCore::new_init_storage(config_path, storage_path, None, &password) {
let Ok(metrics_path) = c_str_to_path(metrics_path, "metrics_path") else {
return FfiCreateWalletOutput::default();
};
match block_on(WalletCore::new_init_storage(
config_path,
storage_path,
metrics_path,
None,
&password,
)) {
Ok((core, mnemonic)) => {
let wrapper = Box::new(WalletWrapper {
core: Mutex::new(core),
@ -142,8 +154,9 @@ pub unsafe extern "C" fn wallet_ffi_create_new(
/// This loads a wallet that was previously created with `wallet_ffi_create_new()`.
///
/// # Parameters
/// - `handle` - Valid wallet handle
/// - `config_path`: Path to the wallet configuration file (JSON)
/// - `storage_path`: Path where wallet data is stored
/// - `metrics_path`: Path to the wallet metrics file (JSON)
///
/// # Returns
/// - Opaque wallet handle on success
@ -151,10 +164,12 @@ pub unsafe extern "C" fn wallet_ffi_create_new(
///
/// # Safety
/// All string parameters must be valid null-terminated UTF-8 strings.
/// `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_open(
config_path: *const c_char,
storage_path: *const c_char,
metrics_path: *const c_char,
) -> *mut WalletHandle {
let Ok(config_path) = c_str_to_path(config_path, "config_path") else {
return ptr::null_mut();
@ -164,7 +179,16 @@ pub unsafe extern "C" fn wallet_ffi_open(
return ptr::null_mut();
};
match WalletCore::new_update_chain(config_path, storage_path, None) {
let Ok(metrics_path) = c_str_to_path(metrics_path, "metrics_path") else {
return ptr::null_mut();
};
match block_on(WalletCore::new_update_chain(
config_path,
storage_path,
metrics_path,
None,
)) {
Ok(core) => {
let wrapper = Box::new(WalletWrapper {
core: Mutex::new(core),
@ -334,7 +358,7 @@ pub unsafe extern "C" fn wallet_ffi_get_sequencer_addr(handle: *mut WalletHandle
}
};
let addr = wallet.config().sequencer_addr.clone().to_string();
let addr = wallet.leader_url().to_string();
match std::ffi::CString::new(addr) {
Ok(s) => s.into_raw(),

View File

@ -1564,6 +1564,7 @@ enum WalletFfiError wallet_ffi_vault_claim_private(struct WalletHandle *handle,
* # Parameters
* - `config_path`: Path to the wallet configuration file (JSON)
* - `storage_path`: Path where wallet data will be stored
* - `metrics_path`: Path to the wallet metrics file (JSON)
* - `password`: Password for encrypting the wallet seed
*
* # Returns
@ -1575,6 +1576,7 @@ enum WalletFfiError wallet_ffi_vault_claim_private(struct WalletHandle *handle,
*/
struct FfiCreateWalletOutput wallet_ffi_create_new(const char *config_path,
const char *storage_path,
const char *metrics_path,
const char *password);
/**
@ -1583,8 +1585,9 @@ struct FfiCreateWalletOutput wallet_ffi_create_new(const char *config_path,
* This loads a wallet that was previously created with `wallet_ffi_create_new()`.
*
* # Parameters
* - `handle` - Valid wallet handle
* - `config_path`: Path to the wallet configuration file (JSON)
* - `storage_path`: Path where wallet data is stored
* - `metrics_path`: Path to the wallet metrics file (JSON)
*
* # Returns
* - Opaque wallet handle on success
@ -1592,8 +1595,11 @@ struct FfiCreateWalletOutput wallet_ffi_create_new(const char *config_path,
*
* # Safety
* All string parameters must be valid null-terminated UTF-8 strings.
* `handle` must be a valid wallet handle from `wallet_ffi_create_new` or `wallet_ffi_open`.
*/
struct WalletHandle *wallet_ffi_open(const char *config_path, const char *storage_path);
struct WalletHandle *wallet_ffi_open(const char *config_path,
const char *storage_path,
const char *metrics_path);
/**
* Destroy a wallet handle and free its resources.

View File

@ -211,6 +211,10 @@ impl WalletCore {
self.multi_sequencer_client.leader.clone()
}
pub fn leader_url(&self) -> Url {
self.multi_sequencer_client.leader_url.clone()
}
/// Get storage.
#[must_use]
pub const fn storage(&self) -> &Storage {

View File

@ -158,10 +158,9 @@ pub async fn callibration(client: SequencerClient) -> Metrics {
// Precision loss if fine there
let sample_size = latencies.len();
let latency_avg = (latencies.iter().fold(0, |acc, x| acc + x) as f32) / (sample_size as f32);
let latency_var = (latencies.iter().fold(0f32, |acc, x| {
let latency_var = latencies.iter().fold(0f32, |acc, x| {
acc + ((*x as f32) - latency_avg) * ((*x as f32) - latency_avg)
}) / (sample_size as f32))
.sqrt();
}) / (sample_size as f32);
Metrics {
latency_avg,
@ -216,32 +215,16 @@ pub fn choose_leader(
})
.collect();
// Conservative assumption: least amount of errors is the best
let min_err_addr = client_vec.iter().fold(client_vec[0], |acc, x| {
let old_err_count = metrics.get(acc).unwrap().errors;
let new_err_count = metrics.get(*x).unwrap().errors;
if new_err_count < old_err_count {
*x
} else {
acc
}
// Get the lowest quartile in error distribution
client_vec.sort_by(|a, b| {
metrics
.get(*a)
.unwrap()
.errors
.cmp(&metrics.get(*b).unwrap().errors)
});
let min_err_count = metrics.get(min_err_addr).unwrap().errors;
// Sort out all latest clients
client_vec = client_vec
.iter()
.filter_map(|x| {
let err_count = metrics.get(*x).unwrap().errors;
if err_count == min_err_count {
Some(*x)
} else {
None
}
})
.collect();
client_vec = client_vec[..(client_vec.len() / 4)].to_vec();
// Choose clients with least latency and variance
let min_lat_var_addr = client_vec.iter().fold(client_vec[0], |acc, x| {
@ -250,10 +233,11 @@ pub fn choose_leader(
let new = metrics.get(*x).unwrap();
let (new_lat, new_var) = (new.latency_avg, new.latency_var);
// Client is better if it is strongly linearly better
if ((new_lat - new_var) > (old_lat - old_var))
&& ((new_lat + new_var) > (old_lat + old_var))
{
let new_std = new_var.sqrt();
let old_std = old_var.sqrt();
// Client is better if its averabe is better and variance does not make it worse
if (new_lat < old_lat) && ((new_lat + new_std) < (old_lat + old_std)) {
*x
} else {
acc
@ -283,32 +267,41 @@ struct CumulativeUpdates {
pub failure_count: u64,
pub latest_block_id: Option<u64>,
pub cumulative_latency: f32,
pub cumulative_latency_squares: f32,
pub additional_sample_size: usize,
}
fn cumulative_updates(metric_updates: &[MetricsUpdate]) -> CumulativeUpdates {
let (failure_count, latest_block_id, cumulative_latency) =
metric_updates.iter().fold((0u64, None, 0f32), |acc, x| {
let MetricsUpdate {
latency,
new_latest_block_id,
is_failed,
} = x;
(
if *is_failed { acc.0 + 1 } else { acc.0 },
match (acc.1, new_latest_block_id) {
(None, None) => None,
(None, Some(val)) | (Some(val), None) => Some(val),
(Some(val_old), Some(val_new)) => Some(std::cmp::max(val_old, val_new)),
},
if !*is_failed { acc.2 + latency } else { acc.2 },
)
});
let (failure_count, latest_block_id, cumulative_latency, cumulative_latency_squares) =
metric_updates
.iter()
.fold((0u64, None, 0f32, 0_f32), |acc, x| {
let MetricsUpdate {
latency,
new_latest_block_id,
is_failed,
} = x;
(
if *is_failed { acc.0 + 1 } else { acc.0 },
match (acc.1, new_latest_block_id) {
(None, None) => None,
(None, Some(val)) | (Some(val), None) => Some(val),
(Some(val_old), Some(val_new)) => Some(std::cmp::max(val_old, val_new)),
},
if !*is_failed { acc.2 + latency } else { acc.2 },
if !*is_failed {
acc.3 + latency * latency
} else {
acc.3
},
)
});
CumulativeUpdates {
failure_count,
latest_block_id: latest_block_id.copied(),
cumulative_latency,
cumulative_latency_squares,
additional_sample_size: metric_updates.len() - (failure_count as usize),
}
}
@ -319,19 +312,24 @@ fn cumulative_avg(
orig_size_f: f32,
mod_size_f: f32,
) -> f32 {
(latency_avg_old * orig_size_f + cumulative_latency) / mod_size_f
(latency_avg_old * orig_size_f + cumulative_latency) / (orig_size_f + mod_size_f)
}
fn cumulative_var(
latency_avg_old: f32,
latency_avg_new: f32,
latency_var: f32,
cumulative_latency: f32,
cumulative_latency_squares: f32,
orig_size_f: f32,
mod_size_f: f32,
) -> f32 {
(latency_avg_old - latency_avg_new)
* ((3f32 * latency_avg_old + latency_avg_new) * orig_size_f / mod_size_f)
+ (latency_var * latency_var) * orig_size_f / mod_size_f
(latency_var * orig_size_f
+ (latency_avg_new - latency_avg_old) * (latency_avg_new - latency_avg_old) * orig_size_f
+ cumulative_latency_squares
+ mod_size_f * (latency_avg_new * latency_avg_new)
- 2_f32 * cumulative_latency * latency_avg_new)
/ (orig_size_f + mod_size_f)
}
pub fn update_metrics(
@ -341,12 +339,13 @@ pub fn update_metrics(
) -> Result<(), anyhow::Error> {
let leader_metric = metrics
.get_mut(leader_url)
.ok_or(anyhow::anyhow!("Leader URL is in present in metrics"))?;
.ok_or(anyhow::anyhow!("Leader URL is not present in metrics"))?;
let CumulativeUpdates {
failure_count,
latest_block_id,
cumulative_latency,
cumulative_latency_squares,
additional_sample_size,
} = cumulative_updates(metric_updates);
@ -356,22 +355,25 @@ pub fn update_metrics(
}
let orig_size_f = leader_metric.sample_size as f32;
let mod_size_f = (leader_metric.sample_size + additional_sample_size) as f32;
let mod_size_f = additional_sample_size as f32;
let latency_avg_old = leader_metric.latency_avg;
let latency_avg_new =
cumulative_avg(latency_avg_old, cumulative_latency, orig_size_f, mod_size_f);
let latency_disp_new = cumulative_var(
let latency_var_new = cumulative_var(
latency_avg_old,
latency_avg_new,
leader_metric.latency_var,
cumulative_latency,
cumulative_latency_squares,
orig_size_f,
mod_size_f,
);
leader_metric.latency_avg = latency_avg_new;
leader_metric.latency_var = latency_disp_new.sqrt();
leader_metric.latency_var = latency_var_new;
leader_metric.sample_size += additional_sample_size;
Ok(())
}
@ -389,7 +391,14 @@ pub fn save_metrics_at_path_with_updates(
#[cfg(test)]
mod tests {
use crate::multi_client::{MetricsUpdate, cumulative_avg, cumulative_updates};
use std::collections::HashMap;
use url::Url;
use crate::multi_client::{
CumulativeUpdates, Metrics, MetricsUpdate, cumulative_avg, cumulative_updates,
cumulative_var, update_metrics,
};
#[test]
fn cumulative_updates_test() {
@ -411,25 +420,36 @@ mod tests {
},
];
let cumulative_updates = cumulative_updates(&metrics_updates_vec);
let CumulativeUpdates {
failure_count,
latest_block_id,
cumulative_latency,
cumulative_latency_squares,
additional_sample_size,
} = cumulative_updates(&metrics_updates_vec);
let epsilon = 0.01_f32;
assert_eq!(cumulative_updates.additional_sample_size, 2);
assert_eq!(cumulative_updates.failure_count, 1);
assert_eq!(cumulative_updates.latest_block_id, Some(16));
assert!((cumulative_updates.cumulative_latency - 215_f32).abs() < epsilon);
let sum_squared_manual = 100_f32 * 100_f32 + 115_f32 * 115_f32;
assert_eq!(additional_sample_size, 2);
assert_eq!(failure_count, 1);
assert_eq!(latest_block_id, Some(16));
assert!((cumulative_latency - 215_f32).abs() < epsilon);
assert!((cumulative_latency_squares - sum_squared_manual).abs() < epsilon);
}
#[test]
fn cumulative_avg_test() {
let mut sample = vec![100_f32; 20];
let mut sample = vec![100_f32; 40];
let old_sample_size_f = sample.len() as f32;
let old_avg = sample.iter().sum::<f32>() / old_sample_size_f;
let new_samples = vec![101_f32, 110_f32, 112_f32, 97_f32, 78_f32];
let mod_sample_size_f = new_samples.len() as f32 + old_sample_size_f;
let new_samples = vec![
101_f32, 110_f32, 112_f32, 97_f32, 78_f32, 25_f32, 75_f32, 189_f32, 120_f32, 50_f32,
];
let mod_sample_size_f = new_samples.len() as f32;
let cumulative = new_samples.iter().sum();
sample.extend_from_slice(&new_samples);
@ -442,4 +462,117 @@ mod tests {
assert!((new_avg_1 - new_avg_2).abs() < epsilon);
}
#[test]
fn cumulative_var_test() {
let mut sample = vec![100_f32; 40];
let old_sample_size_f = sample.len() as f32;
let old_avg = sample.iter().sum::<f32>() / old_sample_size_f;
let old_var = sample
.iter()
.fold(0_f32, |acc, x| acc + (x - old_avg) * (x - old_avg))
/ old_sample_size_f;
let new_samples = vec![
101_f32, 110_f32, 112_f32, 97_f32, 78_f32, 25_f32, 75_f32, 189_f32, 120_f32, 50_f32,
];
let mod_sample_size_f = new_samples.len() as f32;
let cumulative = new_samples.iter().sum();
let cumulative_squares = new_samples.iter().fold(0_f32, |acc, x| acc + (*x) * (*x));
let new_avg = cumulative_avg(old_avg, cumulative, old_sample_size_f, mod_sample_size_f);
sample.extend_from_slice(&new_samples);
let new_var_1 = sample
.iter()
.fold(0_f32, |acc, x| acc + (x - new_avg) * (x - new_avg))
/ (sample.len() as f32);
let new_var_2 = cumulative_var(
old_avg,
new_avg,
old_var,
cumulative,
cumulative_squares,
old_sample_size_f,
mod_sample_size_f,
);
let epsilon = 0.01_f32;
assert!((new_var_1 - new_var_2).abs() < epsilon);
}
#[test]
fn metric_updates_correctness() {
let metrics_updates_vec = vec![
MetricsUpdate {
latency: 100_f32,
new_latest_block_id: Some(105),
is_failed: false,
},
MetricsUpdate {
latency: 115_f32,
new_latest_block_id: Some(106),
is_failed: false,
},
MetricsUpdate {
latency: 50_f32,
new_latest_block_id: None,
is_failed: true,
},
];
let addr_leader = Url::parse("https://127.0.0.1:3040").unwrap();
let leader_metrics = Metrics {
latency_avg: 100_f32,
latency_var: 25_f32,
sample_size: 10,
latest_block_id: 100,
errors: 5,
};
let cumulative_latency = 100_f32 + 115_f32;
let cumulative_latency_squares = 100_f32 * 100_f32 + 115_f32 * 115_f32;
let avg_manual = cumulative_avg(
leader_metrics.latency_avg,
cumulative_latency,
10_f32,
2_f32,
);
let var_manual = cumulative_var(
leader_metrics.latency_avg,
avg_manual,
leader_metrics.latency_var,
cumulative_latency,
cumulative_latency_squares,
10_f32,
2_f32,
);
let mut metric_map = HashMap::new();
metric_map.insert(addr_leader.clone(), leader_metrics);
update_metrics(&mut metric_map, &addr_leader, &metrics_updates_vec).unwrap();
let Metrics {
latency_avg,
latency_var,
sample_size,
latest_block_id,
errors,
} = metric_map.get(&addr_leader).unwrap();
let epsilon = 0.01_f32;
assert_eq!(*errors, 6);
assert_eq!(*latest_block_id, 106);
assert_eq!(*sample_size, 12);
assert!((*latency_avg - avg_manual).abs() < epsilon);
assert!((*latency_var - var_manual).abs() < epsilon);
}
}

View File

@ -8,7 +8,7 @@ use lee::{AccountId, PrivateKey, PublicKey};
use lee_core::Identifier;
use sequencer_core::config::{BedrockConfig, GenesisAction, SequencerConfig};
use url::Url;
use wallet::config::WalletConfig;
use wallet::config::{SequencerConnectionData, WalletConfig};
pub const INITIAL_PUBLIC_BALANCES_FOR_WALLET: [u128; 2] = [10_000, 20_000];
pub const INITIAL_PRIVATE_BALANCES_FOR_WALLET: [u128; 2] = [10_000, 20_000];
@ -157,13 +157,15 @@ pub fn genesis_from_accounts(
pub fn wallet_config(sequencer_addr: SocketAddr) -> Result<WalletConfig> {
Ok(WalletConfig {
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
.context("Failed to convert sequencer addr to URL")?,
sequencers_conn_data: vec![SequencerConnectionData {
sequencer_addr: addr_to_url(UrlProtocol::Http, sequencer_addr)
.context("Failed to convert sequencer addr to URL")?,
basic_auth: None,
}],
seq_poll_timeout: Duration::from_secs(30),
seq_tx_poll_max_blocks: 15,
seq_poll_max_retries: 10,
seq_block_poll_max_amount: 100,
basic_auth: None,
})
}

View File

@ -365,6 +365,7 @@ impl TestContextBuilder {
&initial_private_accounts,
wallet_config_overrides,
)
.await
.context("Failed to setup wallet")?;
setup_public_accounts_with_initial_supply(&mut wallet, &initial_public_accounts)
@ -420,15 +421,19 @@ impl BlockingTestContext {
self.ctx.as_ref().expect("TestContext is set")
}
pub fn ctx_mut(&mut self) -> &mut TestContext {
self.ctx.as_mut().expect("TestContext is set")
}
pub const fn runtime(&self) -> &tokio::runtime::Runtime {
&self.runtime
}
pub fn block_on<'ctx, F>(&'ctx self, f: impl FnOnce(&'ctx TestContext) -> F) -> F::Output
pub fn block_on<'ctx, F>(&'ctx mut self, f: impl FnOnce(&'ctx TestContext) -> F) -> F::Output
where
F: std::future::Future + 'ctx,
{
let future = f(self.ctx());
let future = f(self.ctx_mut());
self.runtime.block_on(future)
}

View File

@ -133,7 +133,7 @@ pub async fn setup_sequencer(
Ok((sequencer_handle, temp_sequencer_dir))
}
pub fn setup_wallet(
pub async fn setup_wallet(
sequencer_addr: SocketAddr,
initial_public_accounts: &[(PrivateKey, u128)],
initial_private_accounts: &[InitialPrivateAccountForWallet],
@ -151,14 +151,17 @@ pub fn setup_wallet(
.context("Failed to write wallet config in temp dir")?;
let storage_path = temp_wallet_dir.path().join("storage.json");
let metrics_path = temp_wallet_dir.path().join("metrics.json");
let wallet_password = "test_pass".to_owned();
let (mut wallet, _mnemonic) = WalletCore::new_init_storage(
config_path,
storage_path,
metrics_path,
Some(config_overrides),
&wallet_password,
)
.await
.context("Failed to init wallet")?;
for (private_key, _balance) in initial_public_accounts {