From 930860dfc0386f9eb8e578501c2c0e0477c4a638 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal <39146854+hansieodendaal@users.noreply.github.com> Date: Thu, 28 Oct 2021 20:07:02 +0200 Subject: [PATCH] feat(wallet_ffi)!: add get_balance callback to wallet ffi (#3475) Description --- - Added get_balance callback to the wallet ffi callback handler that fires only if the balance has actually changed. - Expanded the wallet ffi callback handler test framework to include a mock output manager request-response server. - _**Update:** Added required methods and interfaces to the cucumber test framework._ - _**Update:** Fixed flaky wallet FFI cucumber tests._ - _**Update:** Fixed a bug in the wallet FFI cucumber test framework where the return type of `ref.types.ulonglong` did not correspond to the Rust return type and had a memory alignment problem._ - _**Update:** Fixed an issue whereby on a fast 8-core multi-tasking computer (e.g. AMD Ryzen 7 2700X) some of the wallet FFI cucumber tests did not complete properly after the test and went into an endless wait. The root cause of this issue has been traced down to incorrect use of synchronous calls to wallet FFI destroy methods where in actual fact those methods have async behaviour._ - _**Update:** Re-applied #3271._ ~~The following anomaly exists when compiling the proposed `wallet_ffi/tests` module:~~ ``` 24 | use tari_wallet_ffi::callback_handler::CallbackHandler; | ^^^^^^^^^^^^^^^ use of undeclared crate or module `tari_wallet_ffi` ``` _**Update**_ ~~Various code organizations have been tried, all with the same result. As an alternative, a working test and output manager service mock has been added into the test module in `callback_handler.rs`. Hopefully, the anomaly can be fixed. Duplicate code will be removed before the final commit.~~ Motivation and Context --- - Mobile wallet efficiency. - Resilient wallet FFI cucumber tests. How Has This Been Tested? --- - Expanded the current FFI `test_callback_handler` unit test. - _**Update:** Ran all the wallet FFI cucumber tests to verify the new callback is working properly when using the wallet FFI library:_ ``` 2021-10-21T06:29:32.160Z callbackTransactionValidationComplete(9123501482775375388,0) 2021-10-21T06:29:32.161Z callbackBalanceUpdated: available = 0, time locked = 0 pending incoming = 2000000 pending outgoing = 0 2021-10-21T06:29:32.263Z received Transaction with txID 14659183447022727953 ... 2021-10-21T06:31:38.358Z Transaction with txID 14659183447022727953 was mined. 2021-10-21T06:31:38.358Z callbackBalanceUpdated: available = 2000000, time locked = 2000000 pending incoming = 0 pending outgoing = 0 2021-10-21T06:31:38.359Z callbackTransactionValidationComplete(17755253868227079780,0) ``` --- Cargo.lock | 1 + applications/ffi_client/index.js | 22 +- base_layer/wallet_ffi/Cargo.toml | 1 + base_layer/wallet_ffi/src/callback_handler.rs | 468 +--- .../wallet_ffi/src/callback_handler_tests.rs | 542 +++++ base_layer/wallet_ffi/src/lib.rs | 148 ++ .../src/output_manager_service_mock.rs | 129 ++ base_layer/wallet_ffi/wallet.h | 20 + integration_tests/README.md | 27 +- integration_tests/features/WalletFFI.feature | 53 +- .../features/support/ffi_steps.js | 247 +- integration_tests/features/support/steps.js | 6 +- integration_tests/features/support/world.js | 6 +- integration_tests/helpers/ffi/balance.js | 43 + integration_tests/helpers/ffi/ffiInterface.js | 45 +- integration_tests/helpers/ffi/wallet.js | 16 + integration_tests/helpers/ffi/walletFFI.js | 2041 ----------------- integration_tests/helpers/walletFFIClient.js | 33 +- integration_tests/package-lock.json | 931 +++++--- integration_tests/package.json | 9 +- 20 files changed, 1811 insertions(+), 2977 deletions(-) create mode 100644 base_layer/wallet_ffi/src/callback_handler_tests.rs create mode 100644 base_layer/wallet_ffi/src/output_manager_service_mock.rs create mode 100644 integration_tests/helpers/ffi/balance.js delete mode 100644 integration_tests/helpers/ffi/walletFFI.js diff --git a/Cargo.lock b/Cargo.lock index d7645fcc5f..ab2f3ba05b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4952,6 +4952,7 @@ dependencies = [ "tari_crypto", "tari_key_manager", "tari_p2p", + "tari_service_framework", "tari_shutdown", "tari_test_utils", "tari_utilities", diff --git a/applications/ffi_client/index.js b/applications/ffi_client/index.js index 0c1b750df5..20aeaf6c39 100644 --- a/applications/ffi_client/index.js +++ b/applications/ffi_client/index.js @@ -16,6 +16,8 @@ try { let err = ref.alloc(i32); // console.log(err); + let recoveryInProgress = ref.alloc(bool); + console.log("Create Tor transport..."); let tor = lib.transport_tor_create( "/ip4/127.0.0.1/tcp/9051", @@ -79,17 +81,13 @@ try { const txCancelled = ffi.Callback("void", ["pointer"], function (ptr) { console.log("txCancelled: ", ptr); }); - // callback_utxo_validation_complete: unsafe extern "C" fn(u64, u8), - const utxoValidation = ffi.Callback("void", [u64, u8], function (i, j) { + // callback_txo_validation_complete: unsafe extern "C" fn(u64, u8), + const txoValidation = ffi.Callback("void", [u64, u8], function (i, j) { console.log("utxoValidation: ", i, j); }); - // callback_stxo_validation_complete: unsafe extern "C" fn(u64, u8), - const stxoValidation = ffi.Callback("void", [u64, u8], function (i, j) { - console.log("stxoValidation: ", i, j); - }); - // callback_invalid_txo_validation_complete: unsafe extern "C" fn(u64, u8), - const itxoValidation = ffi.Callback("void", [u64, u8], function (i, j) { - console.log("itxoValidation: ", i, j); + // callback_balance_updated: unsafe extern "C" fn(*mut Balance), + const balanceUpdated = ffi.Callback("void", ["pointer"], function (ptr) { + console.log("balanceUpdated: ", ptr); }); // callback_transaction_validation_complete: unsafe extern "C" fn(u64, u8), const txValidation = ffi.Callback("void", [u64, u8], function (i, j) { @@ -117,11 +115,11 @@ try { directSendResult, safResult, txCancelled, - utxoValidation, - stxoValidation, - itxoValidation, + txoValidation, + balanceUpdated, txValidation, safsReceived, + recoveryInProgress, err ); diff --git a/base_layer/wallet_ffi/Cargo.toml b/base_layer/wallet_ffi/Cargo.toml index cad25e184c..154fd4332a 100644 --- a/base_layer/wallet_ffi/Cargo.toml +++ b/base_layer/wallet_ffi/Cargo.toml @@ -52,3 +52,4 @@ env_logger = "0.7.1" tari_key_manager = { version = "^0.11", path = "../key_manager" } tari_common_types = { version = "^0.11", path = "../../base_layer/common_types"} tari_test_utils = { version = "^0.11", path = "../../infrastructure/test_utils"} +tari_service_framework = { path = "../../base_layer/service_framework" } diff --git a/base_layer/wallet_ffi/src/callback_handler.rs b/base_layer/wallet_ffi/src/callback_handler.rs index dff1e1c175..fffbe5800e 100644 --- a/base_layer/wallet_ffi/src/callback_handler.rs +++ b/base_layer/wallet_ffi/src/callback_handler.rs @@ -54,7 +54,10 @@ use tari_comms::types::CommsPublicKey; use tari_comms_dht::event::{DhtEvent, DhtEventReceiver}; use tari_shutdown::ShutdownSignal; use tari_wallet::{ - output_manager_service::handle::{OutputManagerEvent, OutputManagerEventReceiver}, + output_manager_service::{ + handle::{OutputManagerEvent, OutputManagerEventReceiver, OutputManagerHandle}, + service::Balance, + }, transaction_service::{ handle::{TransactionEvent, TransactionEventReceiver}, storage::{ @@ -87,14 +90,17 @@ where TBackend: TransactionBackend + 'static callback_store_and_forward_send_result: unsafe extern "C" fn(TxId, bool), callback_transaction_cancellation: unsafe extern "C" fn(*mut CompletedTransaction), callback_txo_validation_complete: unsafe extern "C" fn(u64, u8), + callback_balance_updated: unsafe extern "C" fn(*mut Balance), callback_transaction_validation_complete: unsafe extern "C" fn(u64, u8), callback_saf_messages_received: unsafe extern "C" fn(), db: TransactionDatabase, transaction_service_event_stream: TransactionEventReceiver, output_manager_service_event_stream: OutputManagerEventReceiver, + output_manager_service: OutputManagerHandle, dht_event_stream: DhtEventReceiver, shutdown_signal: Option, comms_public_key: CommsPublicKey, + balance_cache: Balance, } #[allow(clippy::too_many_arguments)] @@ -105,6 +111,7 @@ where TBackend: TransactionBackend + 'static db: TransactionDatabase, transaction_service_event_stream: TransactionEventReceiver, output_manager_service_event_stream: OutputManagerEventReceiver, + output_manager_service: OutputManagerHandle, dht_event_stream: DhtEventReceiver, shutdown_signal: ShutdownSignal, comms_public_key: CommsPublicKey, @@ -118,6 +125,7 @@ where TBackend: TransactionBackend + 'static callback_store_and_forward_send_result: unsafe extern "C" fn(TxId, bool), callback_transaction_cancellation: unsafe extern "C" fn(*mut CompletedTransaction), callback_txo_validation_complete: unsafe extern "C" fn(TxId, u8), + callback_balance_updated: unsafe extern "C" fn(*mut Balance), callback_transaction_validation_complete: unsafe extern "C" fn(TxId, u8), callback_saf_messages_received: unsafe extern "C" fn(), ) -> Self { @@ -161,6 +169,10 @@ where TBackend: TransactionBackend + 'static target: LOG_TARGET, "TxoValidationCompleteCallback -> Assigning Fn: {:?}", callback_txo_validation_complete ); + info!( + target: LOG_TARGET, + "BalanceUpdatedCallback -> Assigning Fn: {:?}", callback_balance_updated + ); info!( target: LOG_TARGET, "TransactionValidationCompleteCallback -> Assigning Fn: {:?}", callback_transaction_validation_complete @@ -181,14 +193,17 @@ where TBackend: TransactionBackend + 'static callback_store_and_forward_send_result, callback_transaction_cancellation, callback_txo_validation_complete, + callback_balance_updated, callback_transaction_validation_complete, callback_saf_messages_received, db, transaction_service_event_stream, output_manager_service_event_stream, + output_manager_service, dht_event_stream, shutdown_signal: Some(shutdown_signal), comms_public_key, + balance_cache: Balance::zero(), } } @@ -209,33 +224,43 @@ where TBackend: TransactionBackend + 'static match (*msg).clone() { TransactionEvent::ReceivedTransaction(tx_id) => { self.receive_transaction_event(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::ReceivedTransactionReply(tx_id) => { self.receive_transaction_reply_event(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::ReceivedFinalizedTransaction(tx_id) => { self.receive_finalized_transaction_event(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionDirectSendResult(tx_id, result) => { self.receive_direct_send_result(tx_id, result); + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionStoreForwardSendResult(tx_id, result) => { self.receive_store_and_forward_send_result(tx_id, result); + self.trigger_balance_refresh().await; }, - TransactionEvent::TransactionCancelled(tx_id) => { + TransactionEvent::TransactionCancelled(tx_id) => { self.receive_transaction_cancellation(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionBroadcast(tx_id) => { self.receive_transaction_broadcast_event(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionMined{tx_id, is_valid: _} => { self.receive_transaction_mined_event(tx_id).await; + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionMinedUnconfirmed{tx_id, num_confirmations, is_valid: _} => { self.receive_transaction_mined_unconfirmed_event(tx_id, num_confirmations).await; + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionValidationSuccess(tx_id) => { self.transaction_validation_complete_event(tx_id, CallbackValidationResults::Success); + self.trigger_balance_refresh().await; }, TransactionEvent::TransactionValidationFailure(tx_id) => { self.transaction_validation_complete_event(tx_id, CallbackValidationResults::Failure); @@ -246,6 +271,12 @@ where TBackend: TransactionBackend + 'static TransactionEvent::TransactionValidationDelayed(tx_id) => { self.transaction_validation_complete_event(tx_id, CallbackValidationResults::BaseNodeNotInSync); }, + TransactionEvent::TransactionMinedRequestTimedOut(_tx_id) | + TransactionEvent::TransactionImported(_tx_id) | + TransactionEvent::TransactionCompletedImmediately(_tx_id) + => { + self.trigger_balance_refresh().await; + }, // Only the above variants are mapped to callbacks _ => (), } @@ -260,6 +291,7 @@ where TBackend: TransactionBackend + 'static match (*msg).clone() { OutputManagerEvent::TxoValidationSuccess(request_key) => { self.output_validation_complete_event(request_key, CallbackValidationResults::Success); + self.trigger_balance_refresh().await; }, OutputManagerEvent::TxoValidationFailure(request_key) => { self.output_validation_complete_event(request_key, CallbackValidationResults::Failure); @@ -347,6 +379,32 @@ where TBackend: TransactionBackend + 'static } } + async fn trigger_balance_refresh(&mut self) { + match self.output_manager_service.get_balance().await { + Ok(balance) => { + if balance != self.balance_cache { + self.balance_cache = balance.clone(); + debug!( + target: LOG_TARGET, + "Calling Update Balance callback function: available {}, time locked {:?}, incoming {}, \ + outgoing {}", + balance.available_balance, + balance.time_locked_balance, + balance.pending_incoming_balance, + balance.pending_outgoing_balance + ); + let boxing = Box::into_raw(Box::new(balance)); + unsafe { + (self.callback_balance_updated)(boxing); + } + } + }, + Err(e) => { + error!(target: LOG_TARGET, "Could not obtain balance ({:?})", e); + }, + } + } + fn receive_direct_send_result(&mut self, tx_id: TxId, result: bool) { debug!( target: LOG_TARGET, @@ -507,409 +565,3 @@ where TBackend: TransactionBackend + 'static } } } - -#[cfg(test)] -mod test { - use crate::callback_handler::CallbackHandler; - use chrono::Utc; - use rand::rngs::OsRng; - use std::{ - sync::{Arc, Mutex}, - thread, - time::Duration, - }; - use tari_common_types::{ - transaction::{TransactionDirection, TransactionStatus}, - types::{BlindingFactor, PrivateKey, PublicKey}, - }; - use tari_comms_dht::event::DhtEvent; - use tari_core::transactions::{ - tari_amount::{uT, MicroTari}, - transaction::Transaction, - ReceiverTransactionProtocol, - SenderTransactionProtocol, - }; - use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; - use tari_shutdown::Shutdown; - use tari_wallet::{ - output_manager_service::handle::OutputManagerEvent, - test_utils::make_wallet_database_connection, - transaction_service::{ - handle::TransactionEvent, - storage::{ - database::TransactionDatabase, - models::{CompletedTransaction, InboundTransaction, OutboundTransaction}, - sqlite_db::TransactionServiceSqliteDatabase, - }, - }, - }; - use tokio::{runtime::Runtime, sync::broadcast}; - - struct CallbackState { - pub received_tx_callback_called: bool, - pub received_tx_reply_callback_called: bool, - pub received_finalized_tx_callback_called: bool, - pub broadcast_tx_callback_called: bool, - pub mined_tx_callback_called: bool, - pub mined_tx_unconfirmed_callback_called: u64, - pub direct_send_callback_called: bool, - pub store_and_forward_send_callback_called: bool, - pub tx_cancellation_callback_called_completed: bool, - pub tx_cancellation_callback_called_inbound: bool, - pub tx_cancellation_callback_called_outbound: bool, - pub callback_txo_validation_complete: u32, - pub callback_transaction_validation_complete: u32, - pub saf_messages_received: bool, - } - - impl CallbackState { - fn new() -> Self { - Self { - received_tx_callback_called: false, - received_tx_reply_callback_called: false, - received_finalized_tx_callback_called: false, - broadcast_tx_callback_called: false, - mined_tx_callback_called: false, - mined_tx_unconfirmed_callback_called: 0, - direct_send_callback_called: false, - store_and_forward_send_callback_called: false, - callback_txo_validation_complete: 0, - callback_transaction_validation_complete: 0, - tx_cancellation_callback_called_completed: false, - tx_cancellation_callback_called_inbound: false, - tx_cancellation_callback_called_outbound: false, - saf_messages_received: false, - } - } - } - - lazy_static! { - static ref CALLBACK_STATE: Mutex = Mutex::new(CallbackState::new()); - } - - unsafe extern "C" fn received_tx_callback(tx: *mut InboundTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.received_tx_callback_called = true; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn received_tx_reply_callback(tx: *mut CompletedTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.received_tx_reply_callback_called = true; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn received_tx_finalized_callback(tx: *mut CompletedTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.received_finalized_tx_callback_called = true; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn broadcast_callback(tx: *mut CompletedTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.broadcast_tx_callback_called = true; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn mined_callback(tx: *mut CompletedTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.mined_tx_callback_called = true; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn mined_unconfirmed_callback(tx: *mut CompletedTransaction, confirmations: u64) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.mined_tx_unconfirmed_callback_called = confirmations; - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn direct_send_callback(_tx_id: u64, _result: bool) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.direct_send_callback_called = true; - drop(lock); - } - - unsafe extern "C" fn store_and_forward_send_callback(_tx_id: u64, _result: bool) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.store_and_forward_send_callback_called = true; - drop(lock); - } - - unsafe extern "C" fn saf_messages_received_callback() { - let mut lock = CALLBACK_STATE.lock().unwrap(); - lock.saf_messages_received = true; - drop(lock); - } - - unsafe extern "C" fn tx_cancellation_callback(tx: *mut CompletedTransaction) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - match (*tx).tx_id { - 3 => lock.tx_cancellation_callback_called_inbound = true, - 4 => lock.tx_cancellation_callback_called_completed = true, - 5 => lock.tx_cancellation_callback_called_outbound = true, - _ => (), - } - - drop(lock); - Box::from_raw(tx); - } - - unsafe extern "C" fn txo_validation_complete_callback(_tx_id: u64, result: u8) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - - lock.callback_txo_validation_complete += result as u32; - - drop(lock); - } - - unsafe extern "C" fn transaction_validation_complete_callback(_tx_id: u64, result: u8) { - let mut lock = CALLBACK_STATE.lock().unwrap(); - - lock.callback_transaction_validation_complete += result as u32; - - drop(lock); - } - - #[test] - fn test_callback_handler() { - let runtime = Runtime::new().unwrap(); - - let (connection, _tempdir) = make_wallet_database_connection(None); - let db = TransactionDatabase::new(TransactionServiceSqliteDatabase::new(connection, None)); - let rtp = ReceiverTransactionProtocol::new_placeholder(); - let inbound_tx = InboundTransaction::new( - 1u64, - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - 22 * uT, - rtp, - TransactionStatus::Pending, - "1".to_string(), - Utc::now().naive_utc(), - ); - let completed_tx = CompletedTransaction::new( - 2u64, - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - MicroTari::from(100), - MicroTari::from(2000), - Transaction::new( - Vec::new(), - Vec::new(), - Vec::new(), - BlindingFactor::default(), - BlindingFactor::default(), - ), - TransactionStatus::Completed, - "2".to_string(), - Utc::now().naive_utc(), - TransactionDirection::Inbound, - None, - ); - let stp = SenderTransactionProtocol::new_placeholder(); - let outbound_tx = OutboundTransaction::new( - 3u64, - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - 22 * uT, - 23 * uT, - stp, - TransactionStatus::Pending, - "3".to_string(), - Utc::now().naive_utc(), - false, - ); - let inbound_tx_cancelled = InboundTransaction { - tx_id: 4u64, - ..inbound_tx.clone() - }; - let completed_tx_cancelled = CompletedTransaction { - tx_id: 5u64, - ..completed_tx.clone() - }; - - runtime - .block_on(db.add_pending_inbound_transaction(1u64, inbound_tx)) - .unwrap(); - runtime - .block_on(db.insert_completed_transaction(2u64, completed_tx)) - .unwrap(); - runtime - .block_on(db.add_pending_inbound_transaction(4u64, inbound_tx_cancelled)) - .unwrap(); - runtime.block_on(db.cancel_pending_transaction(4u64)).unwrap(); - runtime - .block_on(db.insert_completed_transaction(5u64, completed_tx_cancelled)) - .unwrap(); - runtime.block_on(db.cancel_completed_transaction(5u64)).unwrap(); - runtime - .block_on(db.add_pending_outbound_transaction(3u64, outbound_tx)) - .unwrap(); - runtime.block_on(db.cancel_pending_transaction(3u64)).unwrap(); - - let (tx_sender, tx_receiver) = broadcast::channel(20); - let (oms_sender, oms_receiver) = broadcast::channel(20); - let (dht_sender, dht_receiver) = broadcast::channel(20); - - let shutdown_signal = Shutdown::new(); - let callback_handler = CallbackHandler::new( - db, - tx_receiver, - oms_receiver, - dht_receiver, - shutdown_signal.to_signal(), - PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), - received_tx_callback, - received_tx_reply_callback, - received_tx_finalized_callback, - broadcast_callback, - mined_callback, - mined_unconfirmed_callback, - direct_send_callback, - store_and_forward_send_callback, - tx_cancellation_callback, - txo_validation_complete_callback, - transaction_validation_complete_callback, - saf_messages_received_callback, - ); - - runtime.spawn(callback_handler.start()); - - tx_sender - .send(Arc::new(TransactionEvent::ReceivedTransaction(1u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::ReceivedTransactionReply(2u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::ReceivedFinalizedTransaction(2u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionBroadcast(2u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionMined { - tx_id: 2u64, - is_valid: true, - })) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionMinedUnconfirmed { - tx_id: 2u64, - num_confirmations: 22u64, - is_valid: true, - })) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionDirectSendResult(2u64, true))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionStoreForwardSendResult( - 2u64, true, - ))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionCancelled(3u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionCancelled(4u64))) - .unwrap(); - tx_sender - .send(Arc::new(TransactionEvent::TransactionCancelled(5u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionValidationSuccess(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionValidationFailure(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionValidationAborted(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) - .unwrap(); - - oms_sender - .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) - .unwrap(); - - tx_sender - .send(Arc::new(TransactionEvent::TransactionValidationDelayed(1u64))) - .unwrap(); - - dht_sender - .send(Arc::new(DhtEvent::StoreAndForwardMessagesReceived)) - .unwrap(); - - thread::sleep(Duration::from_secs(10)); - - let lock = CALLBACK_STATE.lock().unwrap(); - assert!(lock.received_tx_callback_called); - assert!(lock.received_tx_reply_callback_called); - assert!(lock.received_finalized_tx_callback_called); - assert!(lock.broadcast_tx_callback_called); - assert!(lock.mined_tx_callback_called); - assert_eq!(lock.mined_tx_unconfirmed_callback_called, 22u64); - assert!(lock.direct_send_callback_called); - assert!(lock.store_and_forward_send_callback_called); - assert!(lock.tx_cancellation_callback_called_inbound); - assert!(lock.tx_cancellation_callback_called_completed); - assert!(lock.tx_cancellation_callback_called_outbound); - assert!(lock.saf_messages_received); - assert_eq!(lock.callback_txo_validation_complete, 18); - assert_eq!(lock.callback_transaction_validation_complete, 6); - - drop(lock); - } -} diff --git a/base_layer/wallet_ffi/src/callback_handler_tests.rs b/base_layer/wallet_ffi/src/callback_handler_tests.rs new file mode 100644 index 0000000000..a140a49225 --- /dev/null +++ b/base_layer/wallet_ffi/src/callback_handler_tests.rs @@ -0,0 +1,542 @@ +// Copyright 2019. 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. + +#[cfg(test)] +mod test { + use crate::{callback_handler::CallbackHandler, output_manager_service_mock::MockOutputManagerService}; + use chrono::Utc; + use rand::rngs::OsRng; + use std::{ + sync::{Arc, Mutex}, + thread, + time::Duration, + }; + use tari_common_types::{ + transaction::{TransactionDirection, TransactionStatus}, + types::{BlindingFactor, PrivateKey, PublicKey}, + }; + use tari_comms_dht::event::DhtEvent; + use tari_core::transactions::{ + tari_amount::{uT, MicroTari}, + transaction::Transaction, + ReceiverTransactionProtocol, + SenderTransactionProtocol, + }; + use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; + use tari_service_framework::reply_channel; + use tari_shutdown::Shutdown; + use tari_wallet::{ + output_manager_service::{ + handle::{OutputManagerEvent, OutputManagerHandle}, + service::Balance, + }, + test_utils::make_wallet_database_connection, + transaction_service::{ + handle::TransactionEvent, + storage::{ + database::TransactionDatabase, + models::{CompletedTransaction, InboundTransaction, OutboundTransaction}, + sqlite_db::TransactionServiceSqliteDatabase, + }, + }, + }; + use tokio::{runtime::Runtime, sync::broadcast, time::Instant}; + + struct CallbackState { + pub received_tx_callback_called: bool, + pub received_tx_reply_callback_called: bool, + pub received_finalized_tx_callback_called: bool, + pub broadcast_tx_callback_called: bool, + pub mined_tx_callback_called: bool, + pub mined_tx_unconfirmed_callback_called: u64, + pub direct_send_callback_called: bool, + pub store_and_forward_send_callback_called: bool, + pub tx_cancellation_callback_called_completed: bool, + pub tx_cancellation_callback_called_inbound: bool, + pub tx_cancellation_callback_called_outbound: bool, + pub callback_txo_validation_complete: u32, + pub callback_balance_updated: u32, + pub callback_transaction_validation_complete: u32, + pub saf_messages_received: bool, + } + + impl CallbackState { + fn new() -> Self { + Self { + received_tx_callback_called: false, + received_tx_reply_callback_called: false, + received_finalized_tx_callback_called: false, + broadcast_tx_callback_called: false, + mined_tx_callback_called: false, + mined_tx_unconfirmed_callback_called: 0, + direct_send_callback_called: false, + store_and_forward_send_callback_called: false, + callback_txo_validation_complete: 0, + callback_balance_updated: 0, + callback_transaction_validation_complete: 0, + tx_cancellation_callback_called_completed: false, + tx_cancellation_callback_called_inbound: false, + tx_cancellation_callback_called_outbound: false, + saf_messages_received: false, + } + } + } + + lazy_static! { + static ref CALLBACK_STATE: Mutex = Mutex::new(CallbackState::new()); + } + + unsafe extern "C" fn received_tx_callback(tx: *mut InboundTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.received_tx_callback_called = true; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn received_tx_reply_callback(tx: *mut CompletedTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.received_tx_reply_callback_called = true; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn received_tx_finalized_callback(tx: *mut CompletedTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.received_finalized_tx_callback_called = true; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn broadcast_callback(tx: *mut CompletedTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.broadcast_tx_callback_called = true; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn mined_callback(tx: *mut CompletedTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.mined_tx_callback_called = true; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn mined_unconfirmed_callback(tx: *mut CompletedTransaction, confirmations: u64) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.mined_tx_unconfirmed_callback_called = confirmations; + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn direct_send_callback(_tx_id: u64, _result: bool) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.direct_send_callback_called = true; + drop(lock); + } + + unsafe extern "C" fn store_and_forward_send_callback(_tx_id: u64, _result: bool) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.store_and_forward_send_callback_called = true; + drop(lock); + } + + unsafe extern "C" fn saf_messages_received_callback() { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.saf_messages_received = true; + drop(lock); + } + + unsafe extern "C" fn tx_cancellation_callback(tx: *mut CompletedTransaction) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + match (*tx).tx_id { + 3 => lock.tx_cancellation_callback_called_inbound = true, + 4 => lock.tx_cancellation_callback_called_completed = true, + 5 => lock.tx_cancellation_callback_called_outbound = true, + _ => (), + } + drop(lock); + Box::from_raw(tx); + } + + unsafe extern "C" fn txo_validation_complete_callback(_tx_id: u64, result: u8) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.callback_txo_validation_complete += result as u32; + drop(lock); + } + + unsafe extern "C" fn balance_updated_callback(balance: *mut Balance) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.callback_balance_updated += 1; + drop(lock); + Box::from_raw(balance); + } + + unsafe extern "C" fn transaction_validation_complete_callback(_tx_id: u64, result: u8) { + let mut lock = CALLBACK_STATE.lock().unwrap(); + lock.callback_transaction_validation_complete += result as u32; + drop(lock); + } + + #[test] + fn test_callback_handler() { + let runtime = Runtime::new().unwrap(); + + let (connection, _tempdir) = make_wallet_database_connection(None); + let db = TransactionDatabase::new(TransactionServiceSqliteDatabase::new(connection, None)); + + let rtp = ReceiverTransactionProtocol::new_placeholder(); + let inbound_tx = InboundTransaction::new( + 1u64, + PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + 22 * uT, + rtp, + TransactionStatus::Pending, + "1".to_string(), + Utc::now().naive_utc(), + ); + let completed_tx = CompletedTransaction::new( + 2u64, + PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + MicroTari::from(100), + MicroTari::from(2000), + Transaction::new( + Vec::new(), + Vec::new(), + Vec::new(), + BlindingFactor::default(), + BlindingFactor::default(), + ), + TransactionStatus::Completed, + "2".to_string(), + Utc::now().naive_utc(), + TransactionDirection::Inbound, + None, + ); + let stp = SenderTransactionProtocol::new_placeholder(); + let outbound_tx = OutboundTransaction::new( + 3u64, + PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + 22 * uT, + 23 * uT, + stp, + TransactionStatus::Pending, + "3".to_string(), + Utc::now().naive_utc(), + false, + ); + let inbound_tx_cancelled = InboundTransaction { + tx_id: 4u64, + ..inbound_tx.clone() + }; + let completed_tx_cancelled = CompletedTransaction { + tx_id: 5u64, + ..completed_tx.clone() + }; + + runtime + .block_on(db.add_pending_inbound_transaction(1u64, inbound_tx.clone())) + .unwrap(); + runtime + .block_on(db.insert_completed_transaction(2u64, completed_tx.clone())) + .unwrap(); + runtime + .block_on(db.add_pending_inbound_transaction(4u64, inbound_tx_cancelled)) + .unwrap(); + runtime.block_on(db.cancel_pending_transaction(4u64)).unwrap(); + runtime + .block_on(db.insert_completed_transaction(5u64, completed_tx_cancelled.clone())) + .unwrap(); + runtime.block_on(db.cancel_completed_transaction(5u64)).unwrap(); + runtime + .block_on(db.add_pending_outbound_transaction(3u64, outbound_tx.clone())) + .unwrap(); + runtime.block_on(db.cancel_pending_transaction(3u64)).unwrap(); + + let (transaction_event_sender, transaction_event_receiver) = broadcast::channel(20); + let (oms_event_sender, oms_event_receiver) = broadcast::channel(20); + let (dht_event_sender, dht_event_receiver) = broadcast::channel(20); + + let (oms_request_sender, oms_request_receiver) = reply_channel::unbounded(); + let mut oms_handle = OutputManagerHandle::new(oms_request_sender, oms_event_sender.clone()); + + let shutdown_signal = Shutdown::new(); + let mut mock_output_manager_service = + MockOutputManagerService::new(oms_request_receiver, shutdown_signal.to_signal()); + let mut balance = Balance { + available_balance: completed_tx.amount + + completed_tx.fee + + completed_tx_cancelled.amount + + completed_tx_cancelled.fee, + time_locked_balance: None, + pending_incoming_balance: inbound_tx.amount, + pending_outgoing_balance: outbound_tx.amount + outbound_tx.fee, + }; + let mut mock_output_manager_service_state = mock_output_manager_service.get_response_state(); + mock_output_manager_service_state.set_balance(balance.clone()); + runtime.spawn(mock_output_manager_service.run()); + assert_eq!(balance, runtime.block_on(oms_handle.get_balance()).unwrap()); + + let callback_handler = CallbackHandler::new( + db, + transaction_event_receiver, + oms_event_receiver, + oms_handle, + dht_event_receiver, + shutdown_signal.to_signal(), + PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + mined_unconfirmed_callback, + direct_send_callback, + store_and_forward_send_callback, + tx_cancellation_callback, + txo_validation_complete_callback, + balance_updated_callback, + transaction_validation_complete_callback, + saf_messages_received_callback, + ); + + runtime.spawn(callback_handler.start()); + let mut callback_balance_updated = 0; + + // The balance updated callback is bundled with other callbacks and will only fire if the balance actually + // changed from an initial zero balance. + // Balance updated should be detected with following event, total = 1 times + transaction_event_sender + .send(Arc::new(TransactionEvent::ReceivedTransaction(1u64))) + .unwrap(); + let start = Instant::now(); + while start.elapsed().as_secs() < 10 { + { + let lock = CALLBACK_STATE.lock().unwrap(); + if lock.callback_balance_updated == 1 { + callback_balance_updated = 1; + break; + } + } + thread::sleep(Duration::from_millis(100)); + } + assert_eq!(callback_balance_updated, 1); + + balance.time_locked_balance = Some(completed_tx_cancelled.amount); + mock_output_manager_service_state.set_balance(balance.clone()); + // Balance updated should be detected with following event, total = 2 times + transaction_event_sender + .send(Arc::new(TransactionEvent::ReceivedTransactionReply(2u64))) + .unwrap(); + let start = Instant::now(); + while start.elapsed().as_secs() < 10 { + { + let lock = CALLBACK_STATE.lock().unwrap(); + if lock.callback_balance_updated == 2 { + callback_balance_updated = 2; + break; + } + } + thread::sleep(Duration::from_millis(100)); + } + assert_eq!(callback_balance_updated, 2); + + balance.pending_incoming_balance += inbound_tx.amount; + mock_output_manager_service_state.set_balance(balance.clone()); + // Balance updated should be detected with following event, total = 3 times + transaction_event_sender + .send(Arc::new(TransactionEvent::ReceivedFinalizedTransaction(2u64))) + .unwrap(); + let start = Instant::now(); + while start.elapsed().as_secs() < 10 { + { + let lock = CALLBACK_STATE.lock().unwrap(); + if lock.callback_balance_updated == 3 { + callback_balance_updated = 3; + break; + } + } + thread::sleep(Duration::from_millis(100)); + } + assert_eq!(callback_balance_updated, 3); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionBroadcast(2u64))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionMined { + tx_id: 2u64, + is_valid: true, + })) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionMinedUnconfirmed { + tx_id: 2u64, + num_confirmations: 22u64, + is_valid: true, + })) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionDirectSendResult(2u64, true))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionStoreForwardSendResult( + 2u64, true, + ))) + .unwrap(); + + balance.pending_outgoing_balance += outbound_tx.amount; + mock_output_manager_service_state.set_balance(balance.clone()); + // Balance updated should be detected with following event, total = 4 times + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionCancelled(3u64))) + .unwrap(); + let start = Instant::now(); + while start.elapsed().as_secs() < 10 { + { + let lock = CALLBACK_STATE.lock().unwrap(); + if lock.callback_balance_updated == 4 { + callback_balance_updated = 4; + break; + } + } + thread::sleep(Duration::from_millis(100)); + } + assert_eq!(callback_balance_updated, 4); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionCancelled(4u64))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionCancelled(5u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) + .unwrap(); + + balance.available_balance -= completed_tx_cancelled.amount; + mock_output_manager_service_state.set_balance(balance); + // Balance updated should be detected with following event, total = 5 times + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationSuccess(1u64))) + .unwrap(); + let start = Instant::now(); + while start.elapsed().as_secs() < 10 { + { + let lock = CALLBACK_STATE.lock().unwrap(); + if lock.callback_balance_updated == 5 { + callback_balance_updated = 5; + break; + } + } + thread::sleep(Duration::from_millis(100)); + } + assert_eq!(callback_balance_updated, 5); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionValidationSuccess(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationFailure(1u64))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionValidationFailure(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationAborted(1u64))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionValidationAborted(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) + .unwrap(); + + oms_event_sender + .send(Arc::new(OutputManagerEvent::TxoValidationDelayed(1u64))) + .unwrap(); + + transaction_event_sender + .send(Arc::new(TransactionEvent::TransactionValidationDelayed(1u64))) + .unwrap(); + + dht_event_sender + .send(Arc::new(DhtEvent::StoreAndForwardMessagesReceived)) + .unwrap(); + + thread::sleep(Duration::from_secs(10)); + + let lock = CALLBACK_STATE.lock().unwrap(); + assert!(lock.received_tx_callback_called); + assert!(lock.received_tx_reply_callback_called); + assert!(lock.received_finalized_tx_callback_called); + assert!(lock.broadcast_tx_callback_called); + assert!(lock.mined_tx_callback_called); + assert_eq!(lock.mined_tx_unconfirmed_callback_called, 22u64); + assert!(lock.direct_send_callback_called); + assert!(lock.store_and_forward_send_callback_called); + assert!(lock.tx_cancellation_callback_called_inbound); + assert!(lock.tx_cancellation_callback_called_completed); + assert!(lock.tx_cancellation_callback_called_outbound); + assert!(lock.saf_messages_received); + assert_eq!(lock.callback_txo_validation_complete, 18); + assert_eq!(lock.callback_balance_updated, 5); + assert_eq!(lock.callback_transaction_validation_complete, 6); + + drop(lock); + } +} diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index f41f447071..a0807a42a0 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -194,8 +194,12 @@ use crate::{ }; mod callback_handler; +#[cfg(test)] +mod callback_handler_tests; mod enums; mod error; +#[cfg(test)] +mod output_manager_service_mock; mod tasks; const LOG_TARGET: &str = "wallet_ffi"; @@ -210,6 +214,7 @@ pub struct TariContacts(Vec); pub type TariContact = tari_wallet::contacts_service::storage::database::Contact; pub type TariCompletedTransaction = tari_wallet::transaction_service::storage::models::CompletedTransaction; +pub type TariBalance = tari_wallet::output_manager_service::service::Balance; pub struct TariCompletedTransactions(Vec); @@ -2730,6 +2735,8 @@ unsafe fn init_logging(log_path: *const c_char, num_rolling_log_files: c_uint, s /// `callback_txo_validation_complete` - The callback function pointer matching the function signature. This is called /// when a TXO validation process is completed. The request_key is used to identify which request this /// callback references and the second parameter is a u8 that represent the ClassbackValidationResults enum. +/// `callback_balance_updated` - The callback function pointer matching the function signature. This is called whenever +/// the balance changes. /// `callback_transaction_validation_complete` - The callback function pointer matching the function signature. This is /// called when a Transaction validation process is completed. The request_key is used to identify which request this /// callback references and the second parameter is a u8 that represent the ClassbackValidationResults enum. @@ -2765,6 +2772,7 @@ pub unsafe extern "C" fn wallet_create( callback_store_and_forward_send_result: unsafe extern "C" fn(c_ulonglong, bool), callback_transaction_cancellation: unsafe extern "C" fn(*mut TariCompletedTransaction), callback_txo_validation_complete: unsafe extern "C" fn(u64, u8), + callback_balance_updated: unsafe extern "C" fn(*mut TariBalance), callback_transaction_validation_complete: unsafe extern "C" fn(u64, u8), callback_saf_messages_received: unsafe extern "C" fn(), recovery_in_progress: *mut bool, @@ -2899,6 +2907,7 @@ pub unsafe extern "C" fn wallet_create( TransactionDatabase::new(transaction_backend), w.transaction_service.get_event_stream(), w.output_manager_service.get_event_stream(), + w.output_manager_service.clone(), w.dht_service.subscribe_dht_events(), w.comms.shutdown_signal(), w.comms.node_identity().public_key().clone(), @@ -2912,6 +2921,7 @@ pub unsafe extern "C" fn wallet_create( callback_store_and_forward_send_result, callback_transaction_cancellation, callback_txo_validation_complete, + callback_balance_updated, callback_transaction_validation_complete, callback_saf_messages_received, ); @@ -3226,6 +3236,128 @@ pub unsafe extern "C" fn wallet_remove_contact( } } +/// Gets the available balance from a TariBalance. This is the balance the user can spend. +/// +/// ## Arguments +/// `balance` - The TariBalance pointer +/// `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 +/// `c_ulonglong` - The available balance, 0 if wallet is null +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn balance_get_available(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if balance.is_null() { + error = LibWalletError::from(InterfaceError::NullError("balance".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + c_ulonglong::from((*balance).available_balance) +} + +/// Gets the time locked balance from a TariBalance. This is the balance the user can spend. +/// +/// ## Arguments +/// `balance` - The TariBalance pointer +/// `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 +/// `c_ulonglong` - The time locked balance, 0 if wallet is null +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn balance_get_time_locked(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if balance.is_null() { + error = LibWalletError::from(InterfaceError::NullError("balance".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + let b = if let Some(bal) = (*balance).time_locked_balance { + bal + } else { + MicroTari::from(0) + }; + c_ulonglong::from(b) +} + +/// Gets the pending incoming balance from a TariBalance. This is the balance the user can spend. +/// +/// ## Arguments +/// `balance` - The TariBalance pointer +/// `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 +/// `c_ulonglong` - The pending incoming, 0 if wallet is null +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn balance_get_pending_incoming(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if balance.is_null() { + error = LibWalletError::from(InterfaceError::NullError("balance".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + c_ulonglong::from((*balance).pending_incoming_balance) +} + +/// Gets the pending outgoing balance from a TariBalance. This is the balance the user can spend. +/// +/// ## Arguments +/// `balance` - The TariBalance pointer +/// `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 +/// `c_ulonglong` - The pending outgoing balance, 0 if wallet is null +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn balance_get_pending_outgoing(balance: *mut TariBalance, error_out: *mut c_int) -> c_ulonglong { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if balance.is_null() { + error = LibWalletError::from(InterfaceError::NullError("balance".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + c_ulonglong::from((*balance).pending_outgoing_balance) +} + +/// Frees memory for a TariBalance +/// +/// ## Arguments +/// `balance` - The pointer to a TariBalance +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn balance_destroy(balance: *mut TariBalance) { + if !balance.is_null() { + Box::from_raw(balance); + } +} + /// Gets the available balance from a TariWallet. This is the balance the user can spend. /// /// ## Arguments @@ -5172,6 +5304,7 @@ mod test { pub store_and_forward_send_callback_called: bool, pub tx_cancellation_callback_called: bool, pub callback_txo_validation_complete: bool, + pub callback_balance_updated: bool, pub callback_transaction_validation_complete: bool, } @@ -5188,6 +5321,7 @@ mod test { store_and_forward_send_callback_called: false, tx_cancellation_callback_called: false, callback_txo_validation_complete: false, + callback_balance_updated: false, callback_transaction_validation_complete: false, } } @@ -5311,6 +5445,10 @@ mod test { // assert!(true); //optimized out by compiler } + unsafe extern "C" fn balance_updated_callback(_balance: *mut TariBalance) { + // assert!(true); //optimized out by compiler + } + unsafe extern "C" fn transaction_validation_complete_callback(_tx_id: c_ulonglong, _result: u8) { // assert!(true); //optimized out by compiler } @@ -5646,6 +5784,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5684,6 +5823,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5788,6 +5928,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5834,6 +5975,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5863,6 +6005,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5887,6 +6030,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -5932,6 +6076,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -6006,6 +6151,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -6155,6 +6301,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, @@ -6208,6 +6355,7 @@ mod test { store_and_forward_send_callback, tx_cancellation_callback, txo_validation_complete_callback, + balance_updated_callback, transaction_validation_complete_callback, saf_messages_received_callback, recovery_in_progress_ptr, diff --git a/base_layer/wallet_ffi/src/output_manager_service_mock.rs b/base_layer/wallet_ffi/src/output_manager_service_mock.rs new file mode 100644 index 0000000000..c6c9ec0229 --- /dev/null +++ b/base_layer/wallet_ffi/src/output_manager_service_mock.rs @@ -0,0 +1,129 @@ +// Copyright 2019. 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 futures::StreamExt; +use std::sync::{Arc, Mutex}; +use tari_service_framework::reply_channel::Receiver; +use tari_shutdown::ShutdownSignal; +use tari_wallet::output_manager_service::{ + error::OutputManagerError, + handle::{OutputManagerRequest, OutputManagerResponse}, + service::Balance, +}; + +/// This macro unlocks a Mutex or RwLock. If the lock is poisoned (i.e. panic while unlocked) the last value +/// before the panic is used. +macro_rules! acquire_lock { + ($e:expr, $m:ident) => { + match $e.$m() { + Ok(lock) => lock, + Err(poisoned) => { + log::warn!(target: "wallet", "Lock has been POISONED and will be silently recovered"); + poisoned.into_inner() + }, + } + }; + ($e:expr) => { + acquire_lock!($e, lock) + }; + } + +#[derive(Clone, Debug)] +pub struct ResponseState { + balance: Arc>, +} + +impl ResponseState { + pub fn new() -> Self { + Self { + balance: Arc::new(Mutex::new(Balance::zero())), + } + } + + /// Set the mock server balance response + pub fn set_balance(&mut self, balance: Balance) { + let mut lock = acquire_lock!(self.balance); + *lock = balance; + } + + /// Get the mock server balance value + pub fn get_balance(&mut self) -> Balance { + let lock = acquire_lock!(self.balance); + (*lock).clone() + } +} + +pub struct MockOutputManagerService { + request_stream: Option>>, + state: ResponseState, + shutdown_signal: Option, +} + +impl MockOutputManagerService { + pub fn new( + request_stream: Receiver>, + shutdown_signal: ShutdownSignal, + ) -> Self { + Self { + request_stream: Some(request_stream), + state: ResponseState::new(), + shutdown_signal: Some(shutdown_signal), + } + } + + pub async fn run(mut self) -> Result<(), OutputManagerError> { + let shutdown_signal = self + .shutdown_signal + .take() + .expect("Output Manager Service initialized without shutdown signal"); + + let mut request_stream = self + .request_stream + .take() + .expect("Output Manager Service initialized without request_stream") + .take_until(shutdown_signal); + + while let Some(request_context) = request_stream.next().await { + // Incoming requests + let (request, reply_tx) = request_context.split(); + let response = self.handle_request(request); + let _ = reply_tx.send(response); + } + + Ok(()) + } + + fn handle_request(&mut self, request: OutputManagerRequest) -> Result { + match request { + OutputManagerRequest::GetBalance => Ok(OutputManagerResponse::Balance(self.state.get_balance())), + _ => Err(OutputManagerError::InvalidResponseError(format!( + "Request '{}' not defined for MockOutputManagerService!", + request + ))), + } + } + + /// Returns a clone of the response state to enable updating after the service started + pub fn get_response_state(&mut self) -> ResponseState { + self.state.clone() + } +} diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index dc67011396..f0a69519c3 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -57,6 +57,8 @@ struct TariContact; struct TariCompletedTransactions; +struct TariBalance; + struct TariCompletedTransaction; struct TariPendingOutboundTransactions; @@ -424,6 +426,8 @@ void comms_config_destroy(struct TariCommsConfig *wc); /// `callback_txo_validation_complete` - The callback function pointer matching the function signature. This is called /// when a TXO validation process is completed. The request_key is used to identify which request this /// callback references and the second parameter is a u8 that represent the CallbackValidationResults enum. +/// `callback_balance_updated` - The callback function pointer matching the function signature. This is called whenever +/// the balance changes. /// `callback_transaction_validation_complete` - The callback function pointer matching the function signature. This is /// called when a Transaction validation process is completed. The request_key is used to identify which request this /// callback references and the second parameter is a u8 that represent the CallbackValidationResults enum. @@ -464,6 +468,7 @@ struct TariWallet *wallet_create(struct TariCommsConfig *config, void (*callback_store_and_forward_send_result)(unsigned long long, bool), void (*callback_transaction_cancellation)(struct TariCompletedTransaction *), void (*callback_txo_validation_complete)(unsigned long long, unsigned char), + void (*callback_balance_updated)(struct TariBalance *), void (*callback_transaction_validation_complete)(unsigned long long, unsigned char), void (*callback_saf_message_received)(), bool *recovery_in_progress, @@ -484,6 +489,18 @@ bool wallet_upsert_contact(struct TariWallet *wallet, struct TariContact *contac // Removes a TariContact form the TariWallet bool wallet_remove_contact(struct TariWallet *wallet, struct TariContact *contact, int *error_out); +// Gets the available balance from a TariBalance +unsigned long long balance_get_available(struct TariBalance *balance, int *error_out); + +// Gets the available balance from a TariBalance +unsigned long long balance_get_time_locked(struct TariBalance *balance, int *error_out); + +// Gets the available balance from a TariBalance +unsigned long long balance_get_pending_incoming(struct TariBalance *balance, int *error_out); + +// Gets the available balance from a TariBalance +unsigned long long balance_get_pending_outgoing(struct TariBalance *balance, int *error_out); + // Gets the available balance from a TariWallet unsigned long long wallet_get_available_balance(struct TariWallet *wallet, int *error_out); @@ -693,6 +710,9 @@ bool wallet_start_recovery(struct TariWallet *wallet, struct TariPublicKey *base // Frees memory for a TariWallet void wallet_destroy(struct TariWallet *wallet); +// Frees memory for a TariBalance +void balance_destroy(struct TariBalance *balance); + // This function will produce a partial backup of the specified wallet database file (full file path must be provided. // This backup will be written to the provided file (full path must include the filename and extension) and will include // the full wallet db but will clear the sensitive Comms Private Key diff --git a/integration_tests/README.md b/integration_tests/README.md index 00653b4f5d..6a1bc7ba7e 100644 --- a/integration_tests/README.md +++ b/integration_tests/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- Install `Node.js` +- Install `Node.js` (_Currently, LTS version 12.22.X is recommended for wallet FFI compatibility_) - Open terminal in the `tari-project\integration_tests` folder and run ``` @@ -33,10 +33,10 @@ - To run a specific test, add `-- --name ` to the command line: -```shell - # eg: run a specific test + ```shell + # Runs a specific test npm test -- --name "Simple block sync" -``` + ``` - To run tests with specific tags, e.g. `critical`, add `-- --tags @` to the command line. @@ -53,21 +53,28 @@ npm test -- --tags "@critical and not @long-running and not @broken" ``` - # Runs all @critical tests, but not @long-running - - npm test -- --tags "@critical and not @long-running" +- To run the wallet FFI tests, add `--profile ci` to the command line (_take note of the node version requirements_). + ```shell + # Runs a specific FFI test + npm test -- --profile ci --name "My wallet FFI test scenario name" + # Runs a complete set of FFI tests + npm test -- --profile ci --tags "@wallet-ffi" ``` +- Runs all @critical tests, but not @long-running + + ``` + npm test -- --tags "@critical and not @long-running" ``` - See `npm test -- --help` for more options. ## Notes -- In Windows, running the Tari executables in debug mode fails with a stack overflow, thus Windows users must - run in release mode. See command line options in `baseNodeProcess.js`, `mergeMiningProxyProcess.js` - and `walletProcess.js`. +In Windows, running the Tari executables in debug mode fails with a stack overflow, thus Windows users must +run in release mode. See command line options in `baseNodeProcess.js`, `mergeMiningProxyProcess.js` +and `walletProcess.js`. ## Code Contribution diff --git a/integration_tests/features/WalletFFI.feature b/integration_tests/features/WalletFFI.feature index 2310c785a1..6b5ee539d0 100644 --- a/integration_tests/features/WalletFFI.feature +++ b/integration_tests/features/WalletFFI.feature @@ -36,24 +36,23 @@ Feature: Wallet FFI And I have a ffi wallet FFI_WALLET connected to base node BASE1 And I set base node BASE2 for ffi wallet FFI_WALLET And I stop ffi wallet FFI_WALLET - And I stop node BASE1 - And I wait 5 seconds - # Broken step with reason base node is not persisted - # See details on: - # Scenario: As a client I want to receive Tari via my Public Key sent while I am offline when I come back online - # And I restart ffi wallet FFI_WALLET + And I stop node BASE2 And I restart ffi wallet FFI_WALLET connected to base node BASE2 - Then I wait for ffi wallet FFI_WALLET to receive at least 1 SAF message + And I wait 5 seconds + Then I wait for ffi wallet FFI_WALLET to receive EXACTLY 0 SAF message + And I start base node BASE2 + Then I wait for ffi wallet FFI_WALLET to receive AT_LEAST 1 SAF message And I stop ffi wallet FFI_WALLET Scenario: As a client I want to cancel a transaction Given I have a base node BASE And 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 mining node MINER connected to base node BASE and wallet SENDER And mining node MINER mines 10 blocks Then I wait for wallet SENDER to have at least 1000000 uT - And I have a ffi wallet FFI_WALLET connected to base node BASE 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 Broadcast And wallet SENDER detects all transactions are at least Broadcast And mining node MINER mines 10 blocks Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT @@ -68,53 +67,49 @@ Feature: Wallet FFI Given I have a base node BASE And I have a ffi wallet FFI_WALLET connected to base node BASE And I have wallet WALLET connected to base node BASE - And I wait 5 seconds 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 - # flaky on circle CI - @flaky Scenario: As a client I want to retrieve a list of transactions I have made and received - Given I have a base node BASE - And I have wallet SENDER connected to base node BASE - And I have mining node MINER connected to base node BASE and wallet SENDER + Given I have a seed node SEED + And I have a base node BASE1 connected to all seed nodes + And I have a base node BASE2 connected to all seed nodes + And 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 wallet RECEIVER connected to base node BASE2 + And I have mining node MINER connected to base node BASE1 and wallet SENDER And mining node MINER mines 10 blocks Then I wait for wallet SENDER to have at least 1000000 uT - And I have a ffi wallet FFI_WALLET connected to base node BASE 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 Broadcast And mining node MINER mines 10 blocks Then I wait for ffi wallet FFI_WALLET to have at least 1000000 uT - And I have wallet RECEIVER connected to base node BASE 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 Broadcast And mining node MINER mines 10 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 1 mined + 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 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 base node BASE - And I have wallet SENDER connected to base node BASE - And I have mining node MINER connected to base node BASE and wallet SENDER + Given I have a seed node SEED + And I have a base node BASE1 connected to all seed nodes + And I have a base node BASE2 connected to all seed nodes + And 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 mining node MINER connected to base node BASE1 and wallet SENDER And mining node MINER mines 10 blocks Then I wait for wallet SENDER to have at least 1000000 uT - And I have a ffi wallet FFI_WALLET connected to base node BASE And I stop ffi wallet FFI_WALLET - And I wait 10 seconds And I send 2000000 uT without waiting for broadcast from wallet SENDER to wallet FFI_WALLET at fee 20 - And I wait 5 seconds - # Broken step with reason base node is not persisted - # Log: - # [wallet::transaction_service::callback_handler] DEBUG Calling Received Finalized Transaction callback function for TxId: 7595706993517535281 - # [wallet::transaction_service::service] WARN Error broadcasting completed transaction TxId: 7595706993517535281 to mempool: NoBaseNodeKeysProvided - # And I restart ffi wallet FFI_WALLET - And I restart ffi wallet FFI_WALLET connected to base node BASE + And I restart ffi wallet FFI_WALLET connected to base node BASE2 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 diff --git a/integration_tests/features/support/ffi_steps.js b/integration_tests/features/support/ffi_steps.js index 5e1c95033d..dbc2cddbc6 100644 --- a/integration_tests/features/support/ffi_steps.js +++ b/integration_tests/features/support/ffi_steps.js @@ -26,8 +26,8 @@ Then("I want to get emoji id of ffi wallet {word}", async function (name) { When( "I send {int} uT from ffi wallet {word} to wallet {word} at fee {int}", function (amount, sender, receiver, feePerGram) { - let ffi_wallet = this.getWallet(sender); - let result = ffi_wallet.sendTransaction( + let ffiWallet = this.getWallet(sender); + let result = ffiWallet.sendTransaction( this.getWalletPubkey(receiver), amount, feePerGram, @@ -78,39 +78,20 @@ Then( } ); -Then( - "ffi wallet {word} has {int} broadcast transaction", - { timeout: 120 * 1000 }, - async function (name, count) { - let wallet = this.getWallet(name); - let broadcast = await wallet.getBroadcastTransactionsCount(); - let retries = 1; - const retries_limit = 24; - while (broadcast != count && retries <= retries_limit) { - await sleep(5000); - broadcast = await wallet.getBroadcastTransactionsCount(); - ++retries; - } - expect(broadcast, "Number of broadcasted messages mismatch").to.be.equal( - count - ); - } -); - When( "I add contact with alias {word} and pubkey {word} to ffi wallet {word}", - function (alias, wallet_name, ffi_wallet_name) { - let ffi_wallet = this.getWallet(ffi_wallet_name); - ffi_wallet.addContact(alias, this.getWalletPubkey(wallet_name)); + function (alias, walletName, ffiWalletName) { + let ffiWallet = this.getWallet(ffiWalletName); + ffiWallet.addContact(alias, this.getWalletPubkey(walletName)); } ); Then( "I have contact with alias {word} and pubkey {word} in ffi wallet {word}", - function (alias, wallet_name, ffi_wallet_name) { - let wallet = this.getWalletPubkey(wallet_name); - let ffi_wallet = this.getWallet(ffi_wallet_name); - let contacts = ffi_wallet.getContactList(); + function (alias, walletName, ffiWalletName) { + let wallet = this.getWalletPubkey(walletName); + let ffiWallet = this.getWallet(ffiWalletName); + let contacts = ffiWallet.getContactList(); let length = contacts.getLength(); let found = false; for (let i = 0; i < length; i++) { @@ -130,16 +111,16 @@ Then( When( "I remove contact with alias {word} from ffi wallet {word}", - function (alias, wallet_name) { - let ffi_wallet = this.getWallet(wallet_name); - let contacts = ffi_wallet.getContactList(); + function (alias, walletName) { + let ffiWallet = this.getWallet(walletName); + let contacts = ffiWallet.getContactList(); let length = contacts.getLength(); for (let i = 0; i < length; i++) { { let contact = contacts.getAt(i); let calias = contact.getAlias(); if (alias === calias) { - ffi_wallet.removeContact(contact); + ffiWallet.removeContact(contact); } contact.destroy(); } @@ -150,9 +131,9 @@ When( Then( "I don't have contact with alias {word} in ffi wallet {word}", - function (alias, wallet_name) { - let ffi_wallet = this.getWallet(wallet_name); - let contacts = ffi_wallet.getContactList(); + function (alias, walletName) { + let ffiWallet = this.getWallet(walletName); + let contacts = ffiWallet.getContactList(); let length = contacts.getLength(); let found = false; for (let i = 0; i < length; i++) { @@ -172,8 +153,8 @@ Then( When( "I set base node {word} for ffi wallet {word}", - function (node, wallet_name) { - let wallet = this.getWallet(wallet_name); + function (node, walletName) { + let wallet = this.getWallet(walletName); let peer = this.nodes[node].peerAddress().split("::"); wallet.addBaseNodePeer(peer[0], peer[1]); } @@ -181,16 +162,16 @@ When( Then( "I wait for ffi wallet {word} to have {int} pending outbound transaction(s)", - { timeout: 40 * 1000 }, - async function (wallet_name, count) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, count) { + let wallet = this.getWallet(walletName); let broadcast = wallet.getOutboundTransactions(); let length = broadcast.getLength(); broadcast.destroy(); let retries = 1; - const retries_limit = 24; + const retries_limit = 120; while (length != count && retries <= retries_limit) { - await sleep(5000); + await sleep(1000); broadcast = wallet.getOutboundTransactions(); length = broadcast.getLength(); broadcast.destroy(); @@ -202,8 +183,8 @@ Then( Then( "I cancel all outbound transactions on ffi wallet {word} and it will cancel {int} transaction", - async function (wallet_name, count) { - const wallet = this.getWallet(wallet_name); + function (walletName, count) { + const wallet = this.getWallet(walletName); let txs = wallet.getOutboundTransactions(); let cancelled = 0; for (let i = 0; i < txs.getLength(); i++) { @@ -222,33 +203,33 @@ Then( Given( "I have a ffi wallet {word} connected to base node {word}", async function (walletName, nodeName) { - let ffi_wallet = await this.createAndAddFFIWallet(walletName, null); + let ffiWallet = await this.createAndAddFFIWallet(walletName, null); let peer = this.nodes[nodeName].peerAddress().split("::"); - ffi_wallet.addBaseNodePeer(peer[0], peer[1]); + ffiWallet.addBaseNodePeer(peer[0], peer[1]); } ); Then( "I recover wallet {word} into ffi wallet {word} from seed words on node {word}", - async function (wallet_name, ffi_wallet_name, node) { - let wallet = this.getWallet(wallet_name); + async function (walletName, ffiWalletName, node) { + let wallet = this.getWallet(walletName); const seed_words_text = wallet.getSeedWords(); - await wallet.stop(); + wallet.stop(); await sleep(1000); - let ffi_wallet = await this.createAndAddFFIWallet( - ffi_wallet_name, + let ffiWallet = await this.createAndAddFFIWallet( + ffiWalletName, seed_words_text ); let peer = this.nodes[node].peerAddress().split("::"); - ffi_wallet.addBaseNodePeer(peer[0], peer[1]); - ffi_wallet.startRecovery(peer[0]); + ffiWallet.addBaseNodePeer(peer[0], peer[1]); + ffiWallet.startRecovery(peer[0]); } ); Then( "Check callbacks for finished inbound tx on ffi wallet {word}", - async function (wallet_name) { - const wallet = this.getWallet(wallet_name); + async function (walletName) { + const wallet = this.getWallet(walletName); expect(wallet.receivedTransaction).to.be.greaterThanOrEqual(1); expect(wallet.transactionBroadcast).to.be.greaterThanOrEqual(1); wallet.clearCallbackCounters(); @@ -257,8 +238,8 @@ Then( Then( "Check callbacks for finished outbound tx on ffi wallet {word}", - async function (wallet_name) { - const wallet = this.getWallet(wallet_name); + async function (walletName) { + const wallet = this.getWallet(walletName); expect(wallet.receivedTransactionReply).to.be.greaterThanOrEqual(1); expect(wallet.transactionBroadcast).to.be.greaterThanOrEqual(1); wallet.clearCallbackCounters(); @@ -267,12 +248,13 @@ Then( Then( "I wait for ffi wallet {word} to receive {int} transaction", - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, amount) { + let wallet = this.getWallet(walletName); console.log("\n"); console.log( - "Waiting for " + wallet_name + " to receive " + amount + " transaction(s)" + "Waiting for " + walletName + " to receive " + amount + " transaction(s)" ); await waitForIterate( @@ -281,7 +263,7 @@ Then( }, true, 1000, - 700 + 120 ); if (!(wallet.getCounters().received >= amount)) { @@ -295,14 +277,14 @@ Then( Then( "I wait for ffi wallet {word} to receive {int} finalization", - { timeout: 126 * 1000 }, - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, amount) { + let wallet = this.getWallet(walletName); console.log("\n"); console.log( "Waiting for " + - wallet_name + + walletName + " to receive " + amount + " transaction finalization(s)" @@ -314,7 +296,7 @@ Then( }, true, 1000, - 700 + 120 ); if (!(wallet.getCounters().finalized >= amount)) { @@ -328,13 +310,14 @@ Then( Then( "I wait for ffi wallet {word} to receive {int} broadcast", - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, amount) { + let wallet = this.getWallet(walletName); console.log("\n"); console.log( "Waiting for " + - wallet_name + + walletName + " to receive " + amount + " transaction broadcast(s)" @@ -346,7 +329,7 @@ Then( }, true, 1000, - 700 + 120 ); if (!(wallet.getCounters().broadcast >= amount)) { @@ -360,17 +343,17 @@ Then( Then( "I wait for ffi wallet {word} to receive {int} mined", - { timeout: 7 * 1000 }, - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, amount) { + let wallet = this.getWallet(walletName); console.log("\n"); console.log( "Waiting for " + - wallet_name + + walletName + " to receive " + amount + - " transaction mined" + " transaction(s) mined" ); await waitForIterate( @@ -379,7 +362,7 @@ Then( }, true, 1000, - 700 + 120 ); if (!(wallet.getCounters().mined >= amount)) { @@ -392,15 +375,19 @@ Then( ); Then( - "I wait for ffi wallet {word} to receive at least {int} SAF message", - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); - - console.log("\n"); + "I wait for ffi wallet {word} to receive {word} {int} SAF message", + { timeout: 125 * 1000 }, + async function (walletName, comparison, amount) { + const atLeast = "AT_LEAST"; + const exactly = "EXACTLY"; + expect(comparison === atLeast || comparison === exactly).to.equal(true); + let wallet = this.getWallet(walletName); console.log( - "Waiting for " + - wallet_name + - " to receive at least " + + "\nWaiting for " + + walletName + + " to receive " + + comparison + + " " + amount + " SAF messages(s)" ); @@ -411,7 +398,7 @@ Then( }, true, 1000, - 700 + 120 ); if (!(wallet.getCounters().saf >= amount)) { @@ -419,18 +406,23 @@ Then( } else { console.log(wallet.getCounters()); } - expect(wallet.getCounters().saf >= amount).to.equal(true); + if (comparison === atLeast) { + expect(wallet.getCounters().saf >= amount).to.equal(true); + } else { + expect(wallet.getCounters().saf === amount).to.equal(true); + } } ); Then( "I wait for ffi wallet {word} to have at least {int} uT", - async function (wallet_name, amount) { - let wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName, amount) { + let wallet = this.getWallet(walletName); console.log("\n"); console.log( - "Waiting for " + wallet_name + " balance to be at least " + amount + " uT" + "Waiting for " + walletName + " balance to be at least " + amount + " uT" ); await waitForIterate( @@ -439,7 +431,7 @@ Then( }, true, 1000, - 700 + 120 ); let balance = wallet.getBalance().available; @@ -452,11 +444,54 @@ Then( } ); +Then( + "ffi wallet {word} detects {word} {int} ffi transactions to be Broadcast", + { timeout: 125 * 1000 }, + async function (walletName, comparison, amount) { + // Pending -> Completed -> Broadcast -> Mined Unconfirmed -> Mined Confirmed + const atLeast = "AT_LEAST"; + const exactly = "EXACTLY"; + expect(comparison === atLeast || comparison === exactly).to.equal(true); + const wallet = this.getWallet(walletName); + + console.log("\n"); + console.log( + "Waiting for " + + walletName + + " to have detected " + + comparison + + " " + + amount + + " broadcast transaction(s)" + ); + + await waitForIterate( + () => { + return wallet.getCounters().broadcast >= amount; + }, + true, + 1000, + 120 + ); + + if (!(wallet.getCounters().broadcast >= amount)) { + console.log("Counter not adequate!"); + } else { + console.log(wallet.getCounters()); + } + if (comparison === atLeast) { + expect(wallet.getCounters().broadcast >= amount).to.equal(true); + } else { + expect(wallet.getCounters().broadcast === amount).to.equal(true); + } + } +); + Then( "I wait for recovery of ffi wallet {word} to finish", { timeout: 600 * 1000 }, - function (wallet_name) { - const wallet = this.getWallet(wallet_name); + function (walletName) { + const wallet = this.getWallet(walletName); while (!wallet.recoveryFinished) { sleep(1000).then(); } @@ -491,8 +526,8 @@ Then( "I want to view the transaction kernels for completed transactions in ffi wallet {word}", { timeout: 20 * 1000 }, function (name) { - let ffi_wallet = this.getWallet(name); - let transactions = ffi_wallet.getCompletedTxs(); + let ffiWallet = this.getWallet(name); + let transactions = ffiWallet.getCompletedTxs(); let length = transactions.getLength(); expect(length > 0).to.equal(true); for (let i = 0; i < length; i++) { @@ -511,16 +546,21 @@ Then( } ); -When("I stop ffi wallet {word}", { timeout: 100 }, function (walletName) { - let wallet = this.getWallet(walletName); - wallet.stop(); - wallet.resetCounters(); -}); +When( + "I stop ffi wallet {word}", + { timeout: 20 * 1000 }, + async function (walletName) { + let wallet = this.getWallet(walletName); + await wallet.stop(); + wallet.resetCounters(); + } +); Then( "I start TXO validation on ffi wallet {word}", - async function (wallet_name) { - const wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName) { + const wallet = this.getWallet(walletName); await wallet.startTxoValidation(); while (!wallet.getTxoValidationStatus().txo_validation_complete) { await sleep(1000); @@ -530,8 +570,9 @@ Then( Then( "I start TX validation on ffi wallet {word}", - async function (wallet_name) { - const wallet = this.getWallet(wallet_name); + { timeout: 125 * 1000 }, + async function (walletName) { + const wallet = this.getWallet(walletName); await wallet.startTxValidation(); while (!wallet.getTxValidationStatus().tx_validation_complete) { await sleep(1000); diff --git a/integration_tests/features/support/steps.js b/integration_tests/features/support/steps.js index a4521bf15f..feeeed81ef 100644 --- a/integration_tests/features/support/steps.js +++ b/integration_tests/features/support/steps.js @@ -837,11 +837,7 @@ Then( Then(/node (.*) has a pruned height of (\d+)/, async function (name, height) { const client = this.getClient(name); - await waitFor( - async () => await client.getPrunedHeight(), - height, - 115 * 1000 - ); + await waitFor(async () => await client.getPrunedHeight(), height, 115 * 1000); const currentHeight = await client.getPrunedHeight(); console.log( `Node ${name} has a pruned height: ${currentHeight} (should be`, diff --git a/integration_tests/features/support/world.js b/integration_tests/features/support/world.js index 202f862626..47fe12cb85 100644 --- a/integration_tests/features/support/world.js +++ b/integration_tests/features/support/world.js @@ -417,17 +417,17 @@ BeforeAll({ timeout: 2400000 }, async function () { }); Before(async function (testCase) { - console.log(`Testing scenario "${testCase.pickle.name}"`); + console.log(`\nTesting scenario: "${testCase.pickle.name}"\n`); }); After(async function (testCase) { console.log("Stopping nodes"); + await stopAndHandleLogs(this.walletsFFI, testCase, this); await stopAndHandleLogs(this.seeds, testCase, this); await stopAndHandleLogs(this.nodes, testCase, this); await stopAndHandleLogs(this.proxies, testCase, this); - await stopAndHandleLogs(this.wallets, testCase, this); - await stopAndHandleLogs(this.walletsFFI, testCase, this); await stopAndHandleLogs(this.miners, testCase, this); + await stopAndHandleLogs(this.wallets, testCase, this); }); async function stopAndHandleLogs(objects, testCase, context) { diff --git a/integration_tests/helpers/ffi/balance.js b/integration_tests/helpers/ffi/balance.js new file mode 100644 index 0000000000..f7a55481e4 --- /dev/null +++ b/integration_tests/helpers/ffi/balance.js @@ -0,0 +1,43 @@ +const InterfaceFFI = require("./ffiInterface"); + +class Balance { + ptr; + + pointerAssign(ptr) { + if (this.ptr) { + this.destroy(); + this.ptr = ptr; + } else { + this.ptr = ptr; + } + } + + getPtr() { + return this.ptr; + } + + getAvailable() { + return InterfaceFFI.balanceGetAvailable(this.ptr); + } + + getTimeLocked() { + return InterfaceFFI.balanceGetTimeLocked(this.ptr); + } + + getPendingIncoming() { + return InterfaceFFI.balanceGetPendingIncoming(this.ptr); + } + + getPendingOutgoing() { + return InterfaceFFI.balanceGetPendingOutgoing(this.ptr); + } + + destroy() { + if (this.ptr) { + InterfaceFFI.balanceDestroy(this.ptr); + this.ptr = undefined; //prevent double free segfault + } + } +} + +module.exports = Balance; diff --git a/integration_tests/helpers/ffi/ffiInterface.js b/integration_tests/helpers/ffi/ffiInterface.js index 4eb485baef..cae9624528 100644 --- a/integration_tests/helpers/ffi/ffiInterface.js +++ b/integration_tests/helpers/ffi/ffiInterface.js @@ -13,7 +13,7 @@ class InterfaceFFI { static void = ref.types.void; static bool = ref.types.bool; static int = ref.types.int; - static ulonglong = ref.types.ulonglong; + static ulonglong = ref.types.uint64; // Note: 'ref.types.ulonglong' has a memory alignment problem static uchar = ref.types.uchar; static uint = ref.types.uint; static string = ref.types.CString; @@ -285,6 +285,7 @@ class InterfaceFFI { this.ptr, this.ptr, this.ptr, + this.ptr, this.boolPtr, this.intPtr, ], @@ -303,6 +304,10 @@ class InterfaceFFI { ], wallet_upsert_contact: [this.bool, [this.ptr, this.ptr, this.intPtr]], wallet_remove_contact: [this.bool, [this.ptr, this.ptr, this.intPtr]], + balance_get_available: [this.ulonglong, [this.ptr, this.intPtr]], + balance_get_time_locked: [this.ulonglong, [this.ptr, this.intPtr]], + balance_get_pending_incoming: [this.ulonglong, [this.ptr, this.intPtr]], + balance_get_pending_outgoing: [this.ulonglong, [this.ptr, this.intPtr]], wallet_get_available_balance: [this.ulonglong, [this.ptr, this.intPtr]], wallet_get_pending_incoming_balance: [ this.ulonglong, @@ -426,6 +431,7 @@ class InterfaceFFI { [this.ptr, this.ptr, this.ptr, this.intPtr], ], wallet_destroy: [this.void, [this.ptr]], + balance_destroy: [this.void, [this.ptr]], file_partial_backup: [this.void, [this.string, this.string, this.intPtr]], log_debug_message: [this.void, [this.string]], get_emoji_set: [this.ptr, []], @@ -1119,6 +1125,9 @@ class InterfaceFFI { static createCallbackTxoValidationComplete(fn) { return ffi.Callback(this.void, [this.ulonglong, this.uchar], fn); } + static createCallbackBalanceUpdated(fn) { + return ffi.Callback(this.void, [this.ptr], fn); + } static createCallbackTransactionValidationComplete(fn) { return ffi.Callback(this.void, [this.ulonglong, this.uchar], fn); } @@ -1151,6 +1160,7 @@ class InterfaceFFI { callback_store_and_forward_send_result, callback_transaction_cancellation, callback_txo_validation_complete, + callback_balance_updated, callback_transaction_validation_complete, callback_saf_message_received ) { @@ -1174,6 +1184,7 @@ class InterfaceFFI { callback_store_and_forward_send_result, callback_transaction_cancellation, callback_txo_validation_complete, + callback_balance_updated, callback_transaction_validation_complete, callback_saf_message_received, recovery_in_progress, @@ -1236,6 +1247,34 @@ class InterfaceFFI { return result; } + static balanceGetAvailable(ptr) { + let error = this.initError(); + let result = this.fn.balance_get_available(ptr, error); + this.checkErrorResult(error, `balanceGetAvailable`); + return result; + } + + static balanceGetTimeLocked(ptr) { + let error = this.initError(); + let result = this.fn.balance_get_available(ptr, error); + this.checkErrorResult(error, `balanceGetTimeLocked`); + return result; + } + + static balanceGetPendingIncoming(ptr) { + let error = this.initError(); + let result = this.fn.balance_get_pending_incoming(ptr, error); + this.checkErrorResult(error, `balanceGetPendingIncoming`); + return result; + } + + static balanceGetPendingOutgoing(ptr) { + let error = this.initError(); + let result = this.fn.balance_get_pending_outgoing(ptr, error); + this.checkErrorResult(error, `balanceGetPendingOutgoing`); + return result; + } + static walletGetAvailableBalance(ptr) { let error = this.initError(); let result = this.fn.wallet_get_available_balance(ptr, error); @@ -1534,6 +1573,10 @@ class InterfaceFFI { static walletDestroy(ptr) { this.fn.wallet_destroy(ptr); } + + static balanceDestroy(ptr) { + this.fn.balance_destroy(ptr); + } //endregion } module.exports = InterfaceFFI; diff --git a/integration_tests/helpers/ffi/wallet.js b/integration_tests/helpers/ffi/wallet.js index 70856b18f4..dfdf817e9b 100644 --- a/integration_tests/helpers/ffi/wallet.js +++ b/integration_tests/helpers/ffi/wallet.js @@ -7,6 +7,7 @@ const PendingInboundTransactions = require("./pendingInboundTransactions"); const PendingOutboundTransactions = require("./pendingOutboundTransactions"); const Contact = require("./contact"); const Contacts = require("./contacts"); +const Balance = require("./balance"); const utf8 = require("utf8"); @@ -31,6 +32,7 @@ class Wallet { callback_direct_send_result; callback_store_and_forward_send_result; callback_transaction_cancellation; + callback_balance_updated; callback_transaction_validation_complete; callback_saf_message_received; recoveryProgressCallback; @@ -119,6 +121,9 @@ class Wallet { InterfaceFFI.createCallbackTxoValidationComplete( this.onTxoValidationComplete ); + this.callback_balance_updated = InterfaceFFI.createCallbackBalanceUpdated( + this.onBalanceUpdated + ); this.callback_transaction_validation_complete = InterfaceFFI.createCallbackTransactionValidationComplete( this.onTransactionValidationComplete @@ -165,6 +170,7 @@ class Wallet { this.callback_store_and_forward_send_result, this.callback_transaction_cancellation, this.callback_txo_validation_complete, + this.callback_balance_updated, this.callback_transaction_validation_complete, this.callback_saf_message_received ); @@ -261,6 +267,15 @@ class Wallet { this.txo_validation_result = validation_results; }; + onBalanceUpdated = (ptr) => { + let b = new Balance(); + b.pointerAssign(ptr); + console.log( + `${new Date().toISOString()} callbackBalanceUpdated: available = ${b.getAvailable()}, time locked = ${b.getTimeLocked()} pending incoming = ${b.getPendingIncoming()} pending outgoing = ${b.getPendingOutgoing()}` + ); + b.destroy(); + }; + onTransactionValidationComplete = (request_key, validation_results) => { console.log( `${new Date().toISOString()} callbackTransactionValidationComplete(${request_key},${validation_results})` @@ -422,6 +437,7 @@ class Wallet { this.callback_store_and_forward_send_result = this.callback_transaction_cancellation = this.callback_txo_validation_complete = + this.callback_balance_updated = this.callback_transaction_validation_complete = this.callback_saf_message_received = this.recoveryProgressCallback = diff --git a/integration_tests/helpers/ffi/walletFFI.js b/integration_tests/helpers/ffi/walletFFI.js deleted file mode 100644 index e2c032a5fd..0000000000 --- a/integration_tests/helpers/ffi/walletFFI.js +++ /dev/null @@ -1,2041 +0,0 @@ -/** - * This library was AUTO-GENERATED. Do not modify manually! - */ - -const { expect } = require("chai"); -const ffi = require("ffi-napi"); -const ref = require("ref-napi"); -const dateFormat = require("dateformat"); -const { spawn } = require("child_process"); -const fs = require("fs"); - -class WalletFFI { - static byte_vector = ref.types.void; - static byte_vector_ptr = ref.refType(this.byte_vector); - static tari_comms_config = ref.types.void; - static tari_comms_config_ptr = ref.refType(this.tari_comms_config); - static tari_private_key = ref.types.void; - static tari_private_key_ptr = ref.refType(this.tari_private_key); - static tari_wallet = ref.types.void; - static tari_wallet_ptr = ref.refType(this.tari_wallet); - static tari_public_key = ref.types.void; - static tari_public_key_ptr = ref.refType(this.tari_public_key); - static tari_contacts = ref.types.void; - static tari_contacts_ptr = ref.refType(this.tari_contacts); - static tari_contact = ref.types.void; - static tari_contact_ptr = ref.refType(this.tari_contact); - static tari_completed_transactions = ref.types.void; - static tari_completed_transactions_ptr = ref.refType( - this.tari_completed_transactions - ); - static tari_completed_transaction = ref.types.void; - static tari_completed_transaction_ptr = ref.refType( - this.tari_completed_transaction - ); - static tari_pending_outbound_transactions = ref.types.void; - static tari_pending_outbound_transactions_ptr = ref.refType( - this.tari_pending_outbound_transactions - ); - static tari_pending_outbound_transaction = ref.types.void; - static tari_pending_outbound_transaction_ptr = ref.refType( - this.tari_pending_outbound_transaction - ); - static tari_pending_inbound_transactions = ref.types.void; - static tari_pending_inbound_transactions_ptr = ref.refType( - this.tari_pending_inbound_transactions - ); - static tari_pending_inbound_transaction = ref.types.void; - static tari_pending_inbound_transaction_ptr = ref.refType( - this.tari_pending_inbound_transaction - ); - static tari_transport_type = ref.types.void; - static tari_transport_type_ptr = ref.refType(this.tari_transport_type); - static tari_seed_words = ref.types.void; - static tari_seed_words_ptr = ref.refType(this.tari_seed_words); - static emoji_set = ref.types.void; - static emoji_set_ptr = ref.refType(this.emoji_set); - static tari_excess = ref.types.void; - static tari_excess_ptr = ref.refType(this.tari_excess); - static tari_excess_public_nonce = ref.types.void; - static tari_excess_public_nonce_ptr = ref.refType( - this.tari_excess_public_nonce - ); - static tari_excess_signature = ref.types.void; - static tari_excess_signature_ptr = ref.refType(this.tari_excess_signature); - - static #fn; - static error = ref.alloc(ref.types.int); - static recovery_in_progress = ref.alloc(ref.types.bool); - static NULL = ref.NULL; - static #loaded = false; - static #ps = null; - - static checkAsyncRes(resolve, reject, error_name) { - return (err, res) => { - if (err) reject(err); - expect(this.error.deref()).to.equal(0, `Error in ${error_name}`); - resolve(res); - }; - } - - static compile() { - return new Promise((resolve, _reject) => { - const cmd = "cargo"; - const args = [ - "build", - "--release", - "--package", - "tari_wallet_ffi", - "-Z", - "unstable-options", - "--out-dir", - process.cwd() + "/temp/out", - ]; - const baseDir = `./temp/base_nodes/${dateFormat( - new Date(), - "yyyymmddHHMM" - )}/WalletFFI-compile`; - if (!fs.existsSync(baseDir)) { - fs.mkdirSync(baseDir, { recursive: true }); - fs.mkdirSync(baseDir + "/log", { recursive: true }); - } - const ps = spawn(cmd, args, { - cwd: baseDir, - env: { ...process.env }, - }); - ps.on("close", (_code) => { - resolve(ps); - }); - ps.stderr.on("data", (data) => { - console.log("stderr : ", data.toString()); - }); - ps.on("error", (error) => { - console.log("error : ", error.toString()); - }); - expect(ps.error).to.be.an("undefined"); - this.#ps = ps; - }); - } - - static async Init() { - if (this.#loaded) { - return; - } - - this.#loaded = true; - await this.compile(); - const outputProcess = `${process.cwd()}/temp/out/${ - process.platform === "win32" ? "" : "lib" - }tari_wallet_ffi`; - - // Init callbacks - - this.createCallbackReceivedTransaction = (callback) => - ffi.Callback( - "void", - [this.tari_pending_inbound_transaction_ptr], - callback - ); - this.createCallbackReceivedTransactionReply = (callback) => - ffi.Callback("void", [this.tari_completed_transaction_ptr], callback); - this.createCallbackReceivedFinalizedTransaction = (callback) => - ffi.Callback("void", [this.tari_completed_transaction_ptr], callback); - this.createCallbackTransactionBroadcast = (callback) => - ffi.Callback("void", [this.tari_completed_transaction_ptr], callback); - this.createCallbackTransactionMined = (callback) => - ffi.Callback("void", [this.tari_completed_transaction_ptr], callback); - this.createCallbackTransactionMinedUnconfirmed = (callback) => - ffi.Callback( - "void", - [this.tari_completed_transaction_ptr, "uint64"], - callback - ); - this.createCallbackDirectSendResult = (callback) => - ffi.Callback("void", ["uint64", "bool"], callback); - this.createCallbackStoreAndForwardSendResult = (callback) => - ffi.Callback("void", ["uint64", "bool"], callback); - this.createCallbackTransactionCancellation = (callback) => - ffi.Callback("void", [this.tari_completed_transaction_ptr], callback); - this.createCallbackTxoValidationComplete = (callback) => - ffi.Callback("void", ["uint64", "uchar"], callback); - this.createCallbackTransactionValidationComplete = (callback) => - ffi.Callback("void", ["uint64", "uchar"], callback); - this.createCallbackSafMessageReceived = (callback) => - ffi.Callback("void", [], callback); - this.createRecoveryProgressCallback = (callback) => - ffi.Callback("void", ["uchar", "uint64", "uint64"], callback); - // Load the library - this.#fn = ffi.Library(outputProcess, { - transport_memory_create: [this.tari_transport_type_ptr, []], - transport_tcp_create: [this.tari_transport_type_ptr, ["string", "int*"]], - transport_tor_create: [ - this.tari_transport_type_ptr, - ["string", this.byte_vector_ptr, "ushort", "string", "string", "int*"], - ], - transport_memory_get_address: [ - "char*", - [this.tari_transport_type_ptr, "int*"], - ], - transport_type_destroy: ["void", [this.tari_transport_type_ptr]], - string_destroy: ["void", ["string"]], - byte_vector_create: [this.byte_vector_ptr, ["uchar*", "uint", "int*"]], - byte_vector_get_at: ["uchar", [this.byte_vector_ptr, "uint", "int*"]], - byte_vector_get_length: ["uint", [this.byte_vector_ptr, "int*"]], - byte_vector_destroy: ["void", [this.byte_vector_ptr]], - public_key_create: [ - this.tari_public_key_ptr, - [this.byte_vector_ptr, "int*"], - ], - public_key_get_bytes: [ - this.byte_vector_ptr, - [this.tari_public_key_ptr, "int*"], - ], - public_key_from_private_key: [ - this.tari_public_key_ptr, - [this.tari_private_key_ptr, "int*"], - ], - public_key_from_hex: [this.tari_public_key_ptr, ["string", "int*"]], - public_key_destroy: ["void", [this.tari_public_key_ptr]], - public_key_to_emoji_id: ["char*", [this.tari_public_key_ptr, "int*"]], - emoji_id_to_public_key: [this.tari_public_key_ptr, ["string", "int*"]], - private_key_create: [ - this.tari_private_key_ptr, - [this.byte_vector_ptr, "int*"], - ], - private_key_generate: [this.tari_private_key_ptr, []], - private_key_get_bytes: [ - this.byte_vector_ptr, - [this.tari_private_key_ptr, "int*"], - ], - private_key_from_hex: [this.tari_private_key_ptr, ["string", "int*"]], - private_key_destroy: ["void", [this.tari_private_key_ptr]], - seed_words_create: [this.tari_seed_words_ptr, []], - seed_words_get_length: ["uint", [this.tari_seed_words_ptr, "int*"]], - seed_words_get_at: ["char*", [this.tari_seed_words_ptr, "uint", "int*"]], - seed_words_push_word: [ - "uchar", - [this.tari_seed_words_ptr, "string", "int*"], - ], - seed_words_destroy: ["void", [this.tari_seed_words_ptr]], - contact_create: [ - this.tari_contact_ptr, - ["string", this.tari_public_key_ptr, "int*"], - ], - contact_get_alias: ["char*", [this.tari_contact_ptr, "int*"]], - contact_get_public_key: [ - this.tari_public_key_ptr, - [this.tari_contact_ptr, "int*"], - ], - contact_destroy: ["void", [this.tari_contact_ptr]], - contacts_get_length: ["uint", [this.tari_contacts_ptr, "int*"]], - contacts_get_at: [ - this.tari_contact_ptr, - [this.tari_contacts_ptr, "uint", "int*"], - ], - contacts_destroy: ["void", [this.tari_contacts_ptr]], - completed_transaction_get_destination_public_key: [ - this.tari_public_key_ptr, - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_source_public_key: [ - this.tari_public_key_ptr, - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_amount: [ - "uint64", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_fee: [ - "uint64", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_message: [ - "char*", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_status: [ - "int", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_transaction_id: [ - "uint64", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_timestamp: [ - "uint64", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_is_valid: [ - "bool", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_is_outbound: [ - "bool", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_confirmations: [ - "uint64", - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_destroy: [ - "void", - [this.tari_completed_transaction_ptr], - ], - completed_transaction_get_excess: [ - this.tari_excess_ptr, - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_public_nonce: [ - this.tari_excess_public_nonce_ptr, - [this.tari_completed_transaction_ptr, "int*"], - ], - completed_transaction_get_signature: [ - this.tari_excess_signature_ptr, - [this.tari_completed_transaction_ptr, "int*"], - ], - excess_destroy: ["void", [this.tari_excess_ptr]], - nonce_destroy: ["void", [this.tari_excess_public_nonce_ptr]], - signature_destroy: ["void", [this.tari_excess_signature_ptr]], - completed_transactions_get_length: [ - "uint", - [this.tari_completed_transactions_ptr, "int*"], - ], - completed_transactions_get_at: [ - this.tari_completed_transaction_ptr, - [this.tari_completed_transactions_ptr, "uint", "int*"], - ], - completed_transactions_destroy: [ - "void", - [this.tari_completed_transactions_ptr], - ], - pending_outbound_transaction_get_transaction_id: [ - "uint64", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_destination_public_key: [ - this.tari_public_key_ptr, - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_amount: [ - "uint64", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_fee: [ - "uint64", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_message: [ - "char*", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_timestamp: [ - "uint64", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_get_status: [ - "int", - [this.tari_pending_outbound_transaction_ptr, "int*"], - ], - pending_outbound_transaction_destroy: [ - "void", - [this.tari_pending_outbound_transaction_ptr], - ], - pending_outbound_transactions_get_length: [ - "uint", - [this.tari_pending_outbound_transactions_ptr, "int*"], - ], - pending_outbound_transactions_get_at: [ - this.tari_pending_outbound_transaction_ptr, - [this.tari_pending_outbound_transactions_ptr, "uint", "int*"], - ], - pending_outbound_transactions_destroy: [ - "void", - [this.tari_pending_outbound_transactions_ptr], - ], - pending_inbound_transaction_get_transaction_id: [ - "uint64", - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_get_source_public_key: [ - this.tari_public_key_ptr, - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_get_message: [ - "char*", - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_get_amount: [ - "uint64", - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_get_timestamp: [ - "uint64", - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_get_status: [ - "int", - [this.tari_pending_inbound_transaction_ptr, "int*"], - ], - pending_inbound_transaction_destroy: [ - "void", - [this.tari_pending_inbound_transaction_ptr], - ], - pending_inbound_transactions_get_length: [ - "uint", - [this.tari_pending_inbound_transactions_ptr, "int*"], - ], - pending_inbound_transactions_get_at: [ - this.tari_pending_inbound_transaction_ptr, - [this.tari_pending_inbound_transactions_ptr, "uint", "int*"], - ], - pending_inbound_transactions_destroy: [ - "void", - [this.tari_pending_inbound_transactions_ptr], - ], - comms_config_create: [ - this.tari_comms_config_ptr, - [ - "string", - this.tari_transport_type_ptr, - "string", - "string", - "uint64", - "uint64", - "string", - "int*", - ], - ], - comms_config_destroy: ["void", [this.tari_comms_config_ptr]], - wallet_create: [ - this.tari_wallet_ptr, - [ - this.tari_comms_config_ptr, - "string", - "uint", - "uint", - "string", - this.tari_seed_words_ptr, - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "pointer", - "bool*", - "int*", - ], - ], - wallet_sign_message: ["char*", [this.tari_wallet_ptr, "string", "int*"]], - wallet_verify_message_signature: [ - "bool", - [ - this.tari_wallet_ptr, - this.tari_public_key_ptr, - "string", - "string", - "int*", - ], - ], - wallet_add_base_node_peer: [ - "bool", - [this.tari_wallet_ptr, this.tari_public_key_ptr, "string", "int*"], - ], - wallet_upsert_contact: [ - "bool", - [this.tari_wallet_ptr, this.tari_contact_ptr, "int*"], - ], - wallet_remove_contact: [ - "bool", - [this.tari_wallet_ptr, this.tari_contact_ptr, "int*"], - ], - wallet_get_available_balance: ["uint64", [this.tari_wallet_ptr, "int*"]], - wallet_get_pending_incoming_balance: [ - "uint64", - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_pending_outgoing_balance: [ - "uint64", - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_fee_estimate: [ - "uint64", - [this.tari_wallet_ptr, "uint64", "uint64", "uint64", "uint64", "int*"], - ], - wallet_get_num_confirmations_required: [ - "uint64", - [this.tari_wallet_ptr, "int*"], - ], - wallet_set_num_confirmations_required: [ - "void", - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_send_transaction: [ - "uint64", - [ - this.tari_wallet_ptr, - this.tari_public_key_ptr, - "uint64", - "uint64", - "string", - "int*", - ], - ], - wallet_get_contacts: [ - this.tari_contacts_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_completed_transactions: [ - this.tari_completed_transactions_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_pending_outbound_transactions: [ - this.tari_pending_outbound_transactions_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_public_key: [ - this.tari_public_key_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_pending_inbound_transactions: [ - this.tari_pending_inbound_transactions_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_cancelled_transactions: [ - this.tari_completed_transactions_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_get_completed_transaction_by_id: [ - this.tari_completed_transaction_ptr, - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_get_pending_outbound_transaction_by_id: [ - this.tari_pending_outbound_transaction_ptr, - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_get_pending_inbound_transaction_by_id: [ - this.tari_pending_inbound_transaction_ptr, - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_get_cancelled_transaction_by_id: [ - this.tari_completed_transaction_ptr, - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_import_utxo: [ - "uint64", - [ - this.tari_wallet_ptr, - "uint64", - this.tari_private_key_ptr, - this.tari_public_key_ptr, - "string", - "int*", - ], - ], - wallet_start_txo_validation: ["uint64", [this.tari_wallet_ptr, "int*"]], - wallet_start_transaction_validation: [ - "uint64", - [this.tari_wallet_ptr, "int*"], - ], - wallet_restart_transaction_broadcast: [ - "bool", - [this.tari_wallet_ptr, "int*"], - ], - wallet_set_low_power_mode: ["void", [this.tari_wallet_ptr, "int*"]], - wallet_set_normal_power_mode: ["void", [this.tari_wallet_ptr, "int*"]], - wallet_cancel_pending_transaction: [ - "bool", - [this.tari_wallet_ptr, "uint64", "int*"], - ], - wallet_coin_split: [ - "uint64", - [ - this.tari_wallet_ptr, - "uint64", - "uint64", - "uint64", - "string", - "uint64", - "int*", - ], - ], - wallet_get_seed_words: [ - this.tari_seed_words_ptr, - [this.tari_wallet_ptr, "int*"], - ], - wallet_apply_encryption: [ - "void", - [this.tari_wallet_ptr, "string", "int*"], - ], - wallet_remove_encryption: ["void", [this.tari_wallet_ptr, "int*"]], - wallet_set_key_value: [ - "bool", - [this.tari_wallet_ptr, "string", "string", "int*"], - ], - wallet_get_value: ["char*", [this.tari_wallet_ptr, "string", "int*"]], - wallet_clear_value: ["bool", [this.tari_wallet_ptr, "string", "int*"]], - wallet_is_recovery_in_progress: ["bool", [this.tari_wallet_ptr, "int*"]], - wallet_start_recovery: [ - "bool", - [this.tari_wallet_ptr, this.tari_public_key_ptr, "pointer", "int*"], - ], - wallet_destroy: ["void", [this.tari_wallet_ptr]], - file_partial_backup: ["void", ["string", "string", "int*"]], - log_debug_message: ["void", ["string"]], - get_emoji_set: [this.emoji_set_ptr, []], - emoji_set_destroy: ["void", [this.emoji_set_ptr]], - emoji_set_get_at: [ - this.byte_vector_ptr, - [this.emoji_set_ptr, "uint", "int*"], - ], - emoji_set_get_length: ["uint", [this.emoji_set_ptr, "int*"]], - }); - } - - static transportMemoryCreate() { - return new Promise((resolve, reject) => - this.#fn.transport_memory_create.async( - this.checkAsyncRes(resolve, reject, "transportMemoryCreate") - ) - ); - } - - static transportTcpCreate(listener_address) { - return new Promise((resolve, reject) => - this.#fn.transport_tcp_create.async( - listener_address, - this.error, - this.checkAsyncRes(resolve, reject, "transportTcpCreate") - ) - ); - } - - static transportTorCreate( - control_server_address, - tor_cookie, - tor_port, - socks_username, - socks_password - ) { - return new Promise((resolve, reject) => - this.#fn.transport_tor_create.async( - control_server_address, - tor_cookie, - tor_port, - socks_username, - socks_password, - this.error, - this.checkAsyncRes(resolve, reject, "transportTorCreate") - ) - ); - } - - static transportMemoryGetAddress(transport) { - return new Promise((resolve, reject) => - this.#fn.transport_memory_get_address.async( - transport, - this.error, - this.checkAsyncRes(resolve, reject, "transportMemoryGetAddress") - ) - ); - } - - static transportTypeDestroy(transport) { - return new Promise((resolve, reject) => - this.#fn.transport_type_destroy.async( - transport, - this.checkAsyncRes(resolve, reject, "transportTypeDestroy") - ) - ); - } - - static stringDestroy(s) { - return new Promise((resolve, reject) => - this.#fn.string_destroy.async( - s, - this.checkAsyncRes(resolve, reject, "stringDestroy") - ) - ); - } - - static byteVectorCreate(byte_array, element_count) { - return new Promise((resolve, reject) => - this.#fn.byte_vector_create.async( - byte_array, - element_count, - this.error, - this.checkAsyncRes(resolve, reject, "byteVectorCreate") - ) - ); - } - - static byteVectorGetAt(ptr, i) { - return new Promise((resolve, reject) => - this.#fn.byte_vector_get_at.async( - ptr, - i, - this.error, - this.checkAsyncRes(resolve, reject, "byteVectorGetAt") - ) - ); - } - - static byteVectorGetLength(vec) { - return new Promise((resolve, reject) => - this.#fn.byte_vector_get_length.async( - vec, - this.error, - this.checkAsyncRes(resolve, reject, "byteVectorGetLength") - ) - ); - } - - static byteVectorDestroy(bytes) { - return new Promise((resolve, reject) => - this.#fn.byte_vector_destroy.async( - bytes, - this.checkAsyncRes(resolve, reject, "byteVectorDestroy") - ) - ); - } - - static publicKeyCreate(bytes) { - return new Promise((resolve, reject) => - this.#fn.public_key_create.async( - bytes, - this.error, - this.checkAsyncRes(resolve, reject, "publicKeyCreate") - ) - ); - } - - static publicKeyGetBytes(public_key) { - return new Promise((resolve, reject) => - this.#fn.public_key_get_bytes.async( - public_key, - this.error, - this.checkAsyncRes(resolve, reject, "publicKeyGetBytes") - ) - ); - } - - static publicKeyFromPrivateKey(secret_key) { - return new Promise((resolve, reject) => - this.#fn.public_key_from_private_key.async( - secret_key, - this.error, - this.checkAsyncRes(resolve, reject, "publicKeyFromPrivateKey") - ) - ); - } - - static publicKeyFromHex(hex) { - return new Promise((resolve, reject) => - this.#fn.public_key_from_hex.async( - hex, - this.error, - this.checkAsyncRes(resolve, reject, "publicKeyFromHex") - ) - ); - } - - static publicKeyDestroy(pk) { - return new Promise((resolve, reject) => - this.#fn.public_key_destroy.async( - pk, - this.checkAsyncRes(resolve, reject, "publicKeyDestroy") - ) - ); - } - - static publicKeyToEmojiId(pk) { - return new Promise((resolve, reject) => - this.#fn.public_key_to_emoji_id.async( - pk, - this.error, - this.checkAsyncRes(resolve, reject, "publicKeyToEmojiId") - ) - ); - } - - static emojiIdToPublicKey(emoji) { - return new Promise((resolve, reject) => - this.#fn.emoji_id_to_public_key.async( - emoji, - this.error, - this.checkAsyncRes(resolve, reject, "emojiIdToPublicKey") - ) - ); - } - - static privateKeyCreate(bytes) { - return new Promise((resolve, reject) => - this.#fn.private_key_create.async( - bytes, - this.error, - this.checkAsyncRes(resolve, reject, "privateKeyCreate") - ) - ); - } - - static privateKeyGenerate() { - return new Promise((resolve, reject) => - this.#fn.private_key_generate.async( - this.checkAsyncRes(resolve, reject, "privateKeyGenerate") - ) - ); - } - - static privateKeyGetBytes(private_key) { - return new Promise((resolve, reject) => - this.#fn.private_key_get_bytes.async( - private_key, - this.error, - this.checkAsyncRes(resolve, reject, "privateKeyGetBytes") - ) - ); - } - - static privateKeyFromHex(hex) { - return new Promise((resolve, reject) => - this.#fn.private_key_from_hex.async( - hex, - this.error, - this.checkAsyncRes(resolve, reject, "privateKeyFromHex") - ) - ); - } - - static privateKeyDestroy(pk) { - return new Promise((resolve, reject) => - this.#fn.private_key_destroy.async( - pk, - this.checkAsyncRes(resolve, reject, "privateKeyDestroy") - ) - ); - } - - static seedWordsCreate() { - return new Promise((resolve, reject) => - this.#fn.seed_words_create.async( - this.checkAsyncRes(resolve, reject, "seedWordsCreate") - ) - ); - } - - static seedWordsGetLength(seed_words) { - return new Promise((resolve, reject) => - this.#fn.seed_words_get_length.async( - seed_words, - this.error, - this.checkAsyncRes(resolve, reject, "seedWordsGetLength") - ) - ); - } - - static seedWordsGetAt(seed_words, position) { - return new Promise((resolve, reject) => - this.#fn.seed_words_get_at.async( - seed_words, - position, - this.error, - this.checkAsyncRes(resolve, reject, "seedWordsGetAt") - ) - ); - } - - static seedWordsPushWord(seed_words, word) { - return new Promise((resolve, reject) => - this.#fn.seed_words_push_word.async( - seed_words, - word, - this.error, - this.checkAsyncRes(resolve, reject, "seedWordsPushWord") - ) - ); - } - - static seedWordsDestroy(seed_words) { - return new Promise((resolve, reject) => - this.#fn.seed_words_destroy.async( - seed_words, - this.checkAsyncRes(resolve, reject, "seedWordsDestroy") - ) - ); - } - - static contactCreate(alias, public_key) { - return new Promise((resolve, reject) => - this.#fn.contact_create.async( - alias, - public_key, - this.error, - this.checkAsyncRes(resolve, reject, "contactCreate") - ) - ); - } - - static contactGetAlias(contact) { - return new Promise((resolve, reject) => - this.#fn.contact_get_alias.async( - contact, - this.error, - this.checkAsyncRes(resolve, reject, "contactGetAlias") - ) - ); - } - - static contactGetPublicKey(contact) { - return new Promise((resolve, reject) => - this.#fn.contact_get_public_key.async( - contact, - this.error, - this.checkAsyncRes(resolve, reject, "contactGetPublicKey") - ) - ); - } - - static contactDestroy(contact) { - return new Promise((resolve, reject) => - this.#fn.contact_destroy.async( - contact, - this.checkAsyncRes(resolve, reject, "contactDestroy") - ) - ); - } - - static contactsGetLength(contacts) { - return new Promise((resolve, reject) => - this.#fn.contacts_get_length.async( - contacts, - this.error, - this.checkAsyncRes(resolve, reject, "contactsGetLength") - ) - ); - } - - static contactsGetAt(contacts, position) { - return new Promise((resolve, reject) => - this.#fn.contacts_get_at.async( - contacts, - position, - this.error, - this.checkAsyncRes(resolve, reject, "contactsGetAt") - ) - ); - } - - static contactsDestroy(contacts) { - return new Promise((resolve, reject) => - this.#fn.contacts_destroy.async( - contacts, - this.checkAsyncRes(resolve, reject, "contactsDestroy") - ) - ); - } - - static completedTransactionGetDestinationPublicKey(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_destination_public_key.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "completedTransactionGetDestinationPublicKey" - ) - ) - ); - } - - static completedTransactionGetSourcePublicKey(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_source_public_key.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "completedTransactionGetSourcePublicKey" - ) - ) - ); - } - - static completedTransactionGetAmount(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_amount.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetAmount") - ) - ); - } - - static completedTransactionGetFee(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_fee.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetFee") - ) - ); - } - - static completedTransactionGetMessage(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_message.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetMessage") - ) - ); - } - - static completedTransactionGetStatus(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_status.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetStatus") - ) - ); - } - - static completedTransactionGetTransactionId(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_transaction_id.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "completedTransactionGetTransactionId" - ) - ) - ); - } - - static completedTransactionGetTimestamp(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_timestamp.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetTimestamp") - ) - ); - } - - static completedTransactionIsValid(tx) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_is_valid.async( - tx, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionIsValid") - ) - ); - } - - static completedTransactionIsOutbound(tx) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_is_outbound.async( - tx, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionIsOutbound") - ) - ); - } - - static completedTransactionGetConfirmations(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_confirmations.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "completedTransactionGetConfirmations" - ) - ) - ); - } - - static completedTransactionDestroy(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_destroy.async( - transaction, - this.checkAsyncRes(resolve, reject, "completedTransactionDestroy") - ) - ); - } - - static completedTransactionGetExcess(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_excess.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetExcess") - ) - ); - } - - static completedTransactionGetPublicNonce(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_public_nonce.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "completedTransactionGetPublicNonce" - ) - ) - ); - } - - static completedTransactionGetSignature(transaction) { - return new Promise((resolve, reject) => - this.#fn.completed_transaction_get_signature.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionGetSignature") - ) - ); - } - - static excessDestroy(excess) { - return new Promise((resolve, reject) => - this.#fn.excess_destroy.async( - excess, - this.checkAsyncRes(resolve, reject, "excessDestroy") - ) - ); - } - - static nonceDestroy(nonce) { - return new Promise((resolve, reject) => - this.#fn.nonce_destroy.async( - nonce, - this.checkAsyncRes(resolve, reject, "nonceDestroy") - ) - ); - } - - static signatureDestroy(signature) { - return new Promise((resolve, reject) => - this.#fn.signature_destroy.async( - signature, - this.checkAsyncRes(resolve, reject, "signatureDestroy") - ) - ); - } - - static completedTransactionsGetLength(transactions) { - return new Promise((resolve, reject) => - this.#fn.completed_transactions_get_length.async( - transactions, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionsGetLength") - ) - ); - } - - static completedTransactionsGetAt(transactions, position) { - return new Promise((resolve, reject) => - this.#fn.completed_transactions_get_at.async( - transactions, - position, - this.error, - this.checkAsyncRes(resolve, reject, "completedTransactionsGetAt") - ) - ); - } - - static completedTransactionsDestroy(transactions) { - return new Promise((resolve, reject) => - this.#fn.completed_transactions_destroy.async( - transactions, - this.checkAsyncRes(resolve, reject, "completedTransactionsDestroy") - ) - ); - } - - static pendingOutboundTransactionGetTransactionId(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_transaction_id.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetTransactionId" - ) - ) - ); - } - - static pendingOutboundTransactionGetDestinationPublicKey(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_destination_public_key.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetDestinationPublicKey" - ) - ) - ); - } - - static pendingOutboundTransactionGetAmount(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_amount.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetAmount" - ) - ) - ); - } - - static pendingOutboundTransactionGetFee(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_fee.async( - transaction, - this.error, - this.checkAsyncRes(resolve, reject, "pendingOutboundTransactionGetFee") - ) - ); - } - - static pendingOutboundTransactionGetMessage(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_message.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetMessage" - ) - ) - ); - } - - static pendingOutboundTransactionGetTimestamp(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_timestamp.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetTimestamp" - ) - ) - ); - } - - static pendingOutboundTransactionGetStatus(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_get_status.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionGetStatus" - ) - ) - ); - } - - static pendingOutboundTransactionDestroy(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transaction_destroy.async( - transaction, - this.checkAsyncRes(resolve, reject, "pendingOutboundTransactionDestroy") - ) - ); - } - - static pendingOutboundTransactionsGetLength(transactions) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transactions_get_length.async( - transactions, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionsGetLength" - ) - ) - ); - } - - static pendingOutboundTransactionsGetAt(transactions, position) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transactions_get_at.async( - transactions, - position, - this.error, - this.checkAsyncRes(resolve, reject, "pendingOutboundTransactionsGetAt") - ) - ); - } - - static pendingOutboundTransactionsDestroy(transactions) { - return new Promise((resolve, reject) => - this.#fn.pending_outbound_transactions_destroy.async( - transactions, - this.checkAsyncRes( - resolve, - reject, - "pendingOutboundTransactionsDestroy" - ) - ) - ); - } - - static pendingInboundTransactionGetTransactionId(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_transaction_id.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetTransactionId" - ) - ) - ); - } - - static pendingInboundTransactionGetSourcePublicKey(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_source_public_key.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetSourcePublicKey" - ) - ) - ); - } - - static pendingInboundTransactionGetMessage(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_message.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetMessage" - ) - ) - ); - } - - static pendingInboundTransactionGetAmount(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_amount.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetAmount" - ) - ) - ); - } - - static pendingInboundTransactionGetTimestamp(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_timestamp.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetTimestamp" - ) - ) - ); - } - - static pendingInboundTransactionGetStatus(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_get_status.async( - transaction, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionGetStatus" - ) - ) - ); - } - - static pendingInboundTransactionDestroy(transaction) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transaction_destroy.async( - transaction, - this.checkAsyncRes(resolve, reject, "pendingInboundTransactionDestroy") - ) - ); - } - - static pendingInboundTransactionsGetLength(transactions) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transactions_get_length.async( - transactions, - this.error, - this.checkAsyncRes( - resolve, - reject, - "pendingInboundTransactionsGetLength" - ) - ) - ); - } - - static pendingInboundTransactionsGetAt(transactions, position) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transactions_get_at.async( - transactions, - position, - this.error, - this.checkAsyncRes(resolve, reject, "pendingInboundTransactionsGetAt") - ) - ); - } - - static pendingInboundTransactionsDestroy(transactions) { - return new Promise((resolve, reject) => - this.#fn.pending_inbound_transactions_destroy.async( - transactions, - this.checkAsyncRes(resolve, reject, "pendingInboundTransactionsDestroy") - ) - ); - } - - static commsConfigCreate( - public_address, - transport, - database_name, - datastore_path, - discovery_timeout_in_secs, - saf_message_duration_in_secs, - network - ) { - return new Promise((resolve, reject) => - this.#fn.comms_config_create.async( - public_address, - transport, - database_name, - datastore_path, - discovery_timeout_in_secs, - saf_message_duration_in_secs, - network, - this.error, - this.checkAsyncRes(resolve, reject, "commsConfigCreate") - ) - ); - } - - static commsConfigDestroy(wc) { - return new Promise((resolve, reject) => - this.#fn.comms_config_destroy.async( - wc, - this.checkAsyncRes(resolve, reject, "commsConfigDestroy") - ) - ); - } - - static walletCreate( - config, - log_path, - num_rolling_log_files, - size_per_log_file_bytes, - passphrase, - seed_words, - callback_received_transaction, - callback_received_transaction_reply, - callback_received_finalized_transaction, - callback_transaction_broadcast, - callback_transaction_mined, - callback_transaction_mined_unconfirmed, - callback_direct_send_result, - callback_store_and_forward_send_result, - callback_transaction_cancellation, - callback_txo_validation_complete, - callback_transaction_validation_complete, - callback_saf_message_received - ) { - return new Promise((resolve, reject) => - this.#fn.wallet_create.async( - config, - log_path, - num_rolling_log_files, - size_per_log_file_bytes, - passphrase, - seed_words, - callback_received_transaction, - callback_received_transaction_reply, - callback_received_finalized_transaction, - callback_transaction_broadcast, - callback_transaction_mined, - callback_transaction_mined_unconfirmed, - callback_direct_send_result, - callback_store_and_forward_send_result, - callback_transaction_cancellation, - callback_txo_validation_complete, - callback_transaction_validation_complete, - callback_saf_message_received, - this.recovery_in_progress, - this.error, - this.checkAsyncRes(resolve, reject, "walletCreate") - ) - ); - } - - static walletSignMessage(wallet, msg) { - return new Promise((resolve, reject) => - this.#fn.wallet_sign_message.async( - wallet, - msg, - this.error, - this.checkAsyncRes(resolve, reject, "walletSignMessage") - ) - ); - } - - static walletVerifyMessageSignature(wallet, public_key, hex_sig_nonce, msg) { - return new Promise((resolve, reject) => - this.#fn.wallet_verify_message_signature.async( - wallet, - public_key, - hex_sig_nonce, - msg, - this.error, - this.checkAsyncRes(resolve, reject, "walletVerifyMessageSignature") - ) - ); - } - - static walletAddBaseNodePeer(wallet, public_key, address) { - return new Promise((resolve, reject) => - this.#fn.wallet_add_base_node_peer.async( - wallet, - public_key, - address, - this.error, - this.checkAsyncRes(resolve, reject, "walletAddBaseNodePeer") - ) - ); - } - - static walletUpsertContact(wallet, contact) { - return new Promise((resolve, reject) => - this.#fn.wallet_upsert_contact.async( - wallet, - contact, - this.error, - this.checkAsyncRes(resolve, reject, "walletUpsertContact") - ) - ); - } - - static walletRemoveContact(wallet, contact) { - return new Promise((resolve, reject) => - this.#fn.wallet_remove_contact.async( - wallet, - contact, - this.error, - this.checkAsyncRes(resolve, reject, "walletRemoveContact") - ) - ); - } - - static walletGetAvailableBalance(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_available_balance.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetAvailableBalance") - ) - ); - } - - static walletGetPendingIncomingBalance(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_incoming_balance.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetPendingIncomingBalance") - ) - ); - } - - static walletGetPendingOutgoingBalance(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_outgoing_balance.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetPendingOutgoingBalance") - ) - ); - } - - static walletGetFeeEstimate( - wallet, - amount, - fee_per_gram, - num_kernels, - num_outputs - ) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_fee_estimate.async( - wallet, - amount, - fee_per_gram, - num_kernels, - num_outputs, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetFeeEstimate") - ) - ); - } - - static walletGetNumConfirmationsRequired(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_num_confirmations_required.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetNumConfirmationsRequired") - ) - ); - } - - static walletSetNumConfirmationsRequired(wallet, num) { - return new Promise((resolve, reject) => - this.#fn.wallet_set_num_confirmations_required.async( - wallet, - num, - this.error, - this.checkAsyncRes(resolve, reject, "walletSetNumConfirmationsRequired") - ) - ); - } - - static walletSendTransaction( - wallet, - destination, - amount, - fee_per_gram, - message - ) { - return new Promise((resolve, reject) => - this.#fn.wallet_send_transaction.async( - wallet, - destination, - amount, - fee_per_gram, - message, - this.error, - this.checkAsyncRes(resolve, reject, "walletSendTransaction") - ) - ); - } - - static walletGetContacts(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_contacts.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetContacts") - ) - ); - } - - static walletGetCompletedTransactions(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_completed_transactions.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetCompletedTransactions") - ) - ); - } - - static walletGetPendingOutboundTransactions(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_outbound_transactions.async( - wallet, - this.error, - this.checkAsyncRes( - resolve, - reject, - "walletGetPendingOutboundTransactions" - ) - ) - ); - } - - static walletGetPublicKey(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_public_key.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetPublicKey") - ) - ); - } - - static walletGetPendingInboundTransactions(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_inbound_transactions.async( - wallet, - this.error, - this.checkAsyncRes( - resolve, - reject, - "walletGetPendingInboundTransactions" - ) - ) - ); - } - - static walletGetCancelledTransactions(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_cancelled_transactions.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetCancelledTransactions") - ) - ); - } - - static walletGetCompletedTransactionById(wallet, transaction_id) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_completed_transaction_by_id.async( - wallet, - transaction_id, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetCompletedTransactionById") - ) - ); - } - - static walletGetPendingOutboundTransactionById(wallet, transaction_id) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_outbound_transaction_by_id.async( - wallet, - transaction_id, - this.error, - this.checkAsyncRes( - resolve, - reject, - "walletGetPendingOutboundTransactionById" - ) - ) - ); - } - - static walletGetPendingInboundTransactionById(wallet, transaction_id) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_pending_inbound_transaction_by_id.async( - wallet, - transaction_id, - this.error, - this.checkAsyncRes( - resolve, - reject, - "walletGetPendingInboundTransactionById" - ) - ) - ); - } - - static walletGetCancelledTransactionById(wallet, transaction_id) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_cancelled_transaction_by_id.async( - wallet, - transaction_id, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetCancelledTransactionById") - ) - ); - } - - static walletImportUtxo( - wallet, - amount, - spending_key, - source_public_key, - message - ) { - return new Promise((resolve, reject) => - this.#fn.wallet_import_utxo.async( - wallet, - amount, - spending_key, - source_public_key, - message, - this.error, - this.checkAsyncRes(resolve, reject, "walletImportUtxo") - ) - ); - } - - static walletStartTxoValidation(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_start_txo_validation.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletStartTxoValidation") - ) - ); - } - - static walletStartTransactionValidation(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_start_transaction_validation.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletStartTransactionValidation") - ) - ); - } - - static walletRestartTransactionBroadcast(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_restart_transaction_broadcast.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletRestartTransactionBroadcast") - ) - ); - } - - static walletSetLowPowerMode(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_set_low_power_mode.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletSetLowPowerMode") - ) - ); - } - - static walletSetNormalPowerMode(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_set_normal_power_mode.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletSetNormalPowerMode") - ) - ); - } - - static walletCancelPendingTransaction(wallet, transaction_id) { - return new Promise((resolve, reject) => - this.#fn.wallet_cancel_pending_transaction.async( - wallet, - transaction_id, - this.error, - this.checkAsyncRes(resolve, reject, "walletCancelPendingTransaction") - ) - ); - } - - static walletCoinSplit(wallet, amount, count, fee, msg, lock_height) { - return new Promise((resolve, reject) => - this.#fn.wallet_coin_split.async( - wallet, - amount, - count, - fee, - msg, - lock_height, - this.error, - this.checkAsyncRes(resolve, reject, "walletCoinSplit") - ) - ); - } - - static walletGetSeedWords(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_seed_words.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetSeedWords") - ) - ); - } - - static walletApplyEncryption(wallet, passphrase) { - return new Promise((resolve, reject) => - this.#fn.wallet_apply_encryption.async( - wallet, - passphrase, - this.error, - this.checkAsyncRes(resolve, reject, "walletApplyEncryption") - ) - ); - } - - static walletRemoveEncryption(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_remove_encryption.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletRemoveEncryption") - ) - ); - } - - static walletSetKeyValue(wallet, key, value) { - return new Promise((resolve, reject) => - this.#fn.wallet_set_key_value.async( - wallet, - key, - value, - this.error, - this.checkAsyncRes(resolve, reject, "walletSetKeyValue") - ) - ); - } - - static walletGetValue(wallet, key) { - return new Promise((resolve, reject) => - this.#fn.wallet_get_value.async( - wallet, - key, - this.error, - this.checkAsyncRes(resolve, reject, "walletGetValue") - ) - ); - } - - static walletClearValue(wallet, key) { - return new Promise((resolve, reject) => - this.#fn.wallet_clear_value.async( - wallet, - key, - this.error, - this.checkAsyncRes(resolve, reject, "walletClearValue") - ) - ); - } - - static walletIsRecoveryInProgress(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_is_recovery_in_progress.async( - wallet, - this.error, - this.checkAsyncRes(resolve, reject, "walletIsRecoveryInProgress") - ) - ); - } - - static walletStartRecovery( - wallet, - base_node_public_key, - recovery_progress_callback - ) { - return new Promise((resolve, reject) => - this.#fn.wallet_start_recovery.async( - wallet, - base_node_public_key, - recovery_progress_callback, - this.error, - this.checkAsyncRes(resolve, reject, "walletStartRecovery") - ) - ); - } - - static walletDestroy(wallet) { - return new Promise((resolve, reject) => - this.#fn.wallet_destroy.async( - wallet, - this.checkAsyncRes(resolve, reject, "walletDestroy") - ) - ); - } - - static filePartialBackup(original_file_path, backup_file_path) { - return new Promise((resolve, reject) => - this.#fn.file_partial_backup.async( - original_file_path, - backup_file_path, - this.error, - this.checkAsyncRes(resolve, reject, "filePartialBackup") - ) - ); - } - - static logDebugMessage(msg) { - return new Promise((resolve, reject) => - this.#fn.log_debug_message.async( - msg, - this.checkAsyncRes(resolve, reject, "logDebugMessage") - ) - ); - } - - static getEmojiSet() { - return new Promise((resolve, reject) => - this.#fn.get_emoji_set.async( - this.checkAsyncRes(resolve, reject, "getEmojiSet") - ) - ); - } - - static emojiSetDestroy(emoji_set) { - return new Promise((resolve, reject) => - this.#fn.emoji_set_destroy.async( - emoji_set, - this.checkAsyncRes(resolve, reject, "emojiSetDestroy") - ) - ); - } - - static emojiSetGetAt(emoji_set, position) { - return new Promise((resolve, reject) => - this.#fn.emoji_set_get_at.async( - emoji_set, - position, - this.error, - this.checkAsyncRes(resolve, reject, "emojiSetGetAt") - ) - ); - } - - static emojiSetGetLength(emoji_set) { - return new Promise((resolve, reject) => - this.#fn.emoji_set_get_length.async( - emoji_set, - this.error, - this.checkAsyncRes(resolve, reject, "emojiSetGetLength") - ) - ); - } -} -module.exports = WalletFFI; diff --git a/integration_tests/helpers/walletFFIClient.js b/integration_tests/helpers/walletFFIClient.js index 08d509fb9c..68441f390d 100644 --- a/integration_tests/helpers/walletFFIClient.js +++ b/integration_tests/helpers/walletFFIClient.js @@ -4,6 +4,7 @@ const CommsConfig = require("./ffi/commsConfig"); const Wallet = require("./ffi/wallet"); const { getFreePort } = require("./util"); const dateFormat = require("dateformat"); +const { sleep } = require("./util"); class WalletFFIClient { name; @@ -125,7 +126,9 @@ class WalletFFIClient { return this.wallet.getCounters(); } resetCounters() { - this.wallet.clearCallbackCounters(); + if (this.wallet) { + this.wallet.clearCallbackCounters(); + } } sendTransaction(destination, amount, fee_per_gram, message) { @@ -168,18 +171,32 @@ class WalletFFIClient { return this.wallet.cancelPendingTransaction(tx_id); } - stop() { - if (this.wallet) { - this.wallet.destroy(); - } + async stop() { if (this.comms_config) { - this.comms_config.destroy(); + // console.log("walletFFI destroy comms_config ..."); + await this.comms_config.destroy(); + this.comms_config = undefined; + // console.log("walletFFI destroy comms_config ... done!"); + await sleep(100); } if (this.transport) { - this.transport.destroy(); + // console.log("walletFFI destroy transport ..."); + await this.transport.destroy(); + this.transport = undefined; + // console.log("walletFFI destroy transport ... done!"); + await sleep(100); } if (this.seed_words) { - this.seed_words.destroy(); + // console.log("walletFFI destroy seed_words ..."); + await this.seed_words.destroy(); + this.seed_words = undefined; + // console.log("walletFFI destroy seed_words ... done!"); + } + if (this.wallet) { + // console.log("walletFFI destroy wallet ..."); + await this.wallet.destroy(); + this.wallet = undefined; + // console.log("walletFFI destroy wallet ... done!"); } } } diff --git a/integration_tests/package-lock.json b/integration_tests/package-lock.json index 1bc348f376..54d92efa38 100644 --- a/integration_tests/package-lock.json +++ b/integration_tests/package-lock.json @@ -227,16 +227,6 @@ "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", "dev": true }, - "@babel/runtime-corejs3": { - "version": "7.15.4", - "resolved": false, - "integrity": "sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg==", - "dev": true, - "requires": { - "core-js-pure": "^3.16.0", - "regenerator-runtime": "^0.13.4" - } - }, "@babel/template": { "version": "7.15.4", "resolved": false, @@ -275,6 +265,134 @@ "to-fast-properties": "^2.0.0" } }, + "@cucumber/create-meta": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/create-meta/-/create-meta-5.0.0.tgz", + "integrity": "sha512-Z5kMZkUff00S3/KSnKzB/KOm2UIxMXY1xXmj2dQMlD49lV6v/W8EEvgDMNtQotQNSOQU5bDupmWQpk+o16tXIw==", + "dev": true, + "requires": { + "@cucumber/messages": "^16.0.0" + } + }, + "@cucumber/cucumber": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-7.3.1.tgz", + "integrity": "sha512-x1+/AvouZy205ZvfYbeEVat5aBAj4EeLt9TZfD7pO9j+tQ3W6uxSuDB1TKfxAXFU3WYrswor0CXoJBYOIZhzMw==", + "dev": true, + "requires": { + "@cucumber/create-meta": "^5.0.0", + "@cucumber/cucumber-expressions": "^12.1.1", + "@cucumber/gherkin": "^19.0.3", + "@cucumber/gherkin-streams": "^2.0.2", + "@cucumber/html-formatter": "^15.0.2", + "@cucumber/messages": "^16.0.1", + "@cucumber/tag-expressions": "^3.0.1", + "assertion-error-formatter": "^3.0.0", + "bluebird": "^3.7.2", + "capital-case": "^1.0.4", + "cli-table3": "^0.6.0", + "colors": "^1.4.0", + "commander": "^7.0.0", + "create-require": "^1.1.1", + "duration": "^0.2.2", + "durations": "^3.4.2", + "figures": "^3.2.0", + "glob": "^7.1.6", + "indent-string": "^4.0.0", + "is-generator": "^1.0.3", + "is-stream": "^2.0.0", + "knuth-shuffle-seeded": "^1.0.6", + "lodash": "^4.17.21", + "mz": "^2.7.0", + "progress": "^2.0.3", + "resolve": "^1.19.0", + "resolve-pkg": "^2.0.0", + "stack-chain": "^2.0.0", + "stacktrace-js": "^2.0.2", + "string-argv": "^0.3.1", + "tmp": "^0.2.1", + "util-arity": "^1.1.0", + "verror": "^1.10.0" + } + }, + "@cucumber/cucumber-expressions": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-12.1.3.tgz", + "integrity": "sha512-LB8MAzE4F/t2KIgsDEz4gZH0xSI4aG0/LmYUPyISPPjUS1pI/yGWWyeX2WsiUQxpSs765WcNIq5Bggt7gGGO3Q==", + "dev": true, + "requires": { + "regexp-match-indices": "1.0.2" + } + }, + "@cucumber/gherkin": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-19.0.3.tgz", + "integrity": "sha512-gWdMm8mfRk3P+VugJWvNALaQV5QnT+5RkqWy3tO+4NsMSQZPo5p4V4vXwriQZ/sZR1Wni5TDRztuRsKLgZ3XHA==", + "dev": true, + "requires": { + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.1" + } + }, + "@cucumber/gherkin-streams": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-streams/-/gherkin-streams-2.0.2.tgz", + "integrity": "sha512-cKmXOBz4OwGlrHMBCc4qCC3KzLaqcEZ11nWWskIbv6jyfvlIRuM2OgEF6VLcNVewczifW1p6DrDj0OO+BeXocA==", + "dev": true, + "requires": { + "@cucumber/gherkin": "^19.0.1", + "@cucumber/message-streams": "^2.0.0", + "@cucumber/messages": "^16.0.0", + "commander": "7.2.0", + "source-map-support": "0.5.19" + } + }, + "@cucumber/html-formatter": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-15.0.2.tgz", + "integrity": "sha512-j+YGY4ytj78G/v1gZo53D+vuKXlTg/oxNwSCCGvRQo75+AqYDJSkm/vexXJQ5lY1rXAvlbZ9KI6jhg6LDs0YdQ==", + "dev": true, + "requires": { + "@cucumber/messages": "^16.0.1", + "commander": "7.2.0", + "source-map-support": "0.5.19" + } + }, + "@cucumber/message-streams": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-2.1.0.tgz", + "integrity": "sha512-Yh3mw3qv6QL9NI/ihkZF8V9MX2GbnR6oktv34kC3uAbrQy9d/b2SZ3HNjG3J9JQqpV4B7Om3SPElJYIeo66TrA==", + "dev": true, + "requires": { + "@cucumber/messages": "^16.0.1" + } + }, + "@cucumber/messages": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-16.0.1.tgz", + "integrity": "sha512-80JcaAfQragFqR1rMhRwiqWL9HcR6Z4LDD2mfF0Lxg/lFkCNvmWa9Jl10NUNfFXYD555NKPzP/8xFo55abw8TQ==", + "dev": true, + "requires": { + "@types/uuid": "8.3.0", + "class-transformer": "0.4.0", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } + } + }, + "@cucumber/tag-expressions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-3.0.1.tgz", + "integrity": "sha512-OGCXaJ1BQXmQ5b9pw+JYsBGumK2/LPZiLmbj1o1JFVeSNs2PY8WPQFSyXrskhrHz5Nd/6lYg7lvGMtFHOncC4w==", + "dev": true + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": false, @@ -310,12 +428,28 @@ } }, "@grpc/grpc-js": { - "version": "1.3.7", - "resolved": false, - "integrity": "sha512-CKQVuwuSPh40tgOkR7c0ZisxYRiN05PcKPW72mQL5y++qd7CwBRoaJZvU5xfXnCJDFBmS3qZGQ71Frx6Ofo2XA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.1.tgz", + "integrity": "sha512-/chkA48TdAvATHA7RXJPeHQLdfFhpu51974s8htjO/XTDHA41j5+SkR5Io+lr9XsLmkZD6HxLyRAFGmA9wjO2w==", "dev": true, "requires": { + "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" + }, + "dependencies": { + "@grpc/proto-loader": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.6.tgz", + "integrity": "sha512-cdMaPZ8AiFz6ua6PUbP+LKbhwJbFXnrQ/mlnKGUyzDUZ3wp7vPLksnmLCBX6SHgSmjX7CbNVNLFYD5GmmjO4GQ==", + "dev": true, + "requires": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^6.10.0", + "yargs": "^16.1.1" + } + } } }, "@grpc/proto-loader": { @@ -427,6 +561,12 @@ "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", + "dev": true + }, "acorn": { "version": "7.4.1", "resolved": false, @@ -457,6 +597,15 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, "ansi-regex": { "version": "5.0.1", "resolved": false, @@ -472,6 +621,12 @@ "color-convert": "^1.9.0" } }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", + "dev": true + }, "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -618,12 +773,6 @@ "resolved": false, "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, - "becke-ch--regex--s0-0-v1--base--pl--lib": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-Qpzuu/pffpNueNc/vcfacWKyDiA=", - "dev": true - }, "bindings": { "version": "1.5.0", "resolved": false, @@ -701,6 +850,12 @@ "resolved": false, "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "call-bind": { "version": "1.0.2", "resolved": false, @@ -723,6 +878,27 @@ "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, + "capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, "chai": { "version": "4.3.4", "resolved": false, @@ -754,15 +930,32 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, + "class-transformer": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", + "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==", + "dev": true + }, "cli-table3": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", "dev": true, "requires": { "colors": "^1.1.2", "object-assign": "^4.1.0", - "string-width": "^2.1.1" + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "clone-deep": { @@ -797,9 +990,9 @@ "dev": true }, "commander": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, "compress-commons": { @@ -827,12 +1020,6 @@ "safe-buffer": "~5.1.1" } }, - "core-js-pure": { - "version": "3.18.2", - "resolved": false, - "integrity": "sha512-4hMMLUlZhKJKOWbbGD1/VDUxGPEhEoN/T01k7bx271WiBKCvCfkgPzy0IeRS4PB50p6/N1q/SZL4B/TRsTE5bA==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": false, @@ -856,6 +1043,12 @@ "readable-stream": "^3.4.0" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": false, @@ -876,56 +1069,14 @@ } }, "cucumber": { - "version": "6.0.5", - "resolved": false, - "integrity": "sha512-x+W9Fwk6TvcapQsYMxwFU5AsQJDOIJVGrPKmH15OC7jzb9/Dk7Hb0ZAyw4WcpaDcUDRc8bi2k2yJejDp5eTRlg==", + "version": "7.0.0-rc.0", + "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-7.0.0-rc.0.tgz", + "integrity": "sha512-I8qgAStiDj7l7k14QtBFZrt6B22epU55TdL8L4R52+NF+nx4bvixPCia8pxzQkuEKmLU6r6dAwMPru+laSOiLg==", "dev": true, "requires": { - "assertion-error-formatter": "^3.0.0", - "bluebird": "^3.4.1", - "cli-table3": "^0.5.1", - "colors": "^1.1.2", - "commander": "^3.0.1", - "cucumber-expressions": "^8.1.0", - "cucumber-tag-expressions": "^2.0.2", - "duration": "^0.2.1", - "escape-string-regexp": "^2.0.0", - "figures": "^3.0.0", - "gherkin": "5.0.0", - "glob": "^7.1.3", - "indent-string": "^4.0.0", - "is-generator": "^1.0.2", - "is-stream": "^2.0.0", - "knuth-shuffle-seeded": "^1.0.6", - "lodash": "^4.17.14", - "mz": "^2.4.0", - "progress": "^2.0.0", - "resolve": "^1.3.3", - "serialize-error": "^4.1.0", - "stack-chain": "^2.0.0", - "stacktrace-js": "^2.0.0", - "string-argv": "^0.3.0", - "title-case": "^2.1.1", - "util-arity": "^1.0.2", - "verror": "^1.9.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "cucumber-expressions": { - "version": "8.3.0", - "resolved": false, - "integrity": "sha512-cP2ya0EiorwXBC7Ll7Cj7NELYbasNv9Ty42L4u7sso9KruWemWG1ZiTq4PMqir3SNDSrbykoqI5wZgMbLEDjLQ==", - "dev": true, - "requires": { - "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.4.0", - "xregexp": "^4.2.4" + "@cucumber/cucumber": "^7.0.0-rc.0", + "marked": "^1.1.1", + "marked-terminal": "^4.1.0" } }, "cucumber-html-reporter": { @@ -946,22 +1097,60 @@ } }, "cucumber-pretty": { - "version": "6.0.0", - "resolved": false, - "integrity": "sha512-ddx/VInPVKFB7N86QujgLivihJhuzexKwExMuFaUjSlEs5zVVqBgaf55f88h97VafXTWX+ZAcxTUwMBS4mYj/g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/cucumber-pretty/-/cucumber-pretty-1.5.2.tgz", + "integrity": "sha512-mZLqD+L2Q5DrfbUvAVBsYRFjU+W8dWXWCb6MniJq9XCeozvIsSR3oC1hGX5YLe3seXpJf/blVSVwAREL5HWW0w==", "dev": true, "requires": { "cli-table3": "^0.5.1", - "colors": "^1.4.0", + "colors": "^1.3.3", "figures": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } } }, - "cucumber-tag-expressions": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha512-+x5j1IfZrBtbvYHuoUX0rl4nUGxaey6Do9sM0CABmZfDCcWXuuRm1fQeCaklIYQgOFHQ6xOHvDSdkMHHpni6tQ==", - "dev": true - }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -1051,6 +1240,12 @@ "es5-ext": "~0.10.46" } }, + "durations": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/durations/-/durations-3.4.2.tgz", + "integrity": "sha512-V/lf7y33dGaypZZetVI1eu7BmvkbC4dItq12OElLRpKuaU5JxQstV2zHwLv8P7cNbQ+KL1WD80zMCTx5dNC4dg==", + "dev": true + }, "electron-to-chromium": { "version": "1.3.864", "resolved": false, @@ -1080,15 +1275,6 @@ "ansi-colors": "^4.1.1" } }, - "error-ex": { - "version": "1.3.2", - "resolved": false, - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "error-stack-parser": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", @@ -1353,12 +1539,13 @@ } }, "eslint-module-utils": { - "version": "2.6.2", - "resolved": false, - "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz", + "integrity": "sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==", "dev": true, "requires": { "debug": "^3.2.7", + "find-up": "^2.1.0", "pkg-dir": "^2.0.0" }, "dependencies": { @@ -1374,24 +1561,22 @@ } }, "eslint-plugin-import": { - "version": "2.24.2", - "resolved": false, - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", + "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", "dev": true, "requires": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", + "eslint-module-utils": "^2.7.0", "has": "^1.0.3", - "is-core-module": "^2.6.0", + "is-core-module": "^2.7.0", + "is-glob": "^4.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", + "object.values": "^1.1.5", "resolve": "^1.20.0", "tsconfig-paths": "^3.11.0" }, @@ -1757,6 +1942,12 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-func-name": { "version": "2.0.0", "resolved": false, @@ -1799,12 +1990,6 @@ "get-symbol-from-current-process-h": "^1.0.1" } }, - "gherkin": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha1-lt70EZjsOQgli1Ea909lWidk0qE=", - "dev": true - }, "glob": { "version": "7.2.0", "resolved": false, @@ -1885,12 +2070,6 @@ "resolved": false, "integrity": "sha1-rRB4rIHVfXLeYjKxADvE9vsCh8A=" }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": false, - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, "husky": { "version": "6.0.0", "resolved": false, @@ -1960,12 +2139,6 @@ "side-channel": "^1.0.4" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": false, - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-bigint": { "version": "1.0.4", "resolved": false, @@ -2016,9 +2189,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-generator": { @@ -2158,12 +2331,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": false, @@ -2294,18 +2461,6 @@ "immediate": "~3.0.5" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, "locate-path": { "version": "2.0.0", "resolved": false, @@ -2378,10 +2533,13 @@ "dev": true }, "lower-case": { - "version": "1.1.4", - "resolved": false, - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } }, "lru-cache": { "version": "6.0.0", @@ -2392,6 +2550,77 @@ "yallist": "^4.0.0" } }, + "marked": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", + "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", + "dev": true + }, + "marked-terminal": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-4.2.0.tgz", + "integrity": "sha512-DQfNRV9svZf0Dm9Cf5x5xaVJ1+XjxQW6XjFJ5HFkVyK52SDpj5PCBzS5X5r2w9nHr3mlB0T5201UMLue9fmhUw==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.1", + "cardinal": "^2.1.1", + "chalk": "^4.1.0", + "cli-table3": "^0.6.0", + "node-emoji": "^1.10.0", + "supports-hyperlinks": "^2.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "minimatch": { "version": "3.0.4", "resolved": false, @@ -2435,12 +2664,13 @@ "dev": true }, "no-case": { - "version": "2.3.2", - "resolved": false, - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, "requires": { - "lower-case": "^1.1.1" + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, "node-addon-api": { @@ -2470,31 +2700,16 @@ "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": false, - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": false, - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "normalize-path": { "version": "3.0.0", "resolved": false, "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, + "nvm": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/nvm/-/nvm-0.0.4.tgz", + "integrity": "sha1-OKF46dMbKDUIyS0VydqGHRqSELw=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2614,16 +2829,6 @@ "callsites": "^3.0.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-exists": { "version": "3.0.0", "resolved": false, @@ -2647,15 +2852,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-type": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, "pathval": { "version": "1.1.1", "resolved": false, @@ -2668,12 +2864,6 @@ "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, "pkg-dir": { "version": "2.0.0", "resolved": false, @@ -2683,15 +2873,6 @@ "find-up": "^2.1.0" } }, - "pkg-up": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, "prelude-ls": { "version": "1.2.1", "resolved": false, @@ -2756,27 +2937,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "read-pkg": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, "readable-stream": { "version": "3.6.0", "resolved": false, @@ -2795,6 +2955,15 @@ "minimatch": "^3.0.4" } }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, "ref-napi": { "version": "3.0.3", "resolved": false, @@ -2827,10 +2996,25 @@ } } }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": false, - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regexp-match-indices": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", + "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", + "dev": true, + "requires": { + "regexp-tree": "^0.1.11" + } + }, + "regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", "dev": true }, "regexpp": { @@ -2845,6 +3029,12 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, "require-from-string": { "version": "2.0.2", "resolved": false, @@ -2867,6 +3057,23 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg/-/resolve-pkg-2.0.0.tgz", + "integrity": "sha512-+1lzwXehGCXSeryaISr6WujZzowloigEofRB+dj75y9RRa/obVcYgbHJd53tdYw8pvZj8GojXaaENws8Ktw/hQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "rimraf": { "version": "3.0.2", "resolved": false, @@ -2893,15 +3100,6 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, - "serialize-error": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-5j9GgyGsP9vV9Uj1S0lDCvlsd+gc2LEPVK7HHHte7IyPwOD4lVQFeaX143gx3U5AnoCi+wbcb3mvaxVysjpxEw==", - "dev": true, - "requires": { - "type-fest": "^0.3.0" - } - }, "set-immediate-shim": { "version": "1.0.1", "resolved": false, @@ -2983,12 +3181,6 @@ "resolved": false, "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true } } }, @@ -2998,38 +3190,24 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "spdx-correct": { - "version": "3.1.1", - "resolved": false, - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": false, - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "spdx-license-ids": { - "version": "3.0.10", - "resolved": false, - "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": false, @@ -3093,30 +3271,14 @@ "dev": true }, "string-width": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "string.prototype.trimend": { @@ -3184,6 +3346,33 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "synchronized-promise": { "version": "0.3.1", "resolved": false, @@ -3218,28 +3407,11 @@ "uri-js": "^4.2.2" } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "json-schema-traverse": { "version": "1.0.0", "resolved": false, "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": false, - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } } } }, @@ -3284,14 +3456,13 @@ "thenify": ">= 3.1.0 < 4" } }, - "title-case": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.0.3" + "rimraf": "^3.0.0" } }, "to-fast-properties": { @@ -3329,6 +3500,12 @@ } } }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -3351,9 +3528,9 @@ "dev": true }, "type-fest": { - "version": "0.3.1", - "resolved": false, - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, "unbox-primitive": { @@ -3374,11 +3551,14 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, - "upper-case": { - "version": "1.1.3", - "resolved": false, - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } }, "uri-js": { "version": "4.4.1", @@ -3417,16 +3597,6 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -3556,19 +3726,53 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "xregexp": { - "version": "4.4.1", - "resolved": false, - "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==", - "dev": true, - "requires": { - "@babel/runtime-corejs3": "^7.12.1" - } + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true }, "yallist": { "version": "4.0.0", @@ -3576,6 +3780,27 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, "zip-stream": { "version": "4.1.0", "resolved": false, diff --git a/integration_tests/package.json b/integration_tests/package.json index 89cb1f8f08..8dc86e6d29 100644 --- a/integration_tests/package.json +++ b/integration_tests/package.json @@ -17,17 +17,17 @@ "@babel/core": "^7.15.8", "@babel/eslint-parser": "^7.15.8", "@babel/eslint-plugin": "^7.14.5", - "@grpc/grpc-js": "^1.3.6", + "@grpc/grpc-js": "^1.4.1", "@grpc/proto-loader": "^0.5.5", "blakejs": "^1.1.0", "chai": "^4.2.0", - "cucumber": "^6.0.5", + "cucumber": "^7.0.0-rc.0", "cucumber-html-reporter": "^5.5.0", - "cucumber-pretty": "^6.0.0", + "cucumber-pretty": "^1.5.2", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-config-standard": "^16.0.2", - "eslint-plugin-import": "^2.24.2", + "eslint-plugin-import": "^2.25.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.4.1", "eslint-plugin-promise": "^4.3.1", @@ -46,6 +46,7 @@ "glob": "^7.2.0", "hex64": "^0.4.0", "jszip": "^3.7.1", + "nvm": "^0.0.4", "sha3": "^2.1.3", "synchronized-promise": "^0.3.1", "tari_crypto": "^0.9.1",