From 795e7178020b41bbda0510563e0ac0c2448eb359 Mon Sep 17 00:00:00 2001 From: Cifko Date: Thu, 29 Dec 2022 23:13:50 +0100 Subject: [PATCH] feat: wallet FFI cucumber --- Cargo.lock | 2 + base_layer/wallet_ffi/src/lib.rs | 59 ++ base_layer/wallet_ffi/wallet.h | 34 + integration_tests/Cargo.toml | 2 + integration_tests/tests/cucumber.rs | 763 +++++++++++++----- .../tests/features/WalletFFI.feature | 297 ++++--- integration_tests/tests/utils/ffi/balance.rs | 91 +++ .../tests/utils/ffi/callbacks.rs | 326 ++++++++ .../tests/utils/ffi/coin_preview.rs | 49 ++ .../tests/utils/ffi/comms_config.rs | 61 ++ .../tests/utils/ffi/completed_transaction.rs | 196 +++++ .../tests/utils/ffi/completed_transactions.rs | 67 ++ integration_tests/tests/utils/ffi/contact.rs | 88 ++ integration_tests/tests/utils/ffi/contacts.rs | 70 ++ .../tests/utils/ffi/contacts_liveness_data.rs | 104 +++ .../tests/utils/ffi/fee_per_gram_stat.rs | 91 +++ .../tests/utils/ffi/fee_per_gram_stats.rs | 67 ++ .../tests/utils/ffi/ffi_bytes.rs | 86 ++ .../tests/utils/ffi/ffi_import.rs | 590 ++++++++++++++ .../tests/utils/ffi/ffi_string.rs | 52 ++ integration_tests/tests/utils/ffi/kernel.rs | 79 ++ integration_tests/tests/utils/ffi/mod.rs | 76 ++ .../utils/ffi/pending_inbound_transaction.rs | 120 +++ .../utils/ffi/pending_inbound_transactions.rs | 68 ++ .../utils/ffi/pending_outbound_transaction.rs | 136 ++++ .../ffi/pending_outbound_transactions.rs | 67 ++ .../tests/utils/ffi/private_key.rs | 92 +++ .../tests/utils/ffi/public_key.rs | 98 +++ .../tests/utils/ffi/public_keys.rs | 68 ++ .../tests/utils/ffi/seed_words.rs | 103 +++ .../utils/ffi/transaction_send_status.rs | 55 ++ .../tests/utils/ffi/transport_config.rs | 56 ++ integration_tests/tests/utils/ffi/vector.rs | 58 ++ integration_tests/tests/utils/ffi/wallet.rs | 429 ++++++++++ .../tests/utils/ffi/wallet_address.rs | 110 +++ integration_tests/tests/utils/mod.rs | 2 + integration_tests/tests/utils/wallet_ffi.rs | 216 +++++ .../tests/utils/wallet_process.rs | 6 +- 38 files changed, 4591 insertions(+), 343 deletions(-) create mode 100644 integration_tests/tests/utils/ffi/balance.rs create mode 100644 integration_tests/tests/utils/ffi/callbacks.rs create mode 100644 integration_tests/tests/utils/ffi/coin_preview.rs create mode 100644 integration_tests/tests/utils/ffi/comms_config.rs create mode 100644 integration_tests/tests/utils/ffi/completed_transaction.rs create mode 100644 integration_tests/tests/utils/ffi/completed_transactions.rs create mode 100644 integration_tests/tests/utils/ffi/contact.rs create mode 100644 integration_tests/tests/utils/ffi/contacts.rs create mode 100644 integration_tests/tests/utils/ffi/contacts_liveness_data.rs create mode 100644 integration_tests/tests/utils/ffi/fee_per_gram_stat.rs create mode 100644 integration_tests/tests/utils/ffi/fee_per_gram_stats.rs create mode 100644 integration_tests/tests/utils/ffi/ffi_bytes.rs create mode 100644 integration_tests/tests/utils/ffi/ffi_import.rs create mode 100644 integration_tests/tests/utils/ffi/ffi_string.rs create mode 100644 integration_tests/tests/utils/ffi/kernel.rs create mode 100644 integration_tests/tests/utils/ffi/mod.rs create mode 100644 integration_tests/tests/utils/ffi/pending_inbound_transaction.rs create mode 100644 integration_tests/tests/utils/ffi/pending_inbound_transactions.rs create mode 100644 integration_tests/tests/utils/ffi/pending_outbound_transaction.rs create mode 100644 integration_tests/tests/utils/ffi/pending_outbound_transactions.rs create mode 100644 integration_tests/tests/utils/ffi/private_key.rs create mode 100644 integration_tests/tests/utils/ffi/public_key.rs create mode 100644 integration_tests/tests/utils/ffi/public_keys.rs create mode 100644 integration_tests/tests/utils/ffi/seed_words.rs create mode 100644 integration_tests/tests/utils/ffi/transaction_send_status.rs create mode 100644 integration_tests/tests/utils/ffi/transport_config.rs create mode 100644 integration_tests/tests/utils/ffi/vector.rs create mode 100644 integration_tests/tests/utils/ffi/wallet.rs create mode 100644 integration_tests/tests/utils/ffi/wallet_address.rs create mode 100644 integration_tests/tests/utils/wallet_ffi.rs diff --git a/Cargo.lock b/Cargo.lock index b4132ed430..c40cf9fd8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5461,6 +5461,7 @@ dependencies = [ "include_dir", "indexmap", "json5 0.2.8", + "libc", "libsqlite3-sys", "lmdb-zero", "log", @@ -5488,6 +5489,7 @@ dependencies = [ "tari_test_utils", "tari_utilities", "tari_wallet", + "tari_wallet_ffi", "tari_wallet_grpc_client", "tempfile", "thiserror", diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index f0f0127897..bbf780befc 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -4717,6 +4717,65 @@ pub unsafe extern "C" fn comms_list_connected_public_keys( } } +/// Gets the length of the public keys vector +/// +/// ## Arguments +/// `public_keys` - Pointer to TariPublicKeys +/// +/// ## Returns +/// `c_uint` - Length of the TariPublicKeys vector, 0 if is null +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn public_keys_get_length(public_keys: *const TariPublicKeys, error_out: *mut c_int) -> c_uint { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if public_keys.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public_keys".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*public_keys).0.len() as c_uint +} + +/// Gets a ByteVector at position in a EmojiSet +/// +/// ## Arguments +/// `public_keys` - The pointer to a TariPublicKeys +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `ByteVector` - Returns a ByteVector. Note that the ByteVector will be null if ptr +/// is null or if the position is invalid +/// +/// # Safety +/// The ```byte_vector_destroy``` function must be called when finished with the ByteVector to prevent a memory leak. +#[no_mangle] +pub unsafe extern "C" fn public_keys_get_at( + public_keys: *const TariPublicKeys, + position: c_uint, + error_out: *mut c_int, +) -> *mut TariPublicKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if public_keys.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public_keys".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let last_index = public_keys_get_length(public_keys, error_out) - 1; + if position > last_index { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let result = (*public_keys).0[position as usize].clone(); + Box::into_raw(Box::new(result)) +} + /// ---------------------------------------------------------------------------------------------- /// /// ------------------------------------- Wallet -------------------------------------------------/// diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index 8b180c1c57..5497b19784 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -2473,6 +2473,40 @@ void comms_config_destroy(TariCommsConfig *wc); struct TariPublicKeys *comms_list_connected_public_keys(struct TariWallet *wallet, int *error_out); +/** + * Gets the length of the public keys vector + * + * ## Arguments + * `public_keys` - Pointer to TariPublicKeys + * + * ## Returns + * `c_uint` - Length of the TariPublicKeys vector, 0 if is null + * + * # Safety + * None + */ +unsigned int public_keys_get_length(const struct TariPublicKeys *public_keys, int *error_out); + +/** + * Gets a ByteVector at position in a EmojiSet + * + * ## Arguments + * `public_keys` - The pointer to a TariPublicKeys + * `position` - The integer position + * `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions + * as an out parameter. + * + * ## Returns + * `ByteVector` - Returns a ByteVector. Note that the ByteVector will be null if ptr + * is null or if the position is invalid + * + * # Safety + * The ```byte_vector_destroy``` function must be called when finished with the ByteVector to prevent a memory leak. + */ +TariPublicKey *public_keys_get_at(const struct TariPublicKeys *public_keys, + unsigned int position, + int *error_out); + /** * Creates a TariWallet * diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index c1d87bb917..bb4aa70585 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -20,6 +20,7 @@ tari_test_utils = { path = "../infrastructure/test_utils" } tari_base_node = { path = "../applications/tari_base_node" } tari_console_wallet = { path = "../applications/tari_console_wallet" } tari_wallet = { path = "../base_layer/wallet" } +tari_wallet_ffi = { path = "../base_layer/wallet_ffi" } tari_common_types = { path = "../base_layer/common_types" } tari_script = { path = "../infrastructure/tari_script" } tari_utilities = { git = "https://github.com/tari-project/tari_utilities.git", tag="v0.4.10"} @@ -42,6 +43,7 @@ diesel = { version = "1.4.8", default-features = false, features = ["sqlite"] } futures = { version = "^0.3.1" } json5 = "0.2.2" include_dir = "0.7.2" +libc = "0.2.65" log = { version = "0.4.8", features = ["std"] } log4rs = { version = "1.1.1", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"] } lmdb-zero = "0.4.4" diff --git a/integration_tests/tests/cucumber.rs b/integration_tests/tests/cucumber.rs index 0d3382d0c4..f556bceae8 100644 --- a/integration_tests/tests/cucumber.rs +++ b/integration_tests/tests/cucumber.rs @@ -26,7 +26,9 @@ mod utils; use std::{ collections::VecDeque, convert::TryFrom, + io::BufRead, path::PathBuf, + ptr::null, str, sync::{Arc, Mutex}, time::Duration, @@ -80,6 +82,7 @@ use tari_wallet_grpc_client::grpc::{ }; use thiserror::Error; use tokio::runtime::Runtime; +use utils::wallet_ffi::create_seed_words; use crate::utils::{ base_node_process::{spawn_base_node, spawn_base_node_with_config, BaseNodeProcess}, @@ -93,6 +96,7 @@ use crate::utils::{ MinerProcess, }, transaction::{build_transaction_with_output, build_transaction_with_output_and_fee}, + wallet_ffi::{create_contact, get_mnemonic_word_list_for_language, spawn_wallet_ffi, WalletFFI}, wallet_process::{create_wallet_client, get_default_cli, spawn_wallet, WalletProcess}, }; @@ -108,6 +112,8 @@ pub enum TariWorldError { BaseNodeProcessNotFound(String), #[error("Wallet process not found: {0}")] WalletProcessNotFound(String), + #[error("FFIWallet not found: {0}")] + FFIWalletNotFound(String), #[error("Miner process not found: {0}")] MinerProcessNotFound(String), #[error("Base node error: {0}")] @@ -121,6 +127,7 @@ pub struct TariWorld { base_nodes: IndexMap, blocks: IndexMap, miners: IndexMap, + ffi_wallets: IndexMap, wallets: IndexMap, transactions: IndexMap, wallet_addresses: IndexMap, // values are strings representing tari addresses @@ -162,6 +169,30 @@ impl TariWorld { } } + async fn get_wallet_address>(&self, name: &S) -> anyhow::Result { + if let Some(address) = self.wallet_addresses.get(name.as_ref()) { + return Ok(address.clone()); + } + match self.get_wallet_client(name).await { + Ok(wallet) => { + let mut wallet = wallet; + + Ok(wallet + .get_address(Empty {}) + .await + .unwrap() + .into_inner() + .address + .to_hex()) + }, + Err(_) => { + let ffi_wallet = self.get_ffi_wallet(name).unwrap(); + + Ok(ffi_wallet.get_address().address().get_as_hex()) + }, + } + } + #[allow(dead_code)] async fn get_wallet_client>( &self, @@ -184,6 +215,20 @@ impl TariWorld { .ok_or_else(|| TariWorldError::WalletProcessNotFound(wallet_name.as_ref().to_string()))?) } + fn get_ffi_wallet>(&self, wallet_name: &S) -> anyhow::Result<&WalletFFI> { + Ok(self + .ffi_wallets + .get(wallet_name.as_ref()) + .ok_or_else(|| TariWorldError::FFIWalletNotFound(wallet_name.as_ref().to_string()))?) + } + + fn get_mut_ffi_wallet>(&mut self, wallet_name: &S) -> anyhow::Result<&mut WalletFFI> { + Ok(self + .ffi_wallets + .get_mut(wallet_name.as_ref()) + .ok_or_else(|| TariWorldError::FFIWalletNotFound(wallet_name.as_ref().to_string()))?) + } + fn get_miner>(&self, miner_name: S) -> anyhow::Result<&MinerProcess> { Ok(self .miners @@ -199,6 +244,7 @@ impl TariWorld { self.base_nodes.clear(); self.seed_nodes.clear(); self.wallets.clear(); + self.ffi_wallets.clear(); self.miners.clear(); } } @@ -492,7 +538,10 @@ async fn wallet_connected_to_base_node(world: &mut TariWorld, wallet: String, ba world .wallet_connected_to_base_node .insert(wallet.clone(), base_node.clone()); - spawn_wallet(world, wallet, Some(base_node), peer_seeds, None, None).await; + + let mut cli = get_default_cli(); + cli.seed_words_file_name = Some(PathBuf::new().join("seed_words.txt")); + spawn_wallet(world, wallet, Some(base_node), peer_seeds, None, Some(cli)).await; } #[when(expr = "mining node {word} mines {int} blocks with min difficulty {int} and max difficulty {int}")] @@ -1037,7 +1086,6 @@ async fn list_all_txs_for_wallet(world: &mut TariWorld, transaction_type: String #[then(expr = "wallet {word} has at least {int} transactions that are all {word} and not cancelled")] async fn wallet_has_at_least_num_txs(world: &mut TariWorld, wallet: String, num_txs: u64, transaction_status: String) { let mut client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let transaction_status = match transaction_status.as_str() { "TRANSACTION_STATUS_COMPLETED" => 0, "TRANSACTION_STATUS_BROADCAST" => 1, @@ -1055,7 +1103,6 @@ async fn wallet_has_at_least_num_txs(world: &mut TariWorld, wallet: String, num_ }; let num_retries = 100; - for _ in 0..num_retries { let mut txs = client .get_completed_transactions(grpc::GetCompletedTransactionsRequest {}) @@ -1135,6 +1182,7 @@ async fn wait_for_wallet_to_have_less_than_micro_tari(world: &mut TariWorld, wal } #[when(expr = "I have non-default wallet {word} connected to all seed nodes using {word}")] +#[given(expr = "I have non-default wallet {word} connected to all seed nodes using {word}")] async fn non_default_wallet_connected_to_all_seed_nodes(world: &mut TariWorld, wallet: String, mechanism: String) { let routing_mechanism = TransactionRoutingMechanism::from(mechanism); // assuming we have at least one base node as seed node, we use the first to connect wallet to @@ -1184,15 +1232,9 @@ async fn send_amount_from_source_wallet_to_dest_wallet_without_broadcast( fee: u64, ) { let mut source_client = create_wallet_client(world, source_wallet.clone()).await.unwrap(); - let source_wallet_address = source_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let source_wallet_address = world.get_wallet_address(&source_wallet).await.unwrap(); - let dest_wallet_address = world.wallet_addresses.get(&dest_wallet).unwrap(); + let dest_wallet_address = world.get_wallet_address(&dest_wallet).await.unwrap(); let payment_recipient = PaymentRecipient { address: dest_wallet_address.clone(), @@ -1250,22 +1292,9 @@ async fn send_one_sided_transaction_from_source_wallet_to_dest_wallt( fee: u64, ) { let mut source_client = create_wallet_client(world, source_wallet.clone()).await.unwrap(); - let source_wallet_address = source_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let source_wallet_address = world.get_wallet_address(&source_wallet).await.unwrap(); - let mut dest_client = create_wallet_client(world, dest_wallet.clone()).await.unwrap(); - let dest_wallet_address = dest_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let dest_wallet_address = world.get_wallet_address(&dest_wallet).await.unwrap(); let payment_recipient = PaymentRecipient { address: dest_wallet_address.clone(), @@ -1362,22 +1391,8 @@ async fn send_amount_from_wallet_to_wallet_at_fee( fee_per_gram: u64, ) { let mut sender_wallet_client = create_wallet_client(world, sender.clone()).await.unwrap(); - let sender_wallet_address = sender_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver_wallet_client = create_wallet_client(world, receiver.clone()).await.unwrap(); - let receiver_wallet_address = receiver_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let sender_wallet_address = world.get_wallet_address(&sender).await.unwrap(); + let receiver_wallet_address = world.get_wallet_address(&receiver).await.unwrap(); let payment_recipient = PaymentRecipient { address: receiver_wallet_address.clone(), @@ -1571,13 +1586,7 @@ async fn wallet_detects_at_least_unmined_transactions(world: &mut TariWorld, wal #[then(expr = "wallet {word} detects exactly {int} coinbase transactions as Mined_Confirmed")] async fn wallet_detects_exactly_coinbase_transactions(world: &mut TariWorld, wallet_name: String, coinbases: u64) { let mut client = create_wallet_client(world, wallet_name.clone()).await.unwrap(); - let wallet_address = client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let wallet_address = world.get_wallet_address(&wallet_name).await.unwrap(); let tx_ids = world.wallet_tx_ids.get(&wallet_address).unwrap(); let num_retries = 100; @@ -1720,13 +1729,7 @@ async fn base_node_is_at_same_height_as_node(world: &mut TariWorld, base_node: S #[then(expr = "while mining via SHA3 miner {word} all transactions in wallet {word} are found to be Mined_Confirmed")] async fn while_mining_all_txs_in_wallet_are_mined_confirmed(world: &mut TariWorld, miner: String, wallet: String) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let wallet_address = wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); let wallet_tx_ids = world.wallet_tx_ids.get(&wallet_address).unwrap(); if wallet_tx_ids.is_empty() { @@ -1782,6 +1785,7 @@ async fn stop_all_wallets(world: &mut TariWorld) { } #[then(expr = "I stop wallet {word}")] +#[when(expr = "I stop wallet {word}")] async fn stop_wallet(world: &mut TariWorld, wallet: String) { // conveniently, register wallet address let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); @@ -1827,13 +1831,7 @@ async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( wallet: String, ) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let wallet_address = wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); let wallet_tx_ids = world.wallet_tx_ids.get(&wallet_address).unwrap(); if wallet_tx_ids.is_empty() { @@ -1892,13 +1890,7 @@ async fn while_mining_in_node_all_txs_in_wallet_are_mined_confirmed( async fn all_wallets_detect_all_txs_as_mined_confirmed(world: &mut TariWorld) { for wallet in world.wallets.keys() { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let wallet_address = wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); let wallet_tx_ids = world.wallet_tx_ids.get(&wallet_address); let wallet_tx_ids = if wallet_tx_ids.is_none() { @@ -2032,23 +2024,8 @@ async fn send_num_transactions_to_wallets_at_fee( fee_per_gram: u64, ) { let mut sender_wallet_client = create_wallet_client(world, sender_wallet.clone()).await.unwrap(); - let sender_wallet_address = sender_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver_wallet_client = create_wallet_client(world, receiver_wallet.clone()).await.unwrap(); - let receiver_wallet_address = receiver_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - + let sender_wallet_address = world.get_wallet_address(&sender_wallet).await.unwrap(); + let receiver_wallet_address = world.get_wallet_address(&receiver_wallet).await.unwrap(); let mut tx_ids = vec![]; for _ in 0..num_txs { @@ -2302,22 +2279,8 @@ async fn all_nodes_are_at_product_height(world: &mut TariWorld, a: u64, b: u64) #[when(expr = "I transfer {int}T from {word} to {word}")] async fn transfer_tari_from_wallet_to_receiver(world: &mut TariWorld, amount: u64, sender: String, receiver: String) { let mut sender_wallet_client = create_wallet_client(world, sender.clone()).await.unwrap(); - let sender_wallet_address = sender_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver_wallet_client = create_wallet_client(world, receiver.clone()).await.unwrap(); - let receiver_wallet_address = receiver_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let sender_wallet_address = world.get_wallet_address(&sender).await.unwrap(); + let receiver_wallet_address = world.get_wallet_address(&receiver).await.unwrap(); let payment_recipient = PaymentRecipient { address: receiver_wallet_address.clone(), @@ -2508,31 +2471,9 @@ async fn transfer_from_wallet_to_two_recipients_at_fee( fee_per_gram: u64, ) { let mut sender_client = create_wallet_client(world, sender.clone()).await.unwrap(); - let sender_wallet_address = sender_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver1_client = create_wallet_client(world, receiver1.clone()).await.unwrap(); - let receiver1_address = receiver1_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver2_client = create_wallet_client(world, receiver2.clone()).await.unwrap(); - let receiver2_address = receiver2_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let sender_wallet_address = world.get_wallet_address(&sender).await.unwrap(); + let receiver1_address = world.get_wallet_address(&receiver1).await.unwrap(); + let receiver2_address = world.get_wallet_address(&receiver2).await.unwrap(); let payment_recipient1 = PaymentRecipient { address: receiver1_address.clone(), @@ -2661,13 +2602,7 @@ async fn transfer_from_wallet_to_two_recipients_at_fee( #[when(expr = "I transfer {int} uT to self from wallet {word} at fee {int}")] async fn transfer_tari_to_self(world: &mut TariWorld, amount: u64, sender: String, fee_per_gram: u64) { let mut sender_wallet_client = create_wallet_client(world, sender.clone()).await.unwrap(); - let sender_wallet_address = sender_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let sender_wallet_address = world.get_wallet_address(&sender).await.unwrap(); let payment_recipient = PaymentRecipient { address: sender_wallet_address.clone(), @@ -2745,22 +2680,8 @@ async fn transfer_tari_to_self(world: &mut TariWorld, amount: u64, sender: Strin #[when(expr = "I broadcast HTLC transaction with {int} uT from wallet {word} to wallet {word} at fee {int}")] async fn htlc_transaction(world: &mut TariWorld, amount: u64, sender: String, receiver: String, fee_per_gram: u64) { let mut sender_wallet_client = create_wallet_client(world, sender.clone()).await.unwrap(); - let sender_wallet_address = sender_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - - let mut receiver_wallet_client = create_wallet_client(world, receiver.clone()).await.unwrap(); - let receiver_wallet_address = receiver_wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); + let sender_wallet_address = world.get_wallet_address(&sender).await.unwrap(); + let receiver_wallet_address = world.get_wallet_address(&receiver).await.unwrap(); let payment_recipient = PaymentRecipient { address: receiver_wallet_address.clone(), @@ -2854,14 +2775,7 @@ async fn htlc_transaction(world: &mut TariWorld, amount: u64, sender: String, re #[when(expr = "I claim an HTLC refund transaction with wallet {word} at fee {int}")] async fn claim_htlc_refund_transaction_with_wallet_at_fee(world: &mut TariWorld, wallet: String, fee_per_gram: u64) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let wallet_address = wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - + let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); let output_hash = world.output_hash.clone().unwrap(); let claim_htlc_req = ClaimHtlcRefundRequest { @@ -2931,14 +2845,7 @@ async fn claim_htlc_refund_transaction_with_wallet_at_fee(world: &mut TariWorld, #[when(expr = "I claim an HTLC transaction with wallet {word} at fee {int}")] async fn wallet_claims_htlc_transaction_at_fee(world: &mut TariWorld, wallet: String, fee_per_gram: u64) { let mut wallet_client = create_wallet_client(world, wallet.clone()).await.unwrap(); - let wallet_address = wallet_client - .get_address(Empty {}) - .await - .unwrap() - .into_inner() - .address - .to_hex(); - + let wallet_address = world.get_wallet_address(&wallet).await.unwrap(); let output_hash = world.output_hash.clone().unwrap(); let pre_image = world.pre_image.clone().unwrap(); @@ -3971,6 +3878,480 @@ async fn print_world(world: &mut TariWorld) { eprintln!(); } +// FFI Steps +#[when(expr = "I have a ffi wallet {word} connected to base node {word}")] +#[then(expr = "I have a ffi wallet {word} connected to base node {word}")] +#[given(expr = "I have a ffi wallet {word} connected to base node {word}")] +async fn ffi_start_wallet_connected_to_base_node(world: &mut TariWorld, wallet: String, base_node: String) { + spawn_wallet_ffi(world, wallet.clone(), null()); + let base_node = world.get_node(&base_node).unwrap(); + world.get_ffi_wallet(&wallet).unwrap().add_base_node( + base_node.identity.public_key().to_hex(), + base_node.identity.public_address().to_string(), + ); +} + +#[given(expr = "I have a ffi wallet {word} connected to seed node {word}")] +async fn ffi_start_wallet_connected_to_seed_node(world: &mut TariWorld, wallet: String, seed_node: String) { + spawn_wallet_ffi(world, wallet.clone(), null()); + assert!(world.all_seed_nodes().contains(&seed_node), "Seed node not found."); + let seed_node = world.get_node(&seed_node).unwrap(); + world.get_ffi_wallet(&wallet).unwrap().add_base_node( + seed_node.identity.public_key().to_hex(), + seed_node.identity.public_address().to_string(), + ); +} + +#[given(expr = "I set base node {word} for ffi wallet {word}")] +async fn ffi_set_base_node(world: &mut TariWorld, base_node: String, wallet: String) { + let base_node = world.get_node(&base_node).unwrap(); + world.get_ffi_wallet(&wallet).unwrap().add_base_node( + base_node.identity.public_key().to_hex(), + base_node.identity.public_address().to_string(), + ); +} + +#[then(expr = "I want to get public key of ffi wallet {word}")] +async fn ffi_get_public_key(world: &mut TariWorld, wallet: String) { + let wallet = world.get_ffi_wallet(&wallet).unwrap(); + let public_key = wallet.identify(); + println!("public_key {}", public_key); +} + +#[then(expr = "I want to get emoji id of ffi wallet {word}")] +async fn ffi_get_emoji_id(world: &mut TariWorld, wallet: String) { + let wallet = world.get_ffi_wallet(&wallet).unwrap(); + let emoji_id = wallet.get_emoji_id(); + assert_eq!( + emoji_id.len(), + 132, + "Emoji id {} is expected to be of length 132", + emoji_id + ); +} + +#[then(expr = "I stop ffi wallet {word}")] +async fn ffi_stop_wallet(world: &mut TariWorld, wallet: String) { + let address = world.get_wallet_address(&wallet).await.unwrap(); + let ffi_wallet = world.ffi_wallets.get_mut(&wallet).unwrap(); + println!("Adding wallet {}", wallet); + world.wallet_addresses.insert(wallet, address); + ffi_wallet.destroy(); +} + +#[then(expr = "I retrieve the mnemonic word list for {word}")] +async fn ffi_retrieve_mnemonic_words(_world: &mut TariWorld, language: String) { + println!("Mnemonic words for language {}:", language); + let words = get_mnemonic_word_list_for_language(language); + for i in 0..words.get_length() { + print!("{} ", words.get_at(i as u32).as_string()); + } + println!(); + assert_eq!(words.get_length(), 2048); +} + +#[then(expr = "I wait for ffi wallet {word} to connect to {word}")] +async fn ffi_wait_wallet_to_connect(world: &mut TariWorld, wallet: String, node: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let node = world.get_node(&node).unwrap().identity.public_key(); + for _ in 0..10 { + let public_keys = ffi_wallet.connected_public_keys(); + for i in 0..public_keys.get_length() { + let public_key = public_keys.get_public_key_at(i as u32); + if public_key.get_bytes().get_as_hex() == node.to_hex() { + return; + } + } + tokio::time::sleep(Duration::from_secs(3)).await; + } + panic!("Wallet not connected"); +} + +#[then(expr = "I wait for ffi wallet {word} to have at least {int} uT")] +async fn ffi_wait_for_balance(world: &mut TariWorld, wallet: String, balance: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let mut ffi_balance = ffi_wallet.get_balance(); + let mut cnt = 0; + while ffi_balance.get_available() < balance && cnt < 10 { + tokio::time::sleep(Duration::from_secs(3)).await; + ffi_balance = ffi_wallet.get_balance(); + cnt += 1; + } + assert!( + ffi_balance.get_available() >= balance, + "Wallet doesn't have enough available funds {}", + ffi_balance.get_available() + ); +} + +#[when(expr = "I add contact with alias {word} and address of {word} to ffi wallet {word}")] +async fn ffi_add_contact(world: &mut TariWorld, alias: String, pubkey: String, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + + let address = world.get_wallet_address(&pubkey).await.unwrap(); + let contact = create_contact(alias, address); + + assert!(ffi_wallet.upsert_contact(contact)); +} + +async fn check_contact(world: &mut TariWorld, alias: String, pubkey: Option, wallet: String) -> bool { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let address: Option = match pubkey { + Some(pubkey) => Some(world.get_wallet_address(&pubkey).await.unwrap()), + None => None, + }; + let contacts = ffi_wallet.get_contacts(); + let mut found = false; + for i in 0..contacts.get_length() { + let contact = contacts.get_at(i); + if (address.is_none() || &contact.get_address().address().get_as_hex() == address.as_ref().unwrap()) && + contact.get_alias() == alias + { + found = true; + break; + } + } + found +} + +#[then(expr = "I have contact with alias {word} and address of {word} in ffi wallet {word}")] +async fn ffi_check_contact(world: &mut TariWorld, alias: String, pubkey: String, wallet: String) { + assert!(check_contact(world, alias, Some(pubkey), wallet).await); +} + +#[when(expr = "I remove contact with alias {word} from ffi wallet {word}")] +async fn ffi_remove_contact(world: &mut TariWorld, alias: String, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let contacts = ffi_wallet.get_contacts(); + let mut contact_to_remove = None; + for i in 0..contacts.get_length() { + let contact = contacts.get_at(i); + if contact.get_alias() == alias { + contact_to_remove = Some(contact); + break; + } + } + assert!(contact_to_remove.is_some()); + assert!(ffi_wallet.remove_contact(contact_to_remove.unwrap())); +} + +#[then(expr = "I don't have contact with alias {word} in ffi wallet {word}")] +async fn ffi_check_no_contact(world: &mut TariWorld, alias: String, wallet: String) { + assert!(!check_contact(world, alias, None, wallet).await); +} + +#[when(expr = "I send {int} uT from ffi wallet {word} to wallet {word} at fee {int}")] +#[then(expr = "I send {int} uT from ffi wallet {word} to wallet {word} at fee {int}")] +async fn ffi_send_transaction(world: &mut TariWorld, amount: u64, wallet: String, dest: String, fee: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let dest_pub_key = world.get_wallet_address(&dest).await.unwrap(); + let message = format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee); + let tx_id = ffi_wallet.send_transaction(dest_pub_key, amount, fee, message, false); + assert_ne!(tx_id, 0, "Send transaction was not successful"); +} + +#[when(expr = "I send {int} uT from ffi wallet {word} to wallet {word} at fee {int} via one-sided transactions")] +#[then(expr = "I send {int} uT from ffi wallet {word} to wallet {word} at fee {int} via one-sided transactions")] +async fn ffi_send_one_sided_transaction(world: &mut TariWorld, amount: u64, wallet: String, dest: String, fee: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let dest_pub_key = world.get_wallet_address(&dest).await.unwrap(); + let message = format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee); + let tx_id = ffi_wallet.send_transaction(dest_pub_key, amount, fee, message, true); + assert_ne!(tx_id, 0, "Send transaction was not successful"); +} + +#[when(expr = "I have {int} received and {int} send transaction in ffi wallet {word}")] +#[then(expr = "I have {int} received and {int} send transaction in ffi wallet {word}")] +async fn ffi_check_number_of_transactions(world: &mut TariWorld, received: u32, send: u32, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let inbound_txs = ffi_wallet.get_pending_inbound_transactions(); + let mut inbound_cnt = inbound_txs.get_length(); + let outbound_txs = ffi_wallet.get_pending_outbound_transactions(); + let mut outbound_cnt = outbound_txs.get_length(); + let completed_txs = ffi_wallet.get_completed_transactions(); + for i in 0..completed_txs.get_length() { + let completed_tx = completed_txs.get_at(i); + if completed_tx.is_outbound() { + outbound_cnt += 1; + } else { + inbound_cnt += 1; + } + } + assert_eq!(outbound_cnt, send); + assert_eq!(inbound_cnt, received); +} + +#[then(expr = "I wait for ffi wallet {word} to have {int} pending outbound transaction(s)")] +async fn ffi_check_number_of_outbound_transactions(world: &mut TariWorld, wallet: String, cnt: u32) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let mut found_cnt = 0; + let num_retries = 120; + for _ in 0..num_retries { + let pending_outbound_transactions = ffi_wallet.get_pending_outbound_transactions(); + found_cnt = pending_outbound_transactions.get_length(); + if found_cnt >= cnt { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(found_cnt >= cnt, "The number of pending outbound transaction is lower."); +} + +#[then(expr = "I wait for ffi wallet {word} to have at least {int} contacts to be {word}")] +async fn ffi_check_contacts(world: &mut TariWorld, wallet: String, cnt: u64, status: String) { + assert!( + vec!["Online", "Offline", "NeverSeen"].contains(&status.as_str()), + "Unknown status : {}", + status + ); + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + println!( + "Waiting for {} to have at least {} contacts with status '{}'", + wallet, cnt, status + ); + let mut found_cnt = 0; + + let liveness_data = ffi_wallet.get_liveness_data(); + for _ in 0..120 { + found_cnt = 0; + for (_alias, data) in liveness_data.lock().unwrap().iter() { + if data.get_online_status() == status { + found_cnt += 1; + } + } + if found_cnt >= cnt { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!( + found_cnt >= cnt, + "{} doesn't have at least {} contacts with status {}!", + wallet, + cnt, + status + ); +} + +#[then(expr = "I want to view the transaction kernels for completed transactions in ffi wallet {word}")] +async fn ffi_view_transaction_kernels_for_completed(world: &mut TariWorld, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let completed_transactions = ffi_wallet.get_completed_transactions(); + for i in 0..completed_transactions.get_length() { + let completed_transaction = completed_transactions.get_at(i); + let kernel = completed_transaction.get_transaction_kernel(); + println!("Transaction kernel info :"); + assert!(!kernel.get_excess_hex().is_empty()); + println!("Excess {}", kernel.get_excess_hex()); + assert!(!kernel.get_excess_public_nonce_hex().is_empty()); + println!("Nonce {}", kernel.get_excess_public_nonce_hex()); + assert!(!kernel.get_excess_signature_hex().is_empty()); + println!("Signature {}", kernel.get_excess_signature_hex()); + } +} + +#[then(expr = "I cancel all outbound transactions on ffi wallet {word} and it will cancel {int} transaction")] +async fn ffi_cancel_outbound_transactions(world: &mut TariWorld, wallet: String, cnt: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let pending_outbound_transactions = ffi_wallet.get_pending_outbound_transactions(); + let mut cancelled = 0; + for i in 0..pending_outbound_transactions.get_length() { + let pending_outbound_transaction = pending_outbound_transactions.get_at(i); + if ffi_wallet.cancel_pending_transaction(pending_outbound_transaction.get_transaction_id()) { + cancelled += 1; + } + } + assert_eq!(cancelled, cnt); +} + +#[then(expr = "I wait for ffi wallet {word} to receive {int} transaction")] +async fn ffi_wait_for_transaction_received(world: &mut TariWorld, wallet: String, cnt: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let num_retries = 120; + let mut found_cnt = 0; + for _ in 0..num_retries { + found_cnt = ffi_wallet.get_counters().get_transaction_received(); + if found_cnt >= cnt { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(found_cnt >= cnt, "Expected {}, but got only {}", cnt, found_cnt); +} + +#[then(expr = "I wait for ffi wallet {word} to receive {int} finalization")] +async fn ffi_wait_for_transaction_finalized(world: &mut TariWorld, wallet: String, cnt: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let num_retries = 120; + let mut found_cnt = 0; + for _ in 0..num_retries { + found_cnt = ffi_wallet.get_counters().get_transaction_finalized(); + if found_cnt >= cnt { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(found_cnt >= cnt, "Expected {}, but got only {}", cnt, found_cnt); +} + +#[then(expr = "I wait for ffi wallet {word} to receive {int} broadcast")] +async fn ffi_wait_for_transaction_broadcast(world: &mut TariWorld, wallet: String, cnt: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + let num_retries = 120; + let mut found_cnt = 0; + for _ in 0..num_retries { + found_cnt = ffi_wallet.get_counters().get_transaction_broadcast(); + if found_cnt >= cnt { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(found_cnt >= cnt, "Expected {}, but got only {}", cnt, found_cnt); +} + +#[then(expr = "I start TXO validation on ffi wallet {word}")] +async fn ffi_start_txo_validation(world: &mut TariWorld, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + ffi_wallet.start_txo_validation(); + let num_retries = 120; + let mut validation_complete = false; + for _ in 0..num_retries { + validation_complete = ffi_wallet.get_counters().get_txo_validation_complete(); + if validation_complete { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(validation_complete); +} + +#[then(expr = "I start TX validation on ffi wallet {word}")] +async fn ffi_start_tx_validation(world: &mut TariWorld, wallet: String) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + ffi_wallet.start_transaction_validation(); + let num_retries = 120; + let mut validation_complete = false; + for _ in 0..num_retries { + validation_complete = ffi_wallet.get_counters().get_tx_validation_complete(); + if validation_complete { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(validation_complete); +} + +#[then(expr = "ffi wallet {word} detects {word} {int} ffi transactions to be {word}")] +async fn ffi_detects_transaction( + world: &mut TariWorld, + wallet: String, + comparison: String, + count: u64, + status: String, +) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + assert!(vec![ + "TRANSACTION_STATUS_BROADCAST", + "TRANSACTION_STATUS_FAUX_UNCONFIRMED", + "TRANSACTION_STATUS_FAUX_CONFIRMED" + ] + .contains(&status.as_str())); + println!( + "Waiting for {} to have detected {} {} {} transaction(s)", + wallet, comparison, count, status + ); + let mut found_count = 0; + for _ in 0..120 { + found_count = match status.as_str() { + "TRANSACTION_STATUS_BROADCAST" => ffi_wallet.get_counters().get_transaction_broadcast(), + "TRANSACTION_STATUS_FAUX_UNCONFIRMED" => ffi_wallet.get_counters().get_transaction_faux_unconfirmed(), + "TRANSACTION_STATUS_FAUX_CONFIRMED" => ffi_wallet.get_counters().get_transaction_faux_confirmed(), + _ => unreachable!(), + }; + if found_count >= count { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + match comparison.as_str() { + "AT_LEAST" => assert!( + found_count >= count, + "Counter not adequate! Counter is {}.", + found_count + ), + "EXACTLY" => assert!( + found_count == count, + "Counter not adequate! Counter is {}.", + found_count + ), + _ => panic!("Unknown comparison method {}", comparison), + }; +} + +#[then(expr = "I wait for ffi wallet {word} to receive {int} mined")] +async fn ffi_wait_for_received_mined(world: &mut TariWorld, wallet: String, count: u64) { + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + println!("Waiting for {} to receive {} transaction(s) mined", wallet, count); + + let mut found_cnt = 0; + for _ in 0..120 { + found_cnt = ffi_wallet.get_counters().get_transaction_mined(); + if found_cnt >= count { + break; + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + assert!(found_cnt >= count); +} + +#[then(expr = "I recover wallet {word} into ffi wallet {word} from seed words on node {word}")] +async fn ffi_recover_wallet(world: &mut TariWorld, wallet_name: String, ffi_wallet_name: String, base_node: String) { + let wallet = world.get_wallet(&wallet_name).unwrap(); + let seed_words_path = wallet.temp_dir_path.clone().join("seed_words.txt"); + let seed_words_file = std::fs::File::open(seed_words_path).unwrap(); + let reader = std::io::BufReader::new(seed_words_file); + let line = reader.lines().next().unwrap().unwrap(); + let words = line.split_whitespace().collect(); + let seed_words = create_seed_words(words); + + spawn_wallet_ffi(world, ffi_wallet_name.clone(), seed_words.get_ptr()); + + let base_node = world.get_node(&base_node).unwrap(); + world.get_ffi_wallet(&ffi_wallet_name).unwrap().add_base_node( + base_node.identity.public_key().to_hex(), + base_node.identity.public_address().to_string(), + ); +} + +#[then(expr = "I restart ffi wallet {word} connected to base node {word}")] +async fn ffi_restart_wallet(world: &mut TariWorld, wallet: String, base_node: String) { + let ffi_wallet = world.get_mut_ffi_wallet(&wallet).unwrap(); + ffi_wallet.restart(); + let base_node = world.get_node(&base_node).unwrap(); + let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); + ffi_wallet.add_base_node( + base_node.identity.public_key().to_hex(), + base_node.identity.public_address().to_string(), + ); +} + +#[then(expr = "The fee per gram stats for {word} are {int}, {int}, {int}")] +#[when(expr = "The fee per gram stats for {word} are {int}, {int}, {int}")] +async fn ffi_fee_per_gram_stats(world: &mut TariWorld, wallet: String, min: u64, avg: u64, max: u64) { + let ffi_wallet = world.get_mut_ffi_wallet(&wallet).unwrap(); + let fee_per_gram_stats = ffi_wallet.get_fee_per_gram_stats(5); + for i in 0..fee_per_gram_stats.get_length() { + let fee_per_gram_stat = fee_per_gram_stats.get_at(i); + println!("order {}", fee_per_gram_stat.get_order()); + println!("min {}", fee_per_gram_stat.get_min_fee_per_gram()); + println!("avg {}", fee_per_gram_stat.get_avg_fee_per_gram()); + println!("max {}", fee_per_gram_stat.get_max_fee_per_gram()); + assert_eq!(fee_per_gram_stat.get_min_fee_per_gram(), min); + assert_eq!(fee_per_gram_stat.get_avg_fee_per_gram(), avg); + assert_eq!(fee_per_gram_stat.get_max_fee_per_gram(), max); + } +} + fn flush_stdout(buffer: &Arc>>) { // After each test we flush the stdout to the logs. info!( @@ -3981,31 +4362,18 @@ fn flush_stdout(buffer: &Arc>>) { buffer.lock().unwrap().clear(); } -fn main() { - initialize_logging( - &PathBuf::from("log4rs/cucumber.yml"), - include_str!("../log4rs/cucumber.yml"), - ) - .expect("logging not configured"); - let stdout_buffer = Arc::new(Mutex::new(Vec::::new())); - #[cfg(test)] - std::io::set_output_capture(Some(stdout_buffer.clone())); - // Never move this line below the runtime creation!!! It will cause that any new thread created via task::spawn will - // not be affected by the output capture. - let stdout_buffer_clone = stdout_buffer.clone(); - let runtime = Runtime::new().unwrap(); - runtime.block_on(async { - let world = TariWorld::cucumber() +async fn run_cucumber(is_ffi: bool, stdout_buffer: Arc>>) { + let world = TariWorld::cucumber() .repeat_failed() // following config needed to use eprint statements in the tests - .max_concurrent_scenarios(5) + .max_concurrent_scenarios(if is_ffi {1} else {5}) //.with_writer( // writer::Basic::raw(io::stdout(), writer::Coloring::Never, 0) // .summarized() // .assert_normalized(), //) .after(move |_feature, _rule, scenario, ev, maybe_world| { - let stdout_buffer = stdout_buffer_clone.clone(); + let stdout_buffer = stdout_buffer.clone(); Box::pin(async move { flush_stdout(&stdout_buffer); match ev { @@ -4033,8 +4401,31 @@ fn main() { info!(target: LOG_TARGET, "Starting {} {}", scenario.keyword, scenario.name); }) }); - world.run_and_exit("tests/features/").await; - }); + world + .filter_run_and_exit("tests/features/", move |feature, _, _| { + if is_ffi { + feature.name == "Wallet FFI" + } else { + feature.name != "Wallet FFI" + } + }) + .await; +} + +fn main() { + initialize_logging( + &PathBuf::from("log4rs/cucumber.yml"), + include_str!("../log4rs/cucumber.yml"), + ) + .expect("logging not configured"); + let stdout_buffer = Arc::new(Mutex::new(Vec::::new())); + #[cfg(test)] + std::io::set_output_capture(Some(stdout_buffer.clone())); + // Never move this line below the runtime creation!!! It will cause that any new thread created via task::spawn will + // not be affected by the output capture. + let runtime = Runtime::new().unwrap(); + runtime.block_on(run_cucumber(false, stdout_buffer.clone())); + runtime.block_on(run_cucumber(true, stdout_buffer.clone())); // If by any chance we have anything in the stdout buffer just log it. flush_stdout(&stdout_buffer); diff --git a/integration_tests/tests/features/WalletFFI.feature b/integration_tests/tests/features/WalletFFI.feature index 2b76012e00..4c0aed23ed 100644 --- a/integration_tests/tests/features/WalletFFI.feature +++ b/integration_tests/tests/features/WalletFFI.feature @@ -3,109 +3,93 @@ @wallet-ffi Feature: Wallet FFI - # Appears to run in NodeJS v12 consistently on mac (5 crash-less runs in a row). - # Crashes in v14+ intermittently on mac (more frequently than not) and completely broken on Linux. - # See issues: - # https://github.com/nodejs/node/issues/32463 - # https://github.com/node-ffi-napi/node-ffi-napi/issues/97 - - @critical - Scenario: As a client I want to be able to protect my wallet with a passphrase - # Given I have a base node BASE - # And I have a ffi wallet FFI_WALLET connected to base node BASE - # # It's just calling the encrypt function, we don't test if it's actually encrypted - # And I set passphrase PASSPHRASE of ffi wallet FFI_WALLET - # And I stop ffi wallet FFI_WALLET - Scenario: As a client I want to see my whoami info - # Given I have a base node BASE - # And I have a ffi wallet FFI_WALLET connected to base node BASE - # Then I want to get public key of ffi wallet FFI_WALLET - # And I want to get emoji id of ffi wallet FFI_WALLET - # And I stop ffi wallet FFI_WALLET + Given I have a base node BASE + Given I have a ffi wallet FFI_WALLET connected to base node BASE + Then I want to get public key of ffi wallet FFI_WALLET + And I want to get emoji id of ffi wallet FFI_WALLET + And I stop ffi wallet FFI_WALLET - @broken Scenario: As a client I want to be able to restore my ffi wallet from seed words Given I have a base node BASE When I have wallet SPECTATOR connected to base node BASE When I have mining node MINER connected to base node BASE and wallet SPECTATOR When mining node MINER mines 10 blocks - # Then I wait for wallet SPECTATOR to have at least 1000000 uT - # Then I recover wallet SPECTATOR into ffi wallet FFI_WALLET from seed words on node BASE - # And I wait for ffi wallet FFI_WALLET to have at least 1000000 uT - # And I stop ffi wallet FFI_WALLET + Then I wait for wallet SPECTATOR to have at least 1000000 uT + Then I recover wallet SPECTATOR into ffi wallet FFI_WALLET from seed words on node BASE + And I wait for ffi wallet FFI_WALLET to have at least 1000000 uT + And I stop ffi wallet FFI_WALLET @critical Scenario: As a client I want to retrieve the mnemonic word list for a given language - # Given I have a base node BASE - # And I have a ffi wallet FFI_WALLET connected to base node BASE - # Then I retrieve the mnemonic word list for CHINESE_SIMPLIFIED from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for ENGLISH from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for FRENCH from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for ITALIAN from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for JAPANESE from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for KOREAN from ffi wallet FFI_WALLET - # Then I retrieve the mnemonic word list for SPANISH from ffi wallet FFI_WALLET - # And I stop ffi wallet FFI_WALLET + Then I retrieve the mnemonic word list for CHINESE_SIMPLIFIED + Then I retrieve the mnemonic word list for ENGLISH + Then I retrieve the mnemonic word list for FRENCH + Then I retrieve the mnemonic word list for ITALIAN + Then I retrieve the mnemonic word list for JAPANESE + Then I retrieve the mnemonic word list for KOREAN + Then I retrieve the mnemonic word list for SPANISH Scenario: As a client I want to set the base node - # Given I have a base node BASE1 - # And I have a base node BASE2 - # Given I have a ffi wallet FFI_WALLET connected to base node BASE1 - # Then I wait for ffi wallet FFI_WALLET to connect to BASE1 - # Given I set base node BASE2 for ffi wallet FFI_WALLET - # Then I wait for ffi wallet FFI_WALLET to connect to BASE2 + Given I have a base node BASE1 + And I have a base node BASE2 + Given I have a ffi wallet FFI_WALLET connected to base node BASE1 + Then I wait for ffi wallet FFI_WALLET to connect to BASE1 + Given I set base node BASE2 for ffi wallet FFI_WALLET + Then I wait for ffi wallet FFI_WALLET to connect to BASE2 + @broken Scenario: As a client I want to cancel a transaction Given I have a base node BASE When I have wallet SENDER connected to base node BASE - # And I have a ffi wallet FFI_WALLET connected to base node BASE + And I have a ffi wallet FFI_WALLET connected to base node BASE When I have mining node MINER connected to base node BASE and wallet SENDER When mining node MINER mines 10 blocks - # Then I wait for wallet SENDER to have at least 1000000 uT - # And I send 2000000 uT without waiting for broadcast from wallet SENDER to wallet FFI_WALLET at fee 20 - # Then ffi wallet FFI_WALLET detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST - # And wallet SENDER detects all transactions are at least Broadcast + Then I wait for wallet SENDER to have at least 1000000 uT + And I send 2000000 uT without waiting for broadcast from wallet SENDER to wallet FFI_WALLET at fee 20 + Then ffi wallet FFI_WALLET detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST + And wallet SENDER detects all transactions are at least Broadcast When mining node MINER mines 10 blocks - # Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT - # When I have wallet RECEIVER connected to base node BASE - # And I stop wallet RECEIVER - # And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 20 - # Then I wait for ffi wallet FFI_WALLET to have 1 pending outbound transaction - # Then I cancel all outbound transactions on ffi wallet FFI_WALLET and it will cancel 1 transaction - # And I stop ffi wallet FFI_WALLET + Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT + When I have wallet RECEIVER connected to base node BASE + And I stop wallet RECEIVER + And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 20 + Then I wait for ffi wallet FFI_WALLET to have 1 pending outbound transaction + Then I cancel all outbound transactions on ffi wallet FFI_WALLET and it will cancel 1 transaction + And I stop ffi wallet FFI_WALLET Scenario: As a client I want to manage contacts Given I have a base node BASE - # And I have a ffi wallet FFI_WALLET connected to base node BASE - # When I have wallet WALLET connected to base node BASE - # And I add contact with alias ALIAS and pubkey WALLET to ffi wallet FFI_WALLET - # Then I have contact with alias ALIAS and pubkey WALLET in ffi wallet FFI_WALLET - # When I remove contact with alias ALIAS from ffi wallet FFI_WALLET - # Then I don't have contact with alias ALIAS in ffi wallet FFI_WALLET - # And I stop ffi wallet FFI_WALLET - - # TODO: Was broken due to #4525 - fix underway - @critical @broken + And I have a ffi wallet FFI_WALLET connected to base node BASE + When I have wallet WALLET connected to base node BASE + And I add contact with alias ALIAS and address of WALLET to ffi wallet FFI_WALLET + Then I have contact with alias ALIAS and address of WALLET in ffi wallet FFI_WALLET + When I remove contact with alias ALIAS from ffi wallet FFI_WALLET + Then I don't have contact with alias ALIAS in ffi wallet FFI_WALLET + And I stop ffi wallet FFI_WALLET + + @critical Scenario: As a client I want to receive contact liveness events Given I have a seed node SEED - # # Contact liveness is based on P2P messaging; ensure connectivity by forcing 'DirectOnly' - # And I have non-default wallet WALLET1 connected to all seed nodes using DirectOnly - # And I have non-default wallet WALLET2 connected to all seed nodes using DirectOnly - # And I have a ffi wallet FFI_WALLET connected to seed node SEED - # # Start the contact liveness pings by adding contacts to the FFI wallet - # And I add contact with alias ALIAS1 and pubkey WALLET1 to ffi wallet FFI_WALLET - # And I add contact with alias ALIAS2 and pubkey WALLET2 to ffi wallet FFI_WALLET - # # Do some mining and send transactions to force P2P discovery - # And I have mining node MINER1 connected to base node SEED and wallet WALLET1 - # And I have mining node MINER2 connected to base node SEED and wallet WALLET2 - # And mining node MINER1 mines 1 blocks - # And mining node MINER2 mines 5 blocks - # And I send 100000000 uT without waiting for broadcast from wallet WALLET1 to wallet FFI_WALLET at fee 20 - # And I send 100000000 uT without waiting for broadcast from wallet WALLET2 to wallet FFI_WALLET at fee 20 - # # If the FFI wallet can send the transactions, P2P connectivity has been established - # Then I wait for ffi wallet FFI_WALLET to have at least 2 contacts to be Online - # And I stop ffi wallet FFI_WALLET + # Contact liveness is based on P2P messaging; ensure connectivity by forcing 'DirectOnly' + And I have non-default wallet WALLET1 connected to all seed nodes using DirectOnly + And I have non-default wallet WALLET2 connected to all seed nodes using DirectOnly + And I have a ffi wallet FFI_WALLET connected to seed node SEED + # Start the contact liveness pings by adding contacts to the FFI wallet + When I add contact with alias ALIAS1 and address of WALLET1 to ffi wallet FFI_WALLET + And I add contact with alias ALIAS2 and address of WALLET2 to ffi wallet FFI_WALLET + # Do some mining and send transactions to force P2P discovery + And I have mining node MINER1 connected to base node SEED and wallet WALLET1 + And I have mining node MINER2 connected to base node SEED and wallet WALLET2 + And mining node MINER1 mines 1 blocks + And mining node MINER2 mines 5 blocks + Then I wait for wallet WALLET1 to have at least 100000000 uT + And I wait for wallet WALLET2 to have at least 100000000 uT + When I send 100000000 uT without waiting for broadcast from wallet WALLET1 to wallet FFI_WALLET at fee 20 + And I send 100000000 uT without waiting for broadcast from wallet WALLET2 to wallet FFI_WALLET at fee 20 + # If the FFI wallet can send the transactions, P2P connectivity has been established + Then I wait for ffi wallet FFI_WALLET to have at least 2 contacts to be Online + And I stop ffi wallet FFI_WALLET @critical Scenario: As a client I want to retrieve a list of transactions I have made and received @@ -113,100 +97,102 @@ Feature: Wallet FFI When I have a base node BASE1 connected to all seed nodes When I have a base node BASE2 connected to all seed nodes When I have wallet SENDER connected to base node BASE1 - # And I have a ffi wallet FFI_WALLET connected to base node BASE2 - # When I have wallet RECEIVER connected to base node BASE2 + And I have a ffi wallet FFI_WALLET connected to base node BASE2 + When I have wallet RECEIVER connected to base node BASE2 When I have mining node MINER connected to base node BASE1 and wallet SENDER When mining node MINER mines 10 blocks - # Then I wait for wallet SENDER to have at least 1000000 uT - # And I send 2000000 uT from wallet SENDER to wallet FFI_WALLET at fee 20 - # Then ffi wallet FFI_WALLET detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST + Then I wait for wallet SENDER to have at least 1000000 uT + And I send 1000000 uT from wallet SENDER to wallet FFI_WALLET at fee 20 + Then ffi wallet FFI_WALLET detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST When mining node MINER mines 10 blocks - # Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT - # And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 20 - # Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST - # # The broadcast check does not include delivery; create some holding points to ensure it was received + Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT + And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 20 + Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST + # The broadcast check does not include delivery; create some holding points to ensure it was received When mining node MINER mines 2 blocks Then all nodes are at height 22 When mining node MINER mines 2 blocks Then all nodes are at height 24 When mining node MINER mines 6 blocks - # Then I wait for wallet RECEIVER to have at least 1000000 uT - # And I have 1 received and 1 send transaction in ffi wallet FFI_WALLET - # And I start TXO validation on ffi wallet FFI_WALLET - # And I start TX validation on ffi wallet FFI_WALLET - # Then I wait for ffi wallet FFI_WALLET to receive 2 mined - # Then I want to view the transaction kernels for completed transactions in ffi wallet FFI_WALLET - # And I stop ffi wallet FFI_WALLET + Then I wait for wallet RECEIVER to have at least 1000000 uT + And I have 1 received and 1 send transaction in ffi wallet FFI_WALLET + And I start TXO validation on ffi wallet FFI_WALLET + And I start TX validation on ffi wallet FFI_WALLET + Then I wait for ffi wallet FFI_WALLET to receive 2 mined + Then I want to view the transaction kernels for completed transactions in ffi wallet FFI_WALLET + And I stop ffi wallet FFI_WALLET - @critical + + @critical @broken Scenario: As a client I want to receive Tari via my Public Key sent while I am offline when I come back online Given I have a seed node SEED When I have a base node BASE1 connected to all seed nodes When I have a base node BASE2 connected to all seed nodes When I have wallet SENDER connected to base node BASE1 - # And I have a ffi wallet FFI_WALLET connected to base node BASE1 + And I have a ffi wallet FFI_WALLET connected to base node BASE1 When I have mining node MINER connected to base node BASE1 and wallet SENDER When mining node MINER mines 10 blocks - # Then I wait for wallet SENDER to have at least 1000000 uT - # And I stop ffi wallet FFI_WALLET - # And I send 2000000 uT without waiting for broadcast from wallet SENDER to wallet FFI_WALLET at fee 20 - # And I restart ffi wallet FFI_WALLET connected to base node BASE2 - # # BROKEN - # Then I wait for ffi wallet FFI_WALLET to receive 1 transaction - # Then I wait for ffi wallet FFI_WALLET to receive 1 finalization - # Then I wait for ffi wallet FFI_WALLET to receive 1 broadcast + Then I wait for wallet SENDER to have at least 1000000 uT + And I stop ffi wallet FFI_WALLET + And I send 1000000 uT without waiting for broadcast from wallet SENDER to wallet FFI_WALLET at fee 20 + And I restart ffi wallet FFI_WALLET connected to base node BASE2 + # BROKEN + Then I wait for ffi wallet FFI_WALLET to receive 1 transaction + Then I wait for ffi wallet FFI_WALLET to receive 1 finalization + Then I wait for ffi wallet FFI_WALLET to receive 1 broadcast When mining node MINER mines 10 blocks - # Then I wait for ffi wallet FFI_WALLET to receive 1 mined - # Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT - # And I stop ffi wallet FFI_WALLET + Then I wait for ffi wallet FFI_WALLET to receive 1 mined + Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT + And I stop ffi wallet FFI_WALLET + # TODO: unimplemented step to send money @critical Scenario: As a client I want to send a one-sided transaction Given I have a seed node SEED When I have a base node BASE1 connected to all seed nodes When I have a base node BASE2 connected to all seed nodes When I have wallet SENDER connected to base node BASE1 - # And I have a ffi wallet FFI_WALLET connected to base node BASE2 + And I have a ffi wallet FFI_WALLET connected to base node BASE2 When I have wallet RECEIVER connected to base node BASE2 When I have mining node MINER connected to base node BASE1 and wallet SENDER When mining node MINER mines 10 blocks - # Then I wait for wallet SENDER to have at least 5000000 uT - # And I send 2400000 uT from wallet SENDER to wallet FFI_WALLET at fee 5 - # And I send 2400000 uT from wallet SENDER to wallet FFI_WALLET at fee 5 - # Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST + Then I wait for wallet SENDER to have at least 5000000 uT + And I send 2400000 uT from wallet SENDER to wallet FFI_WALLET at fee 5 + And I send 2400000 uT from wallet SENDER to wallet FFI_WALLET at fee 5 + Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST When mining node MINER mines 10 blocks - # Then I wait for ffi wallet FFI_WALLET to have at least 4000000 uT - # And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 5 via one-sided transactions - # Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST + Then I wait for ffi wallet FFI_WALLET to have at least 4000000 uT + And I send 1000000 uT from ffi wallet FFI_WALLET to wallet RECEIVER at fee 5 via one-sided transactions + Then ffi wallet FFI_WALLET detects AT_LEAST 2 ffi transactions to be TRANSACTION_STATUS_BROADCAST When mining node MINER mines 2 blocks Then all nodes are at height 22 - # Then wallet RECEIVER has at least 1 transactions that are all TRANSACTION_STATUS_FAUX_UNCONFIRMED and not cancelled + Then wallet RECEIVER has at least 1 transactions that are all TRANSACTION_STATUS_FAUX_UNCONFIRMED and not cancelled When mining node MINER mines 5 blocks Then all nodes are at height 27 - # Then wallet RECEIVER has at least 1 transactions that are all TRANSACTION_STATUS_FAUX_CONFIRMED and not cancelled - # And I stop ffi wallet FFI_WALLET + Then wallet RECEIVER has at least 1 transactions that are all TRANSACTION_STATUS_FAUX_CONFIRMED and not cancelled + And I stop ffi wallet FFI_WALLET + # TODO: Missing send @critical Scenario: As a client I want to receive a one-sided transaction Given I have a seed node SEED When I have a base node BASE1 connected to all seed nodes When I have a base node BASE2 connected to all seed nodes When I have wallet SENDER connected to base node BASE1 - # And I have a ffi wallet FFI_RECEIVER connected to base node BASE2 + And I have a ffi wallet FFI_RECEIVER connected to base node BASE2 When I have mining node MINER connected to base node BASE1 and wallet SENDER When mining node MINER mines 10 blocks - # Then I wait for wallet SENDER to have at least 5000000 uT - # Then I send a one-sided transaction of 1000000 uT from SENDER to FFI_RECEIVER at fee 20 + Then I wait for wallet SENDER to have at least 5000000 uT + Then I send a one-sided transaction of 1000000 uT from SENDER to FFI_RECEIVER at fee 20 When mining node MINER mines 2 blocks Then all nodes are at height 12 - # #BROKEN - # Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_FAUX_UNCONFIRMED - # And I send 1000000 uT from wallet SENDER to wallet FFI_RECEIVER at fee 20 - # Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST + Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_FAUX_UNCONFIRMED + And I send 1000000 uT from wallet SENDER to wallet FFI_RECEIVER at fee 20 + Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_BROADCAST When mining node MINER mines 5 blocks Then all nodes are at height 17 - # Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_FAUX_CONFIRMED - # And I stop ffi wallet FFI_RECEIVER + Then ffi wallet FFI_RECEIVER detects AT_LEAST 1 ffi transactions to be TRANSACTION_STATUS_FAUX_CONFIRMED + And I stop ffi wallet FFI_RECEIVER Scenario: As a client I want to get fee per gram stats Given I have a base node BASE @@ -214,41 +200,40 @@ Feature: Wallet FFI When I have wallet WALLET_B connected to base node BASE When I have mining node MINER connected to base node BASE and wallet WALLET_A When mining node MINER mines 7 blocks - # When I have wallet WALLET_B connected to base node BASE - # Then I wait for wallet WALLET_A to have at least 10000000 uT - # And I have a ffi wallet FFI_WALLET connected to base node BASE - # And The fee per gram stats for FFI_WALLET are 1, 1, 1 - # And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 20 - # And The fee per gram stats for FFI_WALLET are 20, 20, 20 - # And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 40 - # And The fee per gram stats for FFI_WALLET are 20, 30, 40 - # And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 60 - # And The fee per gram stats for FFI_WALLET are 20, 40, 60 + Then I wait for wallet WALLET_A to have at least 10000000 uT + And I have a ffi wallet FFI_WALLET connected to base node BASE + And The fee per gram stats for FFI_WALLET are 1, 1, 1 + And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 20 + And The fee per gram stats for FFI_WALLET are 20, 20, 20 + And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 40 + And The fee per gram stats for FFI_WALLET are 20, 30, 40 + And I send 1000000 uT from wallet WALLET_A to wallet WALLET_B at fee 60 + And The fee per gram stats for FFI_WALLET are 20, 40, 60 When mining node MINER mines 1 blocks - # And The fee per gram stats for FFI_WALLET are 1, 1, 1 + And The fee per gram stats for FFI_WALLET are 1, 1, 1 - # Scenario: As a client I want to get my balance - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +# Scenario: As a client I want to get my balance +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - #Scenario: As a client I want to send Tari to a Public Key - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +#Scenario: As a client I want to send Tari to a Public Key +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - #Scenario: As a client I want to specify a custom fee when I send tari - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +#Scenario: As a client I want to specify a custom fee when I send tari +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - #Scenario: As a client I want to receive Tari via my Public Key while I am online - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +#Scenario: As a client I want to receive Tari via my Public Key while I am online +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - # Scenario: As a client I want to be able to initiate TXO and TX validation with the specifed base node. - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +# Scenario: As a client I want to be able to initiate TXO and TX validation with the specifed base node. +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - # Scenario: As a client I want feedback about the progress of sending and receiving a transaction - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +# Scenario: As a client I want feedback about the progress of sending and receiving a transaction +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" - # Scenario: As a client I want feedback about my connection status to the specifed Base Node +# Scenario: As a client I want feedback about my connection status to the specifed Base Node - # Scenario: As a client I want feedback about the wallet restoration process - # It's a subtest of "As a client I want to be able to restore my wallet from seed words" +# Scenario: As a client I want feedback about the wallet restoration process +# It's a subtest of "As a client I want to be able to restore my wallet from seed words" - # Scenario: As a client I want feedback about TXO and TX validation processes - # It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" +# Scenario: As a client I want feedback about TXO and TX validation processes +# It's a subtest of "As a client I want to retrieve a list of transactions I have made and received" diff --git a/integration_tests/tests/utils/ffi/balance.rs b/integration_tests/tests/utils/ffi/balance.rs new file mode 100644 index 0000000000..a988df6182 --- /dev/null +++ b/integration_tests/tests/utils/ffi/balance.rs @@ -0,0 +1,91 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::ffi_import; + +pub struct Balance { + ptr: *mut c_void, +} + +impl Drop for Balance { + fn drop(&mut self) { + unsafe { ffi_import::balance_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl Balance { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_available(&self) -> u64 { + let available; + let mut error = 0; + unsafe { + available = ffi_import::balance_get_available(self.ptr, &mut error); + if error > 0 { + println!("balance_get_available error {}", error); + } + } + available + } + + pub fn get_time_locked(&self) -> u64 { + let time_locked; + let mut error = 0; + unsafe { + time_locked = ffi_import::balance_get_time_locked(self.ptr, &mut error); + if error > 0 { + println!("balance_get_time_locked error {}", error); + } + } + time_locked + } + + pub fn get_pending_incoming(&self) -> u64 { + let pending_incoming; + let mut error = 0; + unsafe { + pending_incoming = ffi_import::balance_get_pending_incoming(self.ptr, &mut error); + if error > 0 { + println!("balance_get_pending_incoming error {}", error); + } + } + pending_incoming + } + + pub fn get_pending_outgoing(&self) -> u64 { + let pending_outgoing; + let mut error = 0; + unsafe { + pending_outgoing = ffi_import::balance_get_pending_outgoing(self.ptr, &mut error); + if error > 0 { + println!("balance_get_pending_outgoing error {}", error); + } + } + pending_outgoing + } +} diff --git a/integration_tests/tests/utils/ffi/callbacks.rs b/integration_tests/tests/utils/ffi/callbacks.rs new file mode 100644 index 0000000000..d5658a0fd6 --- /dev/null +++ b/integration_tests/tests/utils/ffi/callbacks.rs @@ -0,0 +1,326 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::sync::{Arc, Mutex, Once}; + +use libc::c_void; + +use super::{Balance, CompletedTransaction, ContactsLivenessData, PendingInboundTransaction, Wallet}; +use crate::utils::ffi::TransactionSendStatus; + +#[derive(Debug, Default)] +pub struct Callbacks { + transaction_received: Mutex, + transaction_reply_received: Mutex, + transaction_finalized: Mutex, + transaction_broadcast: Mutex, + transaction_mined: Mutex, + transaction_mined_unconfirmed: Mutex, + transaction_faux_confirmed: Mutex, + transaction_faux_unconfirmed: Mutex, + transaction_cancelled: Mutex, + txo_validation_complete: Mutex, + txo_validation_result: Mutex, + tx_validation_complete: Mutex, + tx_validation_result: Mutex, + transaction_saf_message_received: Mutex, + contacts_liveness_data_updated: Mutex, + pub wallet: Option>>, +} + +static mut INSTANCE: Option = None; +static START: Once = Once::new(); + +impl Callbacks { + pub fn get_transaction_received(&self) -> u64 { + *self.transaction_received.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_transaction_reply_received(&self) -> u64 { + *self.transaction_reply_received.lock().unwrap() + } + + pub fn get_transaction_finalized(&self) -> u64 { + *self.transaction_finalized.lock().unwrap() + } + + pub fn get_transaction_broadcast(&self) -> u64 { + *self.transaction_broadcast.lock().unwrap() + } + + pub fn get_transaction_mined(&self) -> u64 { + *self.transaction_mined.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_transaction_mined_unconfirmed(&self) -> u64 { + *self.transaction_mined_unconfirmed.lock().unwrap() + } + + pub fn get_transaction_faux_confirmed(&self) -> u64 { + *self.transaction_faux_confirmed.lock().unwrap() + } + + pub fn get_transaction_faux_unconfirmed(&self) -> u64 { + *self.transaction_faux_unconfirmed.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_transaction_cancelled(&self) -> u64 { + *self.transaction_cancelled.lock().unwrap() + } + + pub fn get_txo_validation_complete(&self) -> bool { + *self.txo_validation_complete.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_txo_validation_result(&self) -> u64 { + *self.txo_validation_result.lock().unwrap() + } + + pub fn get_tx_validation_complete(&self) -> bool { + *self.tx_validation_complete.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_tx_validation_result(&self) -> u64 { + *self.tx_validation_result.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_transaction_saf_message_received(&self) -> u64 { + *self.transaction_saf_message_received.lock().unwrap() + } + + #[allow(dead_code)] + pub fn get_contacts_liveness_data_updated(&self) -> u64 { + *self.contacts_liveness_data_updated.lock().unwrap() + } + + pub fn on_received_transaction(&mut self, ptr: *mut c_void) { + let pending_inbound_transaction = PendingInboundTransaction::from_ptr(ptr); + println!( + "{} received Transaction with txID {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + pending_inbound_transaction.get_transaction_id() + ); + *self.transaction_received.lock().unwrap() += 1; + } + + pub fn on_received_transaction_reply(&mut self, ptr: *mut c_void) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} received reply for Transaction with txID {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id() + ); + *self.transaction_reply_received.lock().unwrap() += 1; + } + + pub fn on_received_finalized_transaction(&mut self, ptr: *mut c_void) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} received finalization for Transaction with txID {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id() + ); + *self.transaction_finalized.lock().unwrap() += 1; + } + + pub fn on_transaction_broadcast(&mut self, ptr: *mut c_void) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} Transaction with txID {} was broadcast.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id() + ); + *self.transaction_broadcast.lock().unwrap() += 1; + } + + pub fn on_transaction_mined(&mut self, ptr: *mut c_void) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} Transaction with txID {} was mined.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id() + ); + *self.transaction_mined.lock().unwrap() += 1; + } + + pub fn on_transaction_mined_unconfirmed(&mut self, ptr: *mut c_void, confirmations: u64) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} Transaction with txID {} is mined unconfirmed with {} confirmations.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id(), + confirmations + ); + *self.transaction_mined_unconfirmed.lock().unwrap() += 1; + } + + pub fn on_faux_transaction_confirmed(&mut self, ptr: *mut c_void) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} Faux transaction with txID {} was confirmed.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id(), + ); + *self.transaction_faux_confirmed.lock().unwrap() += 1; + } + + pub fn on_faux_transaction_mined_unconfirmed(&mut self, ptr: *mut c_void, confirmations: u64) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} Faux transaction with txID {} is mined unconfirmed with {} confirmations.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id(), + confirmations + ); + *self.transaction_faux_unconfirmed.lock().unwrap() += 1; + } + + pub fn on_transaction_send_result(&mut self, tx_id: u64, ptr: *mut c_void) { + let transaction_send_status = TransactionSendStatus::from_ptr(ptr); + println!( + "{} callbackTransactionSendResult ({}: ({}))", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + tx_id, + transaction_send_status.send_status_decode() + ); + } + + pub fn on_transaction_cancellation(&mut self, ptr: *mut c_void, reason: u64) { + let completed_transaction = CompletedTransaction::from_ptr(ptr); + println!( + "{} transaction with txID {} was cancelled with reason code {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + completed_transaction.get_transaction_id(), + reason, + ); + *self.transaction_cancelled.lock().unwrap() += 1; + } + + pub fn on_txo_validation_complete(&mut self, request_key: u64, validation_results: u64) { + println!( + "{} callbackTxoValidationComplete({}, {}).", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + request_key, + validation_results + ); + + *self.txo_validation_complete.lock().unwrap() = true; + *self.txo_validation_result.lock().unwrap() = validation_results; + } + + pub fn on_contacts_liveness_data_updated(&mut self, ptr: *mut c_void) { + let contact_liveness_data = ContactsLivenessData::from_ptr(ptr); + println!( + "{} callbackContactsLivenessUpdated: received {} from contact {} with latency {} at {} and is {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + contact_liveness_data.get_message_type(), + contact_liveness_data.get_public_key().address().get_as_hex(), + contact_liveness_data.get_latency(), + contact_liveness_data.get_last_seen(), + contact_liveness_data.get_online_status() + ); + self.wallet + .as_mut() + .unwrap() + .lock() + .unwrap() + .add_liveness_data(contact_liveness_data); + *self.contacts_liveness_data_updated.lock().unwrap() += 1; + } + + pub fn on_balance_updated(&mut self, ptr: *mut c_void) { + let balance = Balance::from_ptr(ptr); + println!( + "{} callbackBalanceUpdated: available = {}, time locked = {}, pending incoming = {}, pending outgoing = \ + {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + balance.get_available(), + balance.get_time_locked(), + balance.get_pending_incoming(), + balance.get_pending_outgoing() + ); + self.wallet.as_mut().unwrap().lock().unwrap().set_balance(balance); + } + + pub fn on_transaction_validation_complete(&mut self, request_key: u64, validation_results: u64) { + println!( + "{} callbackTransactionValidationComplete({}, {}).", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + request_key, + validation_results + ); + + *self.tx_validation_complete.lock().unwrap() = true; + *self.tx_validation_result.lock().unwrap() = validation_results; + } + + pub fn on_saf_messages_received(&mut self) { + println!( + "{} callbackSafMessageReceived().", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + ); + *self.transaction_saf_message_received.lock().unwrap() += 1; + } + + pub fn on_connectivity_status(&mut self, status: u64) { + println!( + "{} Connectivity Status Changed to {}.", + chrono::Local::now().format("%Y/%m/%d %H:%M:%S"), + status + ); + } + + pub fn reset(&mut self, wallet: Arc>) { + *self.transaction_received.lock().unwrap() = 0; + *self.transaction_reply_received.lock().unwrap() = 0; + *self.transaction_finalized.lock().unwrap() = 0; + *self.transaction_broadcast.lock().unwrap() = 0; + *self.transaction_mined.lock().unwrap() = 0; + *self.transaction_mined_unconfirmed.lock().unwrap() = 0; + *self.transaction_faux_confirmed.lock().unwrap() = 0; + *self.transaction_faux_unconfirmed.lock().unwrap() = 0; + *self.transaction_cancelled.lock().unwrap() = 0; + *self.txo_validation_complete.lock().unwrap() = false; + *self.txo_validation_result.lock().unwrap() = 0; + *self.tx_validation_complete.lock().unwrap() = false; + *self.tx_validation_result.lock().unwrap() = 0; + *self.transaction_saf_message_received.lock().unwrap() = 0; + *self.contacts_liveness_data_updated.lock().unwrap() = 0; + self.wallet = Some(wallet); + println!("wallet {:?}", self.wallet); + } + + pub fn instance() -> &'static mut Self { + unsafe { + START.call_once(|| { + INSTANCE = Some(Self::default()); + }); + INSTANCE.as_mut().unwrap() + } + } +} diff --git a/integration_tests/tests/utils/ffi/coin_preview.rs b/integration_tests/tests/utils/ffi/coin_preview.rs new file mode 100644 index 0000000000..6de7d3afdb --- /dev/null +++ b/integration_tests/tests/utils/ffi/coin_preview.rs @@ -0,0 +1,49 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::ffi_import; + +pub struct CoinPreview { + ptr: *mut c_void, +} + +impl Drop for CoinPreview { + fn drop(&mut self) { + unsafe { ffi_import::destroy_tari_coin_preview(self.ptr) }; + self.ptr = null_mut(); + } +} + +#[allow(dead_code)] +impl CoinPreview { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } +} diff --git a/integration_tests/tests/utils/ffi/comms_config.rs b/integration_tests/tests/utils/ffi/comms_config.rs new file mode 100644 index 0000000000..945eb424ab --- /dev/null +++ b/integration_tests/tests/utils/ffi/comms_config.rs @@ -0,0 +1,61 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_import, transport_config::TransportConfig}; + +pub struct CommsConfig { + ptr: *mut c_void, +} + +impl Drop for CommsConfig { + fn drop(&mut self) { + unsafe { ffi_import::comms_config_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl CommsConfig { + pub fn create(port: u64, transport_config: TransportConfig, base_dir: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::comms_config_create( + CString::new(format!("/ip4/127.0.0.1/tcp/{}", port)).unwrap().into_raw(), + transport_config.get_ptr(), + CString::new("wallet.dat").unwrap().into_raw(), + CString::new(base_dir).unwrap().into_raw(), + 30, + 600, + &mut error, + ); + } + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } +} diff --git a/integration_tests/tests/utils/ffi/completed_transaction.rs b/integration_tests/tests/utils/ffi/completed_transaction.rs new file mode 100644 index 0000000000..e9b7f2c4c1 --- /dev/null +++ b/integration_tests/tests/utils/ffi/completed_transaction.rs @@ -0,0 +1,196 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FFIString, Kernel, WalletAddress}; + +pub struct CompletedTransaction { + ptr: *mut c_void, +} + +impl Drop for CompletedTransaction { + fn drop(&mut self) { + unsafe { ffi_import::completed_transaction_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl CompletedTransaction { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_transaction_id(&self) -> u64 { + let tx_id; + let mut error = 0; + unsafe { + tx_id = ffi_import::completed_transaction_get_transaction_id(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_transaction_id error {}", error); + } + } + tx_id + } + + #[allow(dead_code)] + pub fn get_destination_tari_address(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::completed_transaction_get_destination_tari_address(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_destination_tari_address error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } + + #[allow(dead_code)] + pub fn get_source_tari_address(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::completed_transaction_get_source_tari_address(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_source_tari_address error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } + + pub fn get_transaction_kernel(&self) -> Kernel { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::completed_transaction_get_transaction_kernel(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_transaction_kernel error {}", error); + } + } + Kernel::from_ptr(ptr) + } + + #[allow(dead_code)] + pub fn get_amount(&self) -> u64 { + let amount; + let mut error = 0; + unsafe { + amount = ffi_import::completed_transaction_get_amount(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_amount error {}", error); + } + } + amount + } + + #[allow(dead_code)] + pub fn get_fee(&self) -> u64 { + let fee; + let mut error = 0; + unsafe { + fee = ffi_import::completed_transaction_get_fee(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_fee error {}", error); + } + } + fee + } + + #[allow(dead_code)] + pub fn get_timestamp(&self) -> u64 { + let timestamp; + let mut error = 0; + unsafe { + timestamp = ffi_import::completed_transaction_get_timestamp(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_timestamp error {}", error); + } + } + timestamp + } + + #[allow(dead_code)] + pub fn get_message(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::completed_transaction_get_message(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_message error {}", error); + } + } + FFIString::from_ptr(ptr as *mut i8).as_string() + } + + #[allow(dead_code)] + pub fn get_status(&self) -> i32 { + let status; + let mut error = 0; + unsafe { + status = ffi_import::completed_transaction_get_status(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_status error {}", error); + } + } + status + } + + pub fn is_outbound(&self) -> bool { + let is_outbound; + let mut error = 0; + unsafe { + is_outbound = ffi_import::completed_transaction_is_outbound(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_is_outbound error {}", error); + } + } + is_outbound + } + + #[allow(dead_code)] + pub fn completed_transaction_get_confirmations(&self) -> u64 { + let confirmations_cnt; + let mut error = 0; + unsafe { + confirmations_cnt = ffi_import::completed_transaction_get_confirmations(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_confirmations error {}", error); + } + } + confirmations_cnt + } + + #[allow(dead_code)] + pub fn get_cancellation_reason(&self) -> i32 { + let reason; + let mut error = 0; + unsafe { + reason = ffi_import::completed_transaction_get_cancellation_reason(self.ptr, &mut error); + if error > 0 { + println!("completed_transaction_get_cancellation_reason error {}", error); + } + } + reason + } +} diff --git a/integration_tests/tests/utils/ffi/completed_transactions.rs b/integration_tests/tests/utils/ffi/completed_transactions.rs new file mode 100644 index 0000000000..6f7f2f09bb --- /dev/null +++ b/integration_tests/tests/utils/ffi/completed_transactions.rs @@ -0,0 +1,67 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, CompletedTransaction}; + +pub struct CompletedTransactions { + ptr: *mut c_void, +} + +impl Drop for CompletedTransactions { + fn drop(&mut self) { + unsafe { ffi_import::completed_transactions_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl CompletedTransactions { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> u32 { + let length; + let mut error = 0; + unsafe { + length = ffi_import::completed_transactions_get_length(self.ptr, &mut error); + if error > 0 { + println!("completed_transactions_get_length error {}", error); + } + } + length + } + + pub fn get_at(&self, position: u32) -> CompletedTransaction { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::completed_transactions_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("completed_transactions_get_at error {}", error); + } + } + CompletedTransaction::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/contact.rs b/integration_tests/tests/utils/ffi/contact.rs new file mode 100644 index 0000000000..1bad8d5658 --- /dev/null +++ b/integration_tests/tests/utils/ffi/contact.rs @@ -0,0 +1,88 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_import, FFIString, WalletAddress}; + +pub struct Contact { + ptr: *mut c_void, +} + +impl Drop for Contact { + fn drop(&mut self) { + unsafe { ffi_import::contact_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl Contact { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } + + pub fn create(alias: String, address: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::contact_create( + CString::new(alias).unwrap().into_raw(), + WalletAddress::from_hex(address).get_ptr(), + &mut error, + ); + if error > 0 { + println!("contact_create error {}", error); + } + } + Self { ptr } + } + + pub fn get_alias(&self) -> String { + let mut error = 0; + let alias; + unsafe { + alias = FFIString::from_ptr(ffi_import::contact_get_alias(self.ptr, &mut error)); + if error > 0 { + println!("contact_get_alias error {}", error); + } + } + alias.as_string() + } + + pub fn get_address(&self) -> WalletAddress { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::contact_get_tari_address(self.ptr, &mut error); + if error > 0 { + println!("contact_get_tari_address error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/contacts.rs b/integration_tests/tests/utils/ffi/contacts.rs new file mode 100644 index 0000000000..1ebe653eba --- /dev/null +++ b/integration_tests/tests/utils/ffi/contacts.rs @@ -0,0 +1,70 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, Contact}; + +pub struct Contacts { + ptr: *mut c_void, +} + +impl Drop for Contacts { + fn drop(&mut self) { + unsafe { ffi_import::contacts_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl Contacts { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> u32 { + let mut error = 0; + let length; + unsafe { + length = ffi_import::contacts_get_length(self.ptr, &mut error); + if error > 0 { + println!("contacts_get_length error {}", error); + } + } + length + } + + pub fn get_at(&self, position: u32) -> Contact { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::contacts_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("contacts_get_at error {}", error); + } + } + Contact::from_ptr(ptr) + } +} + +// pub fn contacts_get_at(contacts: *mut TariContacts, position: c_uint, error_out: *mut c_int) -> *mut TariContact; diff --git a/integration_tests/tests/utils/ffi/contacts_liveness_data.rs b/integration_tests/tests/utils/ffi/contacts_liveness_data.rs new file mode 100644 index 0000000000..f5a6da7e29 --- /dev/null +++ b/integration_tests/tests/utils/ffi/contacts_liveness_data.rs @@ -0,0 +1,104 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FFIString, WalletAddress}; + +#[derive(Debug)] +pub struct ContactsLivenessData { + ptr: *mut c_void, +} + +impl Drop for ContactsLivenessData { + fn drop(&mut self) { + unsafe { ffi_import::liveness_data_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl ContactsLivenessData { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_public_key(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::liveness_data_get_public_key(self.ptr, &mut error); + if error > 0 { + println!("liveness_data_get_public_key error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } + + pub fn get_latency(&self) -> i32 { + let latency; + let mut error = 0; + unsafe { + latency = ffi_import::liveness_data_get_latency(self.ptr, &mut error); + if error > 0 { + println!("liveness_data_get_latency error {}", error); + } + } + latency + } + + pub fn get_last_seen(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::liveness_data_get_last_seen(self.ptr, &mut error); + if error > 0 { + println!("liveness_data_get_last_seen error {}", error); + } + } + FFIString::from_ptr(ptr).as_string() + } + + pub fn get_message_type(&self) -> i32 { + let message_type; + let mut error = 0; + unsafe { + message_type = ffi_import::liveness_data_get_message_type(self.ptr, &mut error); + if error > 0 { + println!("liveness_data_get_message_type error {}", error); + } + } + message_type + } + + pub fn get_online_status(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::liveness_data_get_online_status(self.ptr, &mut error); + if error > 0 { + println!("liveness_data_get_online_status error {}", error); + } + } + FFIString::from_ptr(ptr as *mut i8).as_string() + } +} diff --git a/integration_tests/tests/utils/ffi/fee_per_gram_stat.rs b/integration_tests/tests/utils/ffi/fee_per_gram_stat.rs new file mode 100644 index 0000000000..5200e2905a --- /dev/null +++ b/integration_tests/tests/utils/ffi/fee_per_gram_stat.rs @@ -0,0 +1,91 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::ffi_import; + +pub struct FeePerGramStat { + ptr: *mut c_void, +} + +impl Drop for FeePerGramStat { + fn drop(&mut self) { + unsafe { ffi_import::fee_per_gram_stat_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl FeePerGramStat { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_order(&self) -> u64 { + let order; + let mut error = 0; + unsafe { + order = ffi_import::fee_per_gram_stat_get_order(self.ptr, &mut error); + if error > 0 { + println!("fee_per_gram_stat_get_order error {}", error); + } + } + order + } + + pub fn get_min_fee_per_gram(&self) -> u64 { + let min; + let mut error = 0; + unsafe { + min = ffi_import::fee_per_gram_stat_get_min_fee_per_gram(self.ptr, &mut error); + if error > 0 { + println!("fee_per_gram_stat_get_min_fee_per_gram error {}", error); + } + } + min + } + + pub fn get_avg_fee_per_gram(&self) -> u64 { + let avg; + let mut error = 0; + unsafe { + avg = ffi_import::fee_per_gram_stat_get_avg_fee_per_gram(self.ptr, &mut error); + if error > 0 { + println!("fee_per_gram_stat_get_avg_fee_per_gram error {}", error); + } + } + avg + } + + pub fn get_max_fee_per_gram(&self) -> u64 { + let max; + let mut error = 0; + unsafe { + max = ffi_import::fee_per_gram_stat_get_max_fee_per_gram(self.ptr, &mut error); + if error > 0 { + println!("fee_per_gram_stat_get_max_fee_per_gram error {}", error); + } + } + max + } +} diff --git a/integration_tests/tests/utils/ffi/fee_per_gram_stats.rs b/integration_tests/tests/utils/ffi/fee_per_gram_stats.rs new file mode 100644 index 0000000000..242ab69375 --- /dev/null +++ b/integration_tests/tests/utils/ffi/fee_per_gram_stats.rs @@ -0,0 +1,67 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FeePerGramStat}; + +pub struct FeePerGramStats { + ptr: *mut c_void, +} + +impl Drop for FeePerGramStats { + fn drop(&mut self) { + unsafe { ffi_import::fee_per_gram_stats_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl FeePerGramStats { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> u32 { + let length; + let mut error = 0; + unsafe { + length = ffi_import::fee_per_gram_stats_get_length(self.ptr, &mut error); + if error > 0 { + println!("fee_per_gram_stats_get_length error {}", error); + } + } + length + } + + pub fn get_at(&self, position: u32) -> FeePerGramStat { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::fee_per_gram_stats_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("fee_per_gram_stats_get_at error {}", error); + } + } + FeePerGramStat::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/ffi_bytes.rs b/integration_tests/tests/utils/ffi/ffi_bytes.rs new file mode 100644 index 0000000000..123ec05cd7 --- /dev/null +++ b/integration_tests/tests/utils/ffi/ffi_bytes.rs @@ -0,0 +1,86 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; +use tari_utilities::{hex, ByteArray}; + +use super::ffi_import; + +pub struct FFIBytes { + ptr: *mut c_void, +} + +impl Drop for FFIBytes { + fn drop(&mut self) { + unsafe { ffi_import::byte_vector_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl FFIBytes { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + fn get_length(&self) -> usize { + let mut error = 0; + let length; + unsafe { + length = ffi_import::byte_vector_get_length(self.ptr, &mut error) as usize; + if error > 0 { + println!("byte_vector_get_length error {}", error); + } + } + length + } + + fn get_at(&self, i: u32) -> u8 { + let mut error = 0; + let byte; + unsafe { + byte = ffi_import::byte_vector_get_at(self.ptr, i, &mut error); + if error > 0 { + println!("byte_vector_get_at error {}", error); + } + } + byte + } + + pub fn get_vec(&self) -> Vec { + let mut data = Vec::with_capacity(self.get_length()); + for i in 0..self.get_length() { + data.push(self.get_at(i as u32)); + } + data + } + + pub fn get_as_hex(&self) -> String { + let data = self.get_vec(); + hex::to_hex(data.as_bytes()) + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } +} diff --git a/integration_tests/tests/utils/ffi/ffi_import.rs b/integration_tests/tests/utils/ffi/ffi_import.rs new file mode 100644 index 0000000000..144069025d --- /dev/null +++ b/integration_tests/tests/utils/ffi/ffi_import.rs @@ -0,0 +1,590 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_ushort, c_void}; + +pub type TariTransportConfig = c_void; +pub type TariCommsConfig = c_void; +pub type TariSeedWords = c_void; +pub type TariPendingInboundTransaction = c_void; +pub type TariCompletedTransaction = c_void; +pub type TariTransactionSendStatus = c_void; +pub type TariContactsLivenessData = c_void; +pub type TariBalance = c_void; +pub type TariWallet = c_void; +pub type TariWalletAddress = c_void; +pub type ByteVector = c_void; +#[allow(dead_code)] +pub type TariFeePerGramStat = c_void; +#[allow(dead_code)] +pub type TariTypeTag = c_void; +pub type TariVector = c_void; +pub type TariCoinPreview = c_void; +pub type TariTransactionKernel = c_void; +pub type TariPublicKey = c_void; +#[allow(dead_code)] +pub type TariPublicKeys = c_void; +#[allow(dead_code)] +pub type TariPrivateKey = c_void; +#[allow(dead_code)] +pub type TariComAndPubSignature = c_void; +#[allow(dead_code)] +pub type TariOutputFeatures = c_void; +#[allow(dead_code)] +pub type TariCovenant = c_void; +#[allow(dead_code)] +pub type TariEncryptedValue = c_void; +#[allow(dead_code)] +pub type TariUnblindedOutput = c_void; +#[allow(dead_code)] +pub type TariUnblindedOutputs = c_void; +pub type TariContact = c_void; +pub type TariContacts = c_void; +pub type TariCompletedTransactions = c_void; +pub type TariPendingOutboundTransactions = c_void; +pub type TariPendingOutboundTransaction = c_void; +pub type TariPendingInboundTransactions = c_void; +#[allow(dead_code)] +pub type TariUtxoSort = c_void; +#[allow(dead_code)] +pub type EmojiSet = c_void; +#[allow(dead_code)] +pub type TariFeePerGramStats = c_void; + +#[cfg_attr(windows, link(name = "tari_wallet_ffi.dll"))] +#[cfg_attr(not(windows), link(name = "tari_wallet_ffi"))] +#[allow(dead_code)] +extern "C" { + pub fn create_tari_vector(tag: TariTypeTag) -> *mut TariVector; + pub fn tari_vector_push_string(tv: *mut TariVector, s: *const c_char, error_ptr: *mut i32); + pub fn destroy_tari_vector(v: *mut TariVector); + pub fn destroy_tari_coin_preview(p: *mut TariCoinPreview); + pub fn string_destroy(ptr: *mut c_char); + pub fn transaction_kernel_get_excess_hex(kernel: *mut TariTransactionKernel, error_out: *mut c_int) -> *mut c_char; + pub fn transaction_kernel_get_excess_public_nonce_hex( + kernel: *mut TariTransactionKernel, + error_out: *mut c_int, + ) -> *mut c_char; + pub fn transaction_kernel_get_excess_signature_hex( + kernel: *mut TariTransactionKernel, + error_out: *mut c_int, + ) -> *mut c_char; + pub fn transaction_kernel_destroy(x: *mut TariTransactionKernel); + pub fn byte_vector_create( + byte_array: *const c_uchar, + element_count: c_uint, + error_out: *mut c_int, + ) -> *mut ByteVector; + pub fn byte_vector_destroy(bytes: *mut ByteVector); + pub fn byte_vector_get_at(ptr: *mut ByteVector, position: c_uint, error_out: *mut c_int) -> c_uchar; + pub fn byte_vector_get_length(vec: *const ByteVector, error_out: *mut c_int) -> c_uint; + pub fn public_key_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariPublicKey; + pub fn public_key_destroy(pk: *mut TariPublicKey); + pub fn public_keys_destroy(pks: *mut TariPublicKeys); + pub fn public_key_get_bytes(pk: *mut TariPublicKey, error_out: *mut c_int) -> *mut ByteVector; + pub fn public_key_from_private_key(secret_key: *mut TariPrivateKey, error_out: *mut c_int) -> *mut TariPublicKey; + pub fn public_key_from_hex(key: *const c_char, error_out: *mut c_int) -> *mut TariPublicKey; + pub fn tari_address_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariWalletAddress; + pub fn tari_address_destroy(address: *mut TariWalletAddress); + pub fn tari_address_get_bytes(address: *mut TariWalletAddress, error_out: *mut c_int) -> *mut ByteVector; + pub fn tari_address_from_private_key( + secret_key: *mut TariPrivateKey, + network: c_uint, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn tari_address_from_hex(address: *const c_char, error_out: *mut c_int) -> *mut TariWalletAddress; + pub fn tari_address_to_emoji_id(address: *mut TariWalletAddress, error_out: *mut c_int) -> *mut c_char; + pub fn emoji_id_to_tari_address(emoji: *const c_char, error_out: *mut c_int) -> *mut TariWalletAddress; + pub fn commitment_and_public_signature_create_from_bytes( + ephemeral_commitment_bytes: *const ByteVector, + ephemeral_pubkey_bytes: *const ByteVector, + u_a_bytes: *const ByteVector, + u_x_bytes: *const ByteVector, + u_y_bytes: *const ByteVector, + error_out: *mut c_int, + ) -> *mut TariComAndPubSignature; + pub fn commitment_and_public_signature_destroy(compub_sig: *mut TariComAndPubSignature); + pub fn create_tari_unblinded_output( + amount: c_ulonglong, + spending_key: *mut TariPrivateKey, + features: *mut TariOutputFeatures, + script: *const c_char, + input_data: *const c_char, + metadata_signature: *mut TariComAndPubSignature, + sender_offset_public_key: *mut TariPublicKey, + script_private_key: *mut TariPrivateKey, + covenant: *mut TariCovenant, + encrypted_value: *mut TariEncryptedValue, + minimum_value_promise: c_ulonglong, + script_lock_height: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariUnblindedOutput; + pub fn tari_unblinded_output_destroy(output: *mut TariUnblindedOutput); + pub fn unblinded_outputs_get_length(outputs: *mut TariUnblindedOutputs, error_out: *mut c_int) -> c_uint; + pub fn unblinded_outputs_get_at( + outputs: *mut TariUnblindedOutputs, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariUnblindedOutput; + pub fn unblinded_outputs_destroy(outputs: *mut TariUnblindedOutputs); + pub fn wallet_import_external_utxo_as_non_rewindable( + wallet: *mut TariWallet, + output: *mut TariUnblindedOutput, + source_address: *mut TariWalletAddress, + message: *const c_char, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn wallet_get_unspent_outputs(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariUnblindedOutputs; + pub fn private_key_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariPrivateKey; + pub fn private_key_destroy(pk: *mut TariPrivateKey); + pub fn private_key_get_bytes(pk: *mut TariPrivateKey, error_out: *mut c_int) -> *mut ByteVector; + pub fn private_key_generate() -> *mut TariPrivateKey; + pub fn private_key_from_hex(key: *const c_char, error_out: *mut c_int) -> *mut TariPrivateKey; + pub fn covenant_create_from_bytes(covenant_bytes: *const ByteVector, error_out: *mut c_int) -> *mut TariCovenant; + pub fn covenant_destroy(covenant: *mut TariCovenant); + pub fn encrypted_value_create_from_bytes( + encrypted_value_bytes: *const ByteVector, + error_out: *mut c_int, + ) -> *mut TariEncryptedValue; + pub fn encrypted_value_as_bytes( + encrypted_value: *const TariEncryptedValue, + error_out: *mut c_int, + ) -> *mut ByteVector; + pub fn encrypted_value_destroy(encrypted_value: *mut TariEncryptedValue); + pub fn output_features_create_from_bytes( + version: c_uchar, + output_type: c_ushort, + maturity: c_ulonglong, + metadata: *const ByteVector, + error_out: *mut c_int, + ) -> *mut TariOutputFeatures; + pub fn output_features_destroy(output_features: *mut TariOutputFeatures); + pub fn seed_words_create() -> *mut TariSeedWords; + pub fn seed_words_get_mnemonic_word_list_for_language( + language: *const c_char, + error_out: *mut c_int, + ) -> *mut TariSeedWords; + pub fn seed_words_get_length(seed_words: *const TariSeedWords, error_out: *mut c_int) -> c_uint; + pub fn seed_words_get_at(seed_words: *mut TariSeedWords, position: c_uint, error_out: *mut c_int) -> *mut c_char; + pub fn seed_words_push_word(seed_words: *mut TariSeedWords, word: *const c_char, error_out: *mut c_int) -> c_uchar; + pub fn seed_words_destroy(seed_words: *mut TariSeedWords); + pub fn contact_create( + alias: *const c_char, + address: *mut TariWalletAddress, + error_out: *mut c_int, + ) -> *mut TariContact; + pub fn contact_get_alias(contact: *mut TariContact, error_out: *mut c_int) -> *mut c_char; + pub fn contact_get_tari_address(contact: *mut TariContact, error_out: *mut c_int) -> *mut TariWalletAddress; + pub fn contact_destroy(contact: *mut TariContact); + pub fn contacts_get_length(contacts: *mut TariContacts, error_out: *mut c_int) -> c_uint; + pub fn contacts_get_at(contacts: *mut TariContacts, position: c_uint, error_out: *mut c_int) -> *mut TariContact; + pub fn contacts_destroy(contacts: *mut TariContacts); + pub fn liveness_data_get_public_key( + liveness_data: *mut TariContactsLivenessData, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn liveness_data_get_latency(liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int) -> c_int; + pub fn liveness_data_get_last_seen( + liveness_data: *mut TariContactsLivenessData, + error_out: *mut c_int, + ) -> *mut c_char; + pub fn liveness_data_get_message_type(liveness_data: *mut TariContactsLivenessData, error_out: *mut c_int) + -> c_int; + pub fn liveness_data_get_online_status( + liveness_data: *mut TariContactsLivenessData, + error_out: *mut c_int, + ) -> *const c_char; + pub fn liveness_data_destroy(liveness_data: *mut TariContactsLivenessData); + pub fn completed_transactions_get_length( + transactions: *mut TariCompletedTransactions, + error_out: *mut c_int, + ) -> c_uint; + pub fn completed_transactions_get_at( + transactions: *mut TariCompletedTransactions, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariCompletedTransaction; + pub fn completed_transactions_destroy(transactions: *mut TariCompletedTransactions); + pub fn pending_outbound_transactions_get_length( + transactions: *mut TariPendingOutboundTransactions, + error_out: *mut c_int, + ) -> c_uint; + pub fn pending_outbound_transactions_get_at( + transactions: *mut TariPendingOutboundTransactions, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariPendingOutboundTransaction; + pub fn pending_outbound_transactions_destroy(transactions: *mut TariPendingOutboundTransactions); + pub fn pending_inbound_transactions_get_length( + transactions: *mut TariPendingInboundTransactions, + error_out: *mut c_int, + ) -> c_uint; + pub fn pending_inbound_transactions_get_at( + transactions: *mut TariPendingInboundTransactions, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariPendingInboundTransaction; + pub fn pending_inbound_transactions_destroy(transactions: *mut TariPendingInboundTransactions); + pub fn completed_transaction_get_transaction_id( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn completed_transaction_get_destination_tari_address( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn completed_transaction_get_transaction_kernel( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> *mut TariTransactionKernel; + pub fn completed_transaction_get_source_tari_address( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn completed_transaction_get_status(transaction: *mut TariCompletedTransaction, error_out: *mut c_int) + -> c_int; + pub fn completed_transaction_get_amount( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn completed_transaction_get_fee( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn completed_transaction_get_timestamp( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn completed_transaction_get_message( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> *const c_char; + pub fn completed_transaction_is_outbound(tx: *mut TariCompletedTransaction, error_out: *mut c_int) -> bool; + pub fn completed_transaction_get_confirmations( + tx: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn completed_transaction_get_cancellation_reason( + tx: *mut TariCompletedTransaction, + error_out: *mut c_int, + ) -> c_int; + pub fn completed_transaction_destroy(transaction: *mut TariCompletedTransaction); + pub fn pending_outbound_transaction_get_transaction_id( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_outbound_transaction_get_destination_tari_address( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn pending_outbound_transaction_get_amount( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_outbound_transaction_get_fee( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_outbound_transaction_get_timestamp( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_outbound_transaction_get_message( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> *const c_char; + pub fn pending_outbound_transaction_get_status( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, + ) -> c_int; + pub fn pending_outbound_transaction_destroy(transaction: *mut TariPendingOutboundTransaction); + pub fn pending_inbound_transaction_get_transaction_id( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_inbound_transaction_get_source_tari_address( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> *mut TariWalletAddress; + pub fn pending_inbound_transaction_get_amount( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_inbound_transaction_get_timestamp( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn pending_inbound_transaction_get_message( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> *const c_char; + pub fn pending_inbound_transaction_get_status( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, + ) -> c_int; + pub fn pending_inbound_transaction_destroy(transaction: *mut TariPendingInboundTransaction); + pub fn transaction_send_status_decode(status: *const TariTransactionSendStatus, error_out: *mut c_int) -> c_uint; + pub fn transaction_send_status_destroy(status: *mut TariTransactionSendStatus); + pub fn transport_memory_create() -> *mut TariTransportConfig; + pub fn transport_tcp_create(listener_address: *const c_char, error_out: *mut c_int) -> *mut TariTransportConfig; + pub fn transport_tor_create( + control_server_address: *const c_char, + tor_cookie: *const ByteVector, + tor_port: c_ushort, + tor_proxy_bypass_for_outbound: bool, + socks_username: *const c_char, + socks_password: *const c_char, + error_out: *mut c_int, + ) -> *mut TariTransportConfig; + pub fn transport_memory_get_address(transport: *const TariTransportConfig, error_out: *mut c_int) -> *mut c_char; + pub fn transport_type_destroy(transport: *mut TariTransportConfig); + pub fn transport_config_destroy(transport: *mut TariTransportConfig); + pub fn comms_config_create( + public_address: *const c_char, + transport: *const TariTransportConfig, + database_name: *const c_char, + datastore_path: *const c_char, + discovery_timeout_in_secs: c_ulonglong, + saf_message_duration_in_secs: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariCommsConfig; + pub fn comms_config_destroy(wc: *mut TariCommsConfig); + pub fn comms_list_connected_public_keys(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariPublicKeys; + pub fn public_keys_get_length(public_keys: *const TariPublicKeys, error_out: *mut c_int) -> c_uint; + pub fn public_keys_get_at( + public_keys: *const TariPublicKeys, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariPublicKey; + pub fn wallet_create( + config: *mut TariCommsConfig, + log_path: *const c_char, + num_rolling_log_files: c_uint, + size_per_log_file_bytes: c_uint, + passphrase: *const c_char, + seed_words: *const TariSeedWords, + network_str: *const c_char, + callback_received_transaction: unsafe extern "C" fn(*mut TariPendingInboundTransaction), + callback_received_transaction_reply: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_received_finalized_transaction: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_transaction_broadcast: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_transaction_mined: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_transaction_mined_unconfirmed: unsafe extern "C" fn(*mut TariCompletedTransaction, u64), + callback_faux_transaction_confirmed: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_faux_transaction_unconfirmed: unsafe extern "C" fn(*mut TariCompletedTransaction, u64), + callback_transaction_send_result: unsafe extern "C" fn(c_ulonglong, *mut TariTransactionSendStatus), + callback_transaction_cancellation: unsafe extern "C" fn(*mut TariCompletedTransaction, u64), + callback_txo_validation_complete: unsafe extern "C" fn(u64, u64), + callback_contacts_liveness_data_updated: unsafe extern "C" fn(*mut TariContactsLivenessData), + callback_balance_updated: unsafe extern "C" fn(*mut TariBalance), + callback_transaction_validation_complete: unsafe extern "C" fn(u64, u64), + callback_saf_messages_received: unsafe extern "C" fn(), + callback_connectivity_status: unsafe extern "C" fn(u64), + recovery_in_progress: *mut bool, + error_out: *mut c_int, + ) -> *mut TariWallet; + pub fn wallet_get_balance(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariBalance; + pub fn wallet_get_utxos( + wallet: *mut TariWallet, + page: usize, + page_size: usize, + sorting: TariUtxoSort, + states: *mut TariVector, + dust_threshold: u64, + error_ptr: *mut i32, + ) -> *mut TariVector; + pub fn wallet_get_all_utxos(wallet: *mut TariWallet, error_ptr: *mut i32) -> *mut TariVector; + pub fn wallet_coin_split( + wallet: *mut TariWallet, + commitments: *mut TariVector, + number_of_splits: usize, + fee_per_gram: u64, + error_ptr: *mut i32, + ) -> u64; + pub fn wallet_coin_join( + wallet: *mut TariWallet, + commitments: *mut TariVector, + fee_per_gram: u64, + error_ptr: *mut i32, + ) -> u64; + pub fn wallet_preview_coin_join( + wallet: *mut TariWallet, + commitments: *mut TariVector, + fee_per_gram: u64, + error_ptr: *mut i32, + ) -> *mut TariCoinPreview; + pub fn wallet_preview_coin_split( + wallet: *mut TariWallet, + commitments: *mut TariVector, + number_of_splits: usize, + fee_per_gram: u64, + error_ptr: *mut i32, + ) -> *mut TariCoinPreview; + pub fn wallet_sign_message(wallet: *mut TariWallet, msg: *const c_char, error_out: *mut c_int) -> *mut c_char; + pub fn wallet_verify_message_signature( + wallet: *mut TariWallet, + public_key: *mut TariPublicKey, + hex_sig_nonce: *const c_char, + msg: *const c_char, + error_out: *mut c_int, + ) -> bool; + pub fn wallet_add_base_node_peer( + wallet: *mut TariWallet, + public_key: *mut TariPublicKey, + address: *const c_char, + error_out: *mut c_int, + ) -> bool; + pub fn wallet_upsert_contact(wallet: *mut TariWallet, contact: *mut TariContact, error_out: *mut c_int) -> bool; + pub fn wallet_remove_contact(wallet: *mut TariWallet, contact: *mut TariContact, error_out: *mut c_int) -> bool; + pub fn balance_get_available(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong; + pub fn balance_get_time_locked(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong; + pub fn balance_get_pending_incoming(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong; + pub fn balance_get_pending_outgoing(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong; + pub fn balance_destroy(balance: *mut TariBalance); + pub fn wallet_send_transaction( + wallet: *mut TariWallet, + destination: *mut TariWalletAddress, + amount: c_ulonglong, + commitments: *mut TariVector, + fee_per_gram: c_ulonglong, + message: *const c_char, + one_sided: bool, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn wallet_get_fee_estimate( + wallet: *mut TariWallet, + amount: c_ulonglong, + commitments: *mut TariVector, + fee_per_gram: c_ulonglong, + num_kernels: c_ulonglong, + num_outputs: c_ulonglong, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn wallet_get_num_confirmations_required(wallet: *mut TariWallet, error_out: *mut c_int) -> c_ulonglong; + pub fn wallet_set_num_confirmations_required(wallet: *mut TariWallet, num: c_ulonglong, error_out: *mut c_int); + pub fn wallet_get_contacts(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariContacts; + pub fn wallet_get_completed_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, + ) -> *mut TariCompletedTransactions; + pub fn wallet_get_pending_inbound_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, + ) -> *mut TariPendingInboundTransactions; + pub fn wallet_get_pending_outbound_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, + ) -> *mut TariPendingOutboundTransactions; + pub fn wallet_get_cancelled_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, + ) -> *mut TariCompletedTransactions; + pub fn wallet_get_completed_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariCompletedTransaction; + pub fn wallet_get_pending_inbound_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariPendingInboundTransaction; + pub fn wallet_get_pending_outbound_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariPendingOutboundTransaction; + pub fn wallet_get_cancelled_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, + ) -> *mut TariCompletedTransaction; + pub fn wallet_get_tari_address(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariWalletAddress; + pub fn wallet_cancel_pending_transaction( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, + ) -> bool; + pub fn wallet_start_txo_validation(wallet: *mut TariWallet, error_out: *mut c_int) -> c_ulonglong; + pub fn wallet_start_transaction_validation(wallet: *mut TariWallet, error_out: *mut c_int) -> c_ulonglong; + pub fn wallet_restart_transaction_broadcast(wallet: *mut TariWallet, error_out: *mut c_int) -> bool; + pub fn wallet_get_seed_words(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariSeedWords; + pub fn wallet_set_low_power_mode(wallet: *mut TariWallet, error_out: *mut c_int); + pub fn wallet_set_normal_power_mode(wallet: *mut TariWallet, error_out: *mut c_int); + pub fn wallet_set_key_value( + wallet: *mut TariWallet, + key: *const c_char, + value: *const c_char, + error_out: *mut c_int, + ) -> bool; + pub fn wallet_get_value(wallet: *mut TariWallet, key: *const c_char, error_out: *mut c_int) -> *mut c_char; + pub fn wallet_clear_value(wallet: *mut TariWallet, key: *const c_char, error_out: *mut c_int) -> bool; + pub fn wallet_is_recovery_in_progress(wallet: *mut TariWallet, error_out: *mut c_int) -> bool; + pub fn wallet_start_recovery( + wallet: *mut TariWallet, + base_node_public_key: *mut TariPublicKey, + recovery_progress_callback: unsafe extern "C" fn(u8, u64, u64), + recovered_output_message: *const c_char, + error_out: *mut c_int, + ) -> bool; + pub fn wallet_set_one_sided_payment_message( + wallet: *mut TariWallet, + message: *const c_char, + error_out: *mut c_int, + ) -> bool; + pub fn get_emoji_set() -> *mut EmojiSet; + pub fn emoji_set_get_length(emoji_set: *const EmojiSet, error_out: *mut c_int) -> c_uint; + pub fn emoji_set_get_at(emoji_set: *const EmojiSet, position: c_uint, error_out: *mut c_int) -> *mut ByteVector; + pub fn emoji_set_destroy(emoji_set: *mut EmojiSet); + pub fn wallet_destroy(wallet: *mut TariWallet); + pub fn log_debug_message(msg: *const c_char, error_out: *mut c_int); + pub fn wallet_get_fee_per_gram_stats( + wallet: *mut TariWallet, + count: c_uint, + error_out: *mut c_int, + ) -> *mut TariFeePerGramStats; + pub fn fee_per_gram_stats_get_length(fee_per_gram_stats: *mut TariFeePerGramStats, error_out: *mut c_int) + -> c_uint; + pub fn fee_per_gram_stats_get_at( + fee_per_gram_stats: *mut TariFeePerGramStats, + position: c_uint, + error_out: *mut c_int, + ) -> *mut TariFeePerGramStat; + pub fn fee_per_gram_stats_destroy(fee_per_gram_stats: *mut TariFeePerGramStats); + pub fn fee_per_gram_stat_get_order( + fee_per_gram_stat: *mut TariFeePerGramStat, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn fee_per_gram_stat_get_min_fee_per_gram( + fee_per_gram_stat: *mut TariFeePerGramStat, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn fee_per_gram_stat_get_avg_fee_per_gram( + fee_per_gram_stat: *mut TariFeePerGramStat, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn fee_per_gram_stat_get_max_fee_per_gram( + fee_per_gram_stat: *mut TariFeePerGramStat, + error_out: *mut c_int, + ) -> c_ulonglong; + pub fn fee_per_gram_stat_destroy(fee_per_gram_stat: *mut TariFeePerGramStat); +} diff --git a/integration_tests/tests/utils/ffi/ffi_string.rs b/integration_tests/tests/utils/ffi/ffi_string.rs new file mode 100644 index 0000000000..e281f89b8b --- /dev/null +++ b/integration_tests/tests/utils/ffi/ffi_string.rs @@ -0,0 +1,52 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CStr, ptr::null_mut}; + +use libc::c_char; + +use super::ffi_import; + +pub struct FFIString { + ptr: *mut c_char, +} + +impl Drop for FFIString { + fn drop(&mut self) { + unsafe { ffi_import::string_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl FFIString { + pub fn from_ptr(ptr: *mut c_char) -> Self { + Self { ptr } + } + + pub fn as_str(&self) -> &str { + unsafe { CStr::from_ptr(self.ptr).to_str().unwrap() } + } + + pub fn as_string(&self) -> String { + self.as_str().to_owned() + } +} diff --git a/integration_tests/tests/utils/ffi/kernel.rs b/integration_tests/tests/utils/ffi/kernel.rs new file mode 100644 index 0000000000..604214bbd2 --- /dev/null +++ b/integration_tests/tests/utils/ffi/kernel.rs @@ -0,0 +1,79 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FFIString}; + +pub struct Kernel { + ptr: *mut c_void, +} + +impl Drop for Kernel { + fn drop(&mut self) { + unsafe { ffi_import::transaction_kernel_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl Kernel { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_excess_hex(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::transaction_kernel_get_excess_hex(self.ptr, &mut error); + if error > 0 { + println!("transaction_kernel_get_excess_hex error {}", error); + } + } + FFIString::from_ptr(ptr).as_string() + } + + pub fn get_excess_public_nonce_hex(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::transaction_kernel_get_excess_public_nonce_hex(self.ptr, &mut error); + if error > 0 { + println!("transaction_kernel_get_excess_public_nonce_hex error {}", error); + } + } + FFIString::from_ptr(ptr).as_string() + } + + pub fn get_excess_signature_hex(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::transaction_kernel_get_excess_signature_hex(self.ptr, &mut error); + if error > 0 { + println!("transaction_kernel_get_excess_signature_hex error {}", error); + } + } + FFIString::from_ptr(ptr).as_string() + } +} diff --git a/integration_tests/tests/utils/ffi/mod.rs b/integration_tests/tests/utils/ffi/mod.rs new file mode 100644 index 0000000000..79e9fcf7c3 --- /dev/null +++ b/integration_tests/tests/utils/ffi/mod.rs @@ -0,0 +1,76 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod comms_config; +pub mod ffi_bytes; +pub mod ffi_import; +pub use comms_config::CommsConfig; +mod wallet_address; +pub use wallet_address::WalletAddress; +mod transport_config; +pub use transport_config::TransportConfig; +mod wallet; +pub use wallet::Wallet; +mod public_key; +pub use public_key::PublicKey; +mod public_keys; +pub use public_keys::PublicKeys; +mod private_key; +pub use private_key::PrivateKey; +mod ffi_string; +pub use ffi_string::FFIString; +mod seed_words; +pub use seed_words::SeedWords; +mod contact; +pub use contact::Contact; +mod contacts; +pub use contacts::Contacts; +mod balance; +pub use balance::Balance; +mod vector; +pub use vector::Vector; +mod coin_preview; +pub use coin_preview::CoinPreview; +mod pending_outbound_transactions; +pub use pending_outbound_transactions::PendingOutboundTransactions; +mod pending_outbound_transaction; +pub use pending_outbound_transaction::PendingOutboundTransaction; +mod pending_inbound_transactions; +pub use pending_inbound_transactions::PendingInboundTransactions; +mod pending_inbound_transaction; +pub use pending_inbound_transaction::PendingInboundTransaction; +mod completed_transactions; +pub use completed_transactions::CompletedTransactions; +mod completed_transaction; +pub use completed_transaction::CompletedTransaction; +mod kernel; +pub use kernel::Kernel; +mod callbacks; +pub use callbacks::Callbacks; +mod transaction_send_status; +pub use transaction_send_status::TransactionSendStatus; +mod contacts_liveness_data; +pub use contacts_liveness_data::ContactsLivenessData; +mod fee_per_gram_stats; +pub use fee_per_gram_stats::FeePerGramStats; +mod fee_per_gram_stat; +pub use fee_per_gram_stat::FeePerGramStat; diff --git a/integration_tests/tests/utils/ffi/pending_inbound_transaction.rs b/integration_tests/tests/utils/ffi/pending_inbound_transaction.rs new file mode 100644 index 0000000000..8845f43673 --- /dev/null +++ b/integration_tests/tests/utils/ffi/pending_inbound_transaction.rs @@ -0,0 +1,120 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FFIString, WalletAddress}; + +pub struct PendingInboundTransaction { + ptr: *mut c_void, +} + +impl Drop for PendingInboundTransaction { + fn drop(&mut self) { + unsafe { ffi_import::pending_inbound_transaction_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl PendingInboundTransaction { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_transaction_id(&self) -> u64 { + let tx_id; + let mut error = 0; + unsafe { + tx_id = ffi_import::pending_inbound_transaction_get_transaction_id(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_transaction_id error {}", error); + } + } + tx_id + } + + #[allow(dead_code)] + pub fn get_source_tari_address(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_inbound_transaction_get_source_tari_address(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_source_tari_address error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } + + #[allow(dead_code)] + pub fn get_amount(&self) -> u64 { + let amount; + let mut error = 0; + unsafe { + amount = ffi_import::pending_inbound_transaction_get_amount(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_amount error {}", error); + } + } + amount + } + + #[allow(dead_code)] + pub fn get_timestamp(&self) -> u64 { + let timestamp; + let mut error = 0; + unsafe { + timestamp = ffi_import::pending_inbound_transaction_get_timestamp(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_timestamp error {}", error); + } + } + timestamp + } + + #[allow(dead_code)] + pub fn get_message(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_inbound_transaction_get_message(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_message error {}", error); + } + } + FFIString::from_ptr(ptr as *mut i8).as_string() + } + + #[allow(dead_code)] + pub fn get_status(&self) -> i32 { + let status; + let mut error = 0; + unsafe { + status = ffi_import::pending_inbound_transaction_get_status(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transaction_get_status error {}", error); + } + } + status + } +} diff --git a/integration_tests/tests/utils/ffi/pending_inbound_transactions.rs b/integration_tests/tests/utils/ffi/pending_inbound_transactions.rs new file mode 100644 index 0000000000..fbc3a3e925 --- /dev/null +++ b/integration_tests/tests/utils/ffi/pending_inbound_transactions.rs @@ -0,0 +1,68 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, PendingInboundTransaction}; + +pub struct PendingInboundTransactions { + ptr: *mut c_void, +} + +impl Drop for PendingInboundTransactions { + fn drop(&mut self) { + unsafe { ffi_import::pending_inbound_transactions_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl PendingInboundTransactions { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> u32 { + let length; + let mut error = 0; + unsafe { + length = ffi_import::pending_inbound_transactions_get_length(self.ptr, &mut error); + if error > 0 { + println!("pending_inbound_transactions_get_length error {}", error); + } + } + length + } + + #[allow(dead_code)] + pub fn get_at(&self, position: u32) -> PendingInboundTransaction { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_inbound_transactions_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("pending_inbound_transactions_get_at error {}", error); + } + } + PendingInboundTransaction::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/pending_outbound_transaction.rs b/integration_tests/tests/utils/ffi/pending_outbound_transaction.rs new file mode 100644 index 0000000000..31e392ef97 --- /dev/null +++ b/integration_tests/tests/utils/ffi/pending_outbound_transaction.rs @@ -0,0 +1,136 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, FFIString, WalletAddress}; + +pub struct PendingOutboundTransaction { + ptr: *mut c_void, +} + +impl Drop for PendingOutboundTransaction { + fn drop(&mut self) { + unsafe { ffi_import::pending_outbound_transaction_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl PendingOutboundTransaction { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_transaction_id(&self) -> u64 { + let tx_id; + let mut error = 0; + unsafe { + tx_id = ffi_import::pending_outbound_transaction_get_transaction_id(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_transaction_id error {}", error); + } + } + tx_id + } + + #[allow(dead_code)] + pub fn get_destination_tari_address(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_outbound_transaction_get_destination_tari_address(self.ptr, &mut error); + if error > 0 { + println!( + "pending_outbound_transaction_get_destination_tari_address error {}", + error + ); + } + } + WalletAddress::from_ptr(ptr) + } + + #[allow(dead_code)] + pub fn get_amount(&self) -> u64 { + let amount; + let mut error = 0; + unsafe { + amount = ffi_import::pending_outbound_transaction_get_amount(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_amount error {}", error); + } + } + amount + } + + #[allow(dead_code)] + pub fn get_fee(&self) -> u64 { + let fee; + let mut error = 0; + unsafe { + fee = ffi_import::pending_outbound_transaction_get_fee(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_fee error {}", error); + } + } + fee + } + + #[allow(dead_code)] + pub fn get_timestamp(&self) -> u64 { + let timestamp; + let mut error = 0; + unsafe { + timestamp = ffi_import::pending_outbound_transaction_get_timestamp(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_timestamp error {}", error); + } + } + timestamp + } + + #[allow(dead_code)] + pub fn get_message(&self) -> String { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_outbound_transaction_get_message(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_message error {}", error); + } + } + FFIString::from_ptr(ptr as *mut i8).as_string() + } + + #[allow(dead_code)] + pub fn get_status(&self) -> i32 { + let status; + let mut error = 0; + unsafe { + status = ffi_import::pending_outbound_transaction_get_status(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transaction_get_status error {}", error); + } + } + status + } +} diff --git a/integration_tests/tests/utils/ffi/pending_outbound_transactions.rs b/integration_tests/tests/utils/ffi/pending_outbound_transactions.rs new file mode 100644 index 0000000000..c1fc304fa9 --- /dev/null +++ b/integration_tests/tests/utils/ffi/pending_outbound_transactions.rs @@ -0,0 +1,67 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, PendingOutboundTransaction}; + +pub struct PendingOutboundTransactions { + ptr: *mut c_void, +} + +impl Drop for PendingOutboundTransactions { + fn drop(&mut self) { + unsafe { ffi_import::pending_outbound_transactions_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl PendingOutboundTransactions { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> u32 { + let length; + let mut error = 0; + unsafe { + length = ffi_import::pending_outbound_transactions_get_length(self.ptr, &mut error); + if error > 0 { + println!("pending_outbound_transactions_get_length error {}", error); + } + } + length + } + + pub fn get_at(&self, position: u32) -> PendingOutboundTransaction { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::pending_outbound_transactions_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("pending_outbound_transactions_get_at error {}", error); + } + } + PendingOutboundTransaction::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/private_key.rs b/integration_tests/tests/utils/ffi/private_key.rs new file mode 100644 index 0000000000..ae47547124 --- /dev/null +++ b/integration_tests/tests/utils/ffi/private_key.rs @@ -0,0 +1,92 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_bytes::FFIBytes, ffi_import}; + +pub struct PrivateKey { + ptr: *mut c_void, +} + +impl Drop for PrivateKey { + fn drop(&mut self) { + unsafe { ffi_import::private_key_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl PrivateKey { + #[allow(dead_code)] + pub fn create(bytes: FFIBytes) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::private_key_create(bytes.get_ptr(), &mut error); + if error > 0 { + println!("private_key_create error {}", error); + } + } + Self { ptr } + } + + #[allow(dead_code)] + pub fn generate() -> Self { + let ptr; + unsafe { + ptr = ffi_import::private_key_generate(); + } + Self { ptr } + } + + #[allow(dead_code)] + pub fn from_hex(key: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::private_key_from_hex(CString::new(key).unwrap().into_raw(), &mut error); + if error > 0 { + println!("private_key_from_hex error {}", error); + } + } + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } + + #[allow(dead_code)] + pub fn get_bytes(&self) -> FFIBytes { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::private_key_get_bytes(self.ptr, &mut error); + if error > 0 { + println!("private_key_get_bytes error {}", error); + } + } + FFIBytes::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/public_key.rs b/integration_tests/tests/utils/ffi/public_key.rs new file mode 100644 index 0000000000..5668e253c7 --- /dev/null +++ b/integration_tests/tests/utils/ffi/public_key.rs @@ -0,0 +1,98 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_bytes::FFIBytes, ffi_import, PrivateKey}; + +pub struct PublicKey { + ptr: *mut c_void, +} + +impl Drop for PublicKey { + fn drop(&mut self) { + unsafe { ffi_import::public_key_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl PublicKey { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + #[allow(dead_code)] + pub fn create(bytes: FFIBytes) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::public_key_create(bytes.get_ptr(), &mut error); + if error > 0 { + println!("public_key_create error {}", error); + } + } + Self { ptr } + } + + #[allow(dead_code)] + pub fn from_private_key(private_key: PrivateKey) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::public_key_from_private_key(private_key.get_ptr(), &mut error); + if error > 0 { + println!("public_key_from_private_key error {}", error); + } + } + Self { ptr } + } + + pub fn from_hex(key: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::public_key_from_hex(CString::new(key).unwrap().into_raw(), &mut error); + if error > 0 { + println!("public_key_from_private_key error {}", error); + } + } + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } + + pub fn get_bytes(&self) -> FFIBytes { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::public_key_get_bytes(self.ptr, &mut error); + if error > 0 { + println!("public_key_get_bytes error {}", error); + } + } + FFIBytes::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/public_keys.rs b/integration_tests/tests/utils/ffi/public_keys.rs new file mode 100644 index 0000000000..562f6bd378 --- /dev/null +++ b/integration_tests/tests/utils/ffi/public_keys.rs @@ -0,0 +1,68 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::{ffi_import, PublicKey}; + +pub struct PublicKeys { + ptr: *mut c_void, +} + +impl Drop for PublicKeys { + fn drop(&mut self) { + unsafe { ffi_import::public_keys_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl PublicKeys { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_length(&self) -> usize { + let mut error = 0; + let length; + unsafe { + length = ffi_import::public_keys_get_length(self.ptr, &mut error); + if error > 0 { + println!("public_keys_get_length error {}", error); + } + } + length as usize + } + + pub fn get_public_key_at(&self, position: u32) -> PublicKey { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::public_keys_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("public_keys_get_length error {}", error); + } + } + PublicKey::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/seed_words.rs b/integration_tests/tests/utils/ffi/seed_words.rs new file mode 100644 index 0000000000..02fec29641 --- /dev/null +++ b/integration_tests/tests/utils/ffi/seed_words.rs @@ -0,0 +1,103 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_import, FFIString}; + +pub struct SeedWords { + ptr: *mut c_void, +} + +impl Drop for SeedWords { + fn drop(&mut self) { + unsafe { ffi_import::seed_words_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl SeedWords { + pub fn create() -> Self { + let ptr; + unsafe { + ptr = ffi_import::seed_words_create(); + } + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } + + pub fn get_mnemonic_word_list_for_language(language: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::seed_words_get_mnemonic_word_list_for_language( + CString::new(language).unwrap().into_raw(), + &mut error, + ); + if error > 0 { + println!("seed_words_get_mnemonic_word_list_for_language error {}", error); + } + } + Self { ptr } + } + + pub fn get_length(&self) -> usize { + let mut error = 0; + let length; + unsafe { + length = ffi_import::seed_words_get_length(self.ptr, &mut error); + if error > 0 { + println!("seed_words_get_length error {}", error); + } + } + length as usize + } + + pub fn get_at(&self, position: u32) -> FFIString { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::seed_words_get_at(self.ptr, position, &mut error); + if error > 0 { + println!("seed_words_get_at error {}", error); + } + } + FFIString::from_ptr(ptr) + } + + pub fn push_word(&self, word: String) -> u8 { + let mut error = 0; + let result; + unsafe { + result = ffi_import::seed_words_push_word(self.ptr, CString::new(word).unwrap().into_raw(), &mut error); + if error > 0 { + println!("seed_words_push_word error {}", error); + } + } + result + } +} diff --git a/integration_tests/tests/utils/ffi/transaction_send_status.rs b/integration_tests/tests/utils/ffi/transaction_send_status.rs new file mode 100644 index 0000000000..da177b46d9 --- /dev/null +++ b/integration_tests/tests/utils/ffi/transaction_send_status.rs @@ -0,0 +1,55 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::c_void; + +use super::ffi_import; + +pub struct TransactionSendStatus { + ptr: *mut c_void, +} + +impl Drop for TransactionSendStatus { + fn drop(&mut self) { + unsafe { ffi_import::transaction_send_status_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} +impl TransactionSendStatus { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn send_status_decode(&self) -> u32 { + let status; + let mut error = 0; + unsafe { + status = ffi_import::transaction_send_status_decode(self.ptr, &mut error); + if error > 0 { + println!("transaction_send_status_decode error {}", error); + } + } + status + } +} diff --git a/integration_tests/tests/utils/ffi/transport_config.rs b/integration_tests/tests/utils/ffi/transport_config.rs new file mode 100644 index 0000000000..5223ac6a70 --- /dev/null +++ b/integration_tests/tests/utils/ffi/transport_config.rs @@ -0,0 +1,56 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ptr::null_mut; + +use libc::{c_int, c_void}; + +use super::ffi_import; + +pub struct TransportConfig { + ptr: *mut c_void, +} + +impl Drop for TransportConfig { + fn drop(&mut self) { + unsafe { ffi_import::transport_config_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl TransportConfig { + pub fn create_tcp(listener_address: *const i8) -> Self { + let ptr; + let mut error: c_int = 0; + unsafe { + ptr = ffi_import::transport_tcp_create(listener_address, &mut error); + if error > 0 { + println!("transport_tcp_create error {}", error); + } + } + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } +} diff --git a/integration_tests/tests/utils/ffi/vector.rs b/integration_tests/tests/utils/ffi/vector.rs new file mode 100644 index 0000000000..a48d824601 --- /dev/null +++ b/integration_tests/tests/utils/ffi/vector.rs @@ -0,0 +1,58 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::ffi_import::{self}; + +pub struct Vector { + ptr: *mut c_void, +} + +impl Drop for Vector { + fn drop(&mut self) { + unsafe { ffi_import::destroy_tari_vector(self.ptr) }; + self.ptr = null_mut(); + } +} +#[allow(dead_code)] +impl Vector { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } + + pub fn push_string(&self, s: String) { + let mut error = 0; + unsafe { + ffi_import::tari_vector_push_string(self.ptr, CString::new(s).unwrap().into_raw(), &mut error); + if error > 0 { + println!("tari_vector_push_string error {}", error); + } + } + } +} diff --git a/integration_tests/tests/utils/ffi/wallet.rs b/integration_tests/tests/utils/ffi/wallet.rs new file mode 100644 index 0000000000..316ee99cff --- /dev/null +++ b/integration_tests/tests/utils/ffi/wallet.rs @@ -0,0 +1,429 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ + ffi::CString, + ptr::null_mut, + sync::{Arc, Mutex}, +}; + +use callbacks::Callbacks; +use indexmap::IndexMap; +use libc::{c_ulonglong, c_void}; + +use super::{ + ffi_import::{ + self, + wallet_create, + TariBalance, + TariCompletedTransaction, + TariContactsLivenessData, + TariPendingInboundTransaction, + TariTransactionSendStatus, + TariWallet, + }, + Balance, + CommsConfig, + CompletedTransactions, + Contact, + Contacts, + ContactsLivenessData, + FeePerGramStats, + PendingInboundTransactions, + PendingOutboundTransactions, + PublicKey, + PublicKeys, + WalletAddress, +}; +use crate::utils::ffi::callbacks; + +extern "C" fn callback_received_transaction(ptr: *mut TariPendingInboundTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_received_transaction(ptr); + // println!("callback_received_transaction"); +} +extern "C" fn callback_received_transaction_reply(ptr: *mut TariCompletedTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_received_transaction_reply(ptr); + // println!("callback_received_transaction_reply"); +} +extern "C" fn callback_received_finalized_transaction(ptr: *mut TariCompletedTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_received_finalized_transaction(ptr); + // println!("callback_received_finalized_transaction"); +} +extern "C" fn callback_transaction_broadcast(ptr: *mut TariCompletedTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_broadcast(ptr); + // println!("callback_transaction_broadcast"); +} +extern "C" fn callback_transaction_mined(ptr: *mut TariCompletedTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_mined(ptr); + // println!("callback_transaction_mined"); +} +extern "C" fn callback_transaction_mined_unconfirmed(ptr: *mut TariCompletedTransaction, confirmations: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_mined_unconfirmed(ptr, confirmations); + // println!("callback_transaction_mined_unconfirmed"); +} +extern "C" fn callback_faux_transaction_confirmed(ptr: *mut TariCompletedTransaction) { + let callbacks = Callbacks::instance(); + callbacks.on_faux_transaction_confirmed(ptr); + // println!("callback_faux_transaction_confirmed"); +} +extern "C" fn callback_faux_transaction_unconfirmed(ptr: *mut TariCompletedTransaction, confirmations: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_faux_transaction_mined_unconfirmed(ptr, confirmations); + // println!("callback_faux_transaction_unconfirmed"); +} +extern "C" fn callback_transaction_send_result(tx_id: c_ulonglong, ptr: *mut TariTransactionSendStatus) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_send_result(tx_id, ptr); + // println!("callback_transaction_send_result"); +} +extern "C" fn callback_transaction_cancellation(ptr: *mut TariCompletedTransaction, reason: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_cancellation(ptr, reason); + // println!("callback_transaction_cancellation"); +} +extern "C" fn callback_txo_validation_complete(request_key: u64, validation_results: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_txo_validation_complete(request_key, validation_results); + // println!("callback_txo_validation_complete"); +} +extern "C" fn callback_contacts_liveness_data_updated(ptr: *mut TariContactsLivenessData) { + let callbacks = Callbacks::instance(); + callbacks.on_contacts_liveness_data_updated(ptr); + // println!("callback_contacts_liveness_data_updated"); +} +extern "C" fn callback_balance_updated(ptr: *mut TariBalance) { + let callbacks = Callbacks::instance(); + callbacks.on_balance_updated(ptr); + // println!("callback_balance_updated"); +} +extern "C" fn callback_transaction_validation_complete(request_key: u64, validation_results: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_transaction_validation_complete(request_key, validation_results); + // println!("callback_transaction_validation_complete"); +} +extern "C" fn callback_saf_messages_received() { + let callbacks = Callbacks::instance(); + callbacks.on_saf_messages_received(); + // println!("callback_saf_messages_received"); +} +extern "C" fn callback_connectivity_status(status: u64) { + let callbacks = Callbacks::instance(); + callbacks.on_connectivity_status(status); + // println!("callback_connectivity_status"); +} + +#[derive(Default, Debug)] +struct CachedBalance { + available: u64, + time_locked: u64, + pending_incoming: u64, + pending_outgoing: u64, +} + +#[derive(Debug)] +pub struct Wallet { + ptr: *mut TariWallet, + liveness_data: Arc>>, + balance: CachedBalance, +} + +impl Drop for Wallet { + fn drop(&mut self) { + self.destroy(); + } +} + +impl Wallet { + pub fn create(comms_config: CommsConfig, log_path: String, seed_words_ptr: *const c_void) -> Arc> { + let mut recovery_in_progress: bool = false; + let mut error = 0; + let ptr; + unsafe { + ptr = wallet_create( + comms_config.get_ptr(), + CString::new(log_path).unwrap().into_raw(), + 50, + 102400, + CString::new("kensentme").unwrap().into_raw(), + seed_words_ptr, + CString::new("localnet").unwrap().into_raw(), + callback_received_transaction, + callback_received_transaction_reply, + callback_received_finalized_transaction, + callback_transaction_broadcast, + callback_transaction_mined, + callback_transaction_mined_unconfirmed, + callback_faux_transaction_confirmed, + callback_faux_transaction_unconfirmed, + callback_transaction_send_result, + callback_transaction_cancellation, + callback_txo_validation_complete, + callback_contacts_liveness_data_updated, + callback_balance_updated, + callback_transaction_validation_complete, + callback_saf_messages_received, + callback_connectivity_status, + &mut recovery_in_progress, + &mut error, + ); + if error > 0 { + println!("wallet_create error {}", error); + } + } + let wallet = Arc::new(Mutex::new(Self { + ptr, + liveness_data: Default::default(), + balance: Default::default(), + })); + let callbacks = Callbacks::instance(); + callbacks.reset(wallet.clone()); + wallet + } + + pub fn add_liveness_data(&mut self, contact_liveness_data: ContactsLivenessData) { + self.liveness_data.lock().unwrap().insert( + contact_liveness_data.get_public_key().address().get_as_hex(), + contact_liveness_data, + ); + } + + pub fn set_balance(&mut self, balance: Balance) { + self.balance.available = balance.get_available(); + self.balance.pending_incoming = balance.get_pending_incoming(); + self.balance.pending_outgoing = balance.get_pending_outgoing(); + self.balance.time_locked = balance.get_time_locked(); + } + + pub fn destroy(&mut self) { + unsafe { ffi_import::wallet_destroy(self.ptr) }; + self.ptr = null_mut(); + } + + pub fn add_base_node_peer(&self, base_node: PublicKey, address: String) -> bool { + let mut error = 0; + let success; + unsafe { + success = ffi_import::wallet_add_base_node_peer( + self.ptr, + base_node.get_ptr(), + CString::new(address).unwrap().into_raw(), + &mut error, + ); + if error > 0 { + println!("wallet_add_base_node_peer error {}", error); + } + } + success + } + + pub fn get_address(&self) -> WalletAddress { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_tari_address(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_tari_address error {}", error); + } + } + WalletAddress::from_ptr(ptr) + } + + pub fn connected_public_keys(&self) -> PublicKeys { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::comms_list_connected_public_keys(self.ptr, &mut error); + } + PublicKeys::from_ptr(ptr) + } + + pub fn upsert_contact(&self, contact: Contact) -> bool { + let success; + let mut error = 0; + unsafe { + success = ffi_import::wallet_upsert_contact(self.ptr, contact.get_ptr(), &mut error); + if error > 0 { + println!("wallet_upsert_contact error {}", error); + } + } + success + } + + pub fn get_contacts(&self) -> Contacts { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_contacts(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_contacts error {}", error); + } + } + Contacts::from_ptr(ptr) + } + + pub fn remove_contact(&self, contact: Contact) -> bool { + let success; + let mut error = 0; + unsafe { + success = ffi_import::wallet_remove_contact(self.ptr, contact.get_ptr(), &mut error); + if error > 0 { + println!("wallet_remove_contact error {}", error); + } + } + success + } + + pub fn get_balance(&self) -> Balance { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_balance(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_balance error {}", error); + } + } + Balance::from_ptr(ptr) + } + + pub fn send_transaction( + &self, + dest: String, + amount: u64, + fee_per_gram: u64, + message: String, + one_sided: bool, + ) -> u64 { + let tx_id; + let mut error = 0; + unsafe { + tx_id = ffi_import::wallet_send_transaction( + self.ptr, + WalletAddress::from_hex(dest).get_ptr(), + amount, + null_mut(), + fee_per_gram, + CString::new(message).unwrap().into_raw(), + one_sided, + &mut error, + ); + if error > 0 { + println!("wallet_send_transaction error {}", error); + } + } + tx_id + } + + pub fn get_pending_outbound_transactions(&self) -> PendingOutboundTransactions { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_pending_outbound_transactions(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_pending_outbound_transactions error {}", error); + } + } + PendingOutboundTransactions::from_ptr(ptr) + } + + pub fn get_pending_inbound_transactions(&self) -> PendingInboundTransactions { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_pending_inbound_transactions(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_pending_inbound_transactions error {}", error); + } + } + PendingInboundTransactions::from_ptr(ptr) + } + + pub fn get_completed_transactions(&self) -> CompletedTransactions { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_completed_transactions(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_completed_transactions error {}", error); + } + } + CompletedTransactions::from_ptr(ptr) + } + + pub fn cancel_pending_transaction(&self, transaction_id: u64) -> bool { + let cancelled; + let mut error = 0; + unsafe { + cancelled = ffi_import::wallet_cancel_pending_transaction(self.ptr, transaction_id, &mut error); + if error > 0 { + println!("wallet_cancel_pending_transaction error {}", error); + } + } + cancelled + } + + pub fn start_txo_validation(&self) -> u64 { + let request_key; + let mut error = 0; + unsafe { + request_key = ffi_import::wallet_start_txo_validation(self.ptr, &mut error); + if error > 0 { + println!("wallet_start_txo_validation error {}", error); + } + } + request_key + } + + pub fn start_transaction_validation(&self) -> u64 { + let request_key; + let mut error = 0; + unsafe { + request_key = ffi_import::wallet_start_transaction_validation(self.ptr, &mut error); + if error > 0 { + println!("wallet_start_transaction_validation error {}", error); + } + } + request_key + } + + pub fn get_liveness_data(&self) -> Arc>> { + self.liveness_data.clone() + } + + #[allow(dead_code)] + pub fn get_fee_per_gram_stats(&self, count: u32) -> FeePerGramStats { + let ptr; + let mut error = 0; + unsafe { + ptr = ffi_import::wallet_get_fee_per_gram_stats(self.ptr, count, &mut error); + if error > 0 { + println!("wallet_get_fee_per_gram_stats error {}", error); + } + } + FeePerGramStats::from_ptr(ptr) + } +} diff --git a/integration_tests/tests/utils/ffi/wallet_address.rs b/integration_tests/tests/utils/ffi/wallet_address.rs new file mode 100644 index 0000000000..af865c9eea --- /dev/null +++ b/integration_tests/tests/utils/ffi/wallet_address.rs @@ -0,0 +1,110 @@ +// Copyright 2022. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ffi::CString, ptr::null_mut}; + +use libc::c_void; + +use super::{ffi_bytes::FFIBytes, ffi_import, FFIString, PrivateKey}; + +pub struct WalletAddress { + ptr: *mut c_void, +} + +impl Drop for WalletAddress { + fn drop(&mut self) { + unsafe { ffi_import::tari_address_destroy(self.ptr) }; + self.ptr = null_mut(); + } +} + +impl WalletAddress { + pub fn from_ptr(ptr: *mut c_void) -> Self { + Self { ptr } + } + + #[allow(dead_code)] + pub fn from_private_key(private_key: PrivateKey, network: u32) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::tari_address_from_private_key(private_key.get_ptr(), network, &mut error); + if error > 0 { + println!("wallet_get_tari_address error {}", error); + } + } + Self { ptr } + } + + pub fn from_hex(address: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::tari_address_from_hex(CString::new(address).unwrap().into_raw(), &mut error); + if error > 0 { + println!("wallet_get_tari_address error {}", error); + } + } + Self { ptr } + } + + #[allow(dead_code)] + pub fn from_emoji_id(emoji_id: String) -> Self { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::emoji_id_to_tari_address(CString::new(emoji_id).unwrap().into_raw(), &mut error); + if error > 0 { + println!("wallet_get_tari_address error {}", error); + } + } + Self { ptr } + } + + pub fn address(&self) -> FFIBytes { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::tari_address_get_bytes(self.ptr, &mut error); + if error > 0 { + println!("wallet_get_tari_address error {}", error); + } + } + FFIBytes::from_ptr(ptr) + } + + pub fn emoji_id(&self) -> FFIString { + let mut error = 0; + let ptr; + unsafe { + ptr = ffi_import::tari_address_to_emoji_id(self.ptr, &mut error); + if error > 0 { + println!("tari_address_to_emoji_id error {}", error); + } + } + FFIString::from_ptr(ptr) + } + + pub fn get_ptr(&self) -> *mut c_void { + self.ptr + } +} diff --git a/integration_tests/tests/utils/mod.rs b/integration_tests/tests/utils/mod.rs index 30f013470b..f330d62660 100644 --- a/integration_tests/tests/utils/mod.rs +++ b/integration_tests/tests/utils/mod.rs @@ -28,10 +28,12 @@ use crate::TariWorld; pub mod base_node; pub mod base_node_process; +pub mod ffi; pub mod miner; pub mod transaction; pub mod wallet; pub mod wallet_client; +pub mod wallet_ffi; pub mod wallet_process; pub fn get_port(range: Range) -> Option { diff --git a/integration_tests/tests/utils/wallet_ffi.rs b/integration_tests/tests/utils/wallet_ffi.rs new file mode 100644 index 0000000000..a5d8263ef4 --- /dev/null +++ b/integration_tests/tests/utils/wallet_ffi.rs @@ -0,0 +1,216 @@ +// Copyright 2021. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{ + ffi::CString, + ptr::null, + sync::{Arc, Mutex}, + time::SystemTime, +}; + +use chrono::{DateTime, Utc}; +use indexmap::IndexMap; +use libc::c_void; + +// use tari_wallet_ffi::*; +use super::{ + ffi::{ + Balance, + Callbacks, + CompletedTransactions, + Contact, + Contacts, + ContactsLivenessData, + FeePerGramStats, + PendingInboundTransactions, + PendingOutboundTransactions, + PublicKeys, + WalletAddress, + }, + get_port, +}; +use crate::{ + utils::ffi::{self}, + TariWorld, +}; + +#[derive(Debug)] +pub struct WalletFFI { + pub name: String, + pub port: u64, + // pub grpc_port: u64, + // pub temp_dir_path: String, + pub wallet: Arc>, +} + +impl WalletFFI { + fn spawn(name: String, seed_words_ptr: *const c_void) -> Self { + let port = get_port(18000..18499).unwrap(); + let transport_config = + ffi::TransportConfig::create_tcp(CString::new(format!("/ip4/127.0.0.1/tcp/{}", port)).unwrap().into_raw()); + let now: DateTime = SystemTime::now().into(); + let base_dir = format!("./temp/base_nodes/{}", now.format("%Y%m%d-%H%M%S")); + let comms_config = ffi::CommsConfig::create(port, transport_config, base_dir.clone()); + let log_path = format!("{}/log/wallet.log", base_dir); + let wallet = ffi::Wallet::create(comms_config, log_path, seed_words_ptr); + Self { name, port, wallet } + } + + pub fn identify(&self) -> String { + let tari_address = self.get_address(); + let key = tari_address.address(); + key.get_as_hex() + } + + pub fn get_emoji_id(&self) -> String { + let tari_address = self.get_address(); + let emoji_id = tari_address.emoji_id(); + emoji_id.as_string() + } + + pub fn add_base_node(&self, public_key: String, address: String) { + let node_public_key = ffi::PublicKey::from_hex(public_key); + self.wallet.lock().unwrap().add_base_node_peer(node_public_key, address); + } + + pub fn destroy(&mut self) { + self.wallet.lock().unwrap().destroy(); + } + + pub fn get_address(&self) -> WalletAddress { + self.wallet.lock().unwrap().get_address() + } + + pub fn connected_public_keys(&self) -> PublicKeys { + self.wallet.lock().unwrap().connected_public_keys() + } + + pub fn get_balance(&self) -> Balance { + self.wallet.lock().unwrap().get_balance() + } + + pub fn upsert_contact(&self, contact: Contact) -> bool { + self.wallet.lock().unwrap().upsert_contact(contact) + } + + pub fn get_contacts(&self) -> Contacts { + self.wallet.lock().unwrap().get_contacts() + } + + pub fn remove_contact(&self, contact_to_remove: Contact) -> bool { + self.wallet.lock().unwrap().remove_contact(contact_to_remove) + } + + pub fn get_pending_inbound_transactions(&self) -> PendingInboundTransactions { + self.wallet.lock().unwrap().get_pending_inbound_transactions() + } + + pub fn get_pending_outbound_transactions(&self) -> PendingOutboundTransactions { + self.wallet.lock().unwrap().get_pending_outbound_transactions() + } + + pub fn get_completed_transactions(&self) -> CompletedTransactions { + self.wallet.lock().unwrap().get_completed_transactions() + } + + pub fn cancel_pending_transaction(&self, transaction_id: u64) -> bool { + self.wallet.lock().unwrap().cancel_pending_transaction(transaction_id) + } + + pub fn get_counters(&self) -> &mut Callbacks { + let callback = Callbacks::instance(); + callback + } + + pub fn start_txo_validation(&self) -> u64 { + self.wallet.lock().unwrap().start_txo_validation() + } + + pub fn start_transaction_validation(&self) -> u64 { + self.wallet.lock().unwrap().start_transaction_validation() + } + + pub fn get_liveness_data(&self) -> Arc>> { + self.wallet.lock().unwrap().get_liveness_data() + } + + pub fn send_transaction( + &self, + dest: String, + amount: u64, + fee_per_gram: u64, + message: String, + one_sided: bool, + ) -> u64 { + self.wallet + .lock() + .unwrap() + .send_transaction(dest, amount, fee_per_gram, message, one_sided) + } + + pub fn restart(&mut self) { + self.wallet.lock().unwrap().destroy(); + let port = get_port(18000..18499).unwrap(); + let transport_config = + ffi::TransportConfig::create_tcp(CString::new(format!("/ip4/127.0.0.1/tcp/{}", port)).unwrap().into_raw()); + let now: DateTime = SystemTime::now().into(); + let base_dir = format!("./temp/base_nodes/{}", now.format("%Y%m%d-%H%M%S")); + let comms_config = ffi::CommsConfig::create(port, transport_config, base_dir.clone()); + let log_path = format!("{}/log/wallet.log", base_dir); + self.wallet = ffi::Wallet::create(comms_config, log_path, null()); + } + + pub fn get_fee_per_gram_stats(&self, count: u32) -> FeePerGramStats { + self.wallet.lock().unwrap().get_fee_per_gram_stats(count) + } +} + +pub fn spawn_wallet_ffi(world: &mut TariWorld, wallet_name: String, seed_words_ptr: *const c_void) { + let wallet_ffi = WalletFFI::spawn(wallet_name.clone(), seed_words_ptr); + world.ffi_wallets.insert(wallet_name, wallet_ffi); +} + +pub fn get_mnemonic_word_list_for_language(language: String) -> ffi::SeedWords { + let language = match language.as_str() { + "CHINESE_SIMPLIFIED" => "ChineseSimplified", + "ENGLISH" => "English", + "FRENCH" => "French", + "ITALIAN" => "Italian", + "JAPANESE" => "Japanese", + "KOREAN" => "Korean", + "SPANISH" => "Spanish", + _ => panic!("Unknown language {}", language), + }; + ffi::SeedWords::get_mnemonic_word_list_for_language(language.to_string()) +} + +pub fn create_contact(alias: String, address: String) -> ffi::Contact { + ffi::Contact::create(alias, address) +} + +pub fn create_seed_words(words: Vec<&str>) -> ffi::SeedWords { + let seed_words = ffi::SeedWords::create(); + for word in words { + seed_words.push_word(word.to_string()); + } + seed_words +} diff --git a/integration_tests/tests/utils/wallet_process.rs b/integration_tests/tests/utils/wallet_process.rs index b860f35951..081ee19945 100644 --- a/integration_tests/tests/utils/wallet_process.rs +++ b/integration_tests/tests/utils/wallet_process.rs @@ -143,7 +143,11 @@ pub async fn spawn_wallet( let rt = runtime::Builder::new_multi_thread().enable_all().build().unwrap(); - let cli = cli.unwrap_or_else(get_default_cli); + let mut cli = cli.unwrap_or_else(get_default_cli); + // We expect only file_name to be passed from cucumber.rs, now we put it in the right directory. + if let Some(file_name) = cli.seed_words_file_name { + cli.seed_words_file_name = Some(temp_dir_path.join(file_name)); + } if let Err(e) = run_wallet_with_cli(&mut send_to_thread_shutdown, rt, &mut wallet_config, cli) { panic!("{:?}", e);