diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index e09e019ef1..1c400c6221 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -315,7 +315,9 @@ async fn set_base_node_peer( ) -> Result<(CommsPublicKey, Multiaddr), CommandError> { println!("Setting base node peer..."); println!("{}::{}", public_key, address); - wallet.set_base_node_peer(public_key.clone(), address.clone()).await?; + wallet + .set_base_node_peer(public_key.clone(), Some(address.clone())) + .await?; Ok((public_key, address)) } diff --git a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs index b884e7beed..e8166196b2 100644 --- a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -250,7 +250,7 @@ impl wallet_server::Wallet for WalletGrpcServer { println!("{}::{}", public_key, net_address); let mut wallet = self.wallet.clone(); wallet - .set_base_node_peer(public_key.clone(), net_address.clone()) + .set_base_node_peer(public_key.clone(), Some(net_address.clone())) .await .map_err(|e| Status::internal(format!("{:?}", e)))?; diff --git a/applications/minotari_console_wallet/src/init/mod.rs b/applications/minotari_console_wallet/src/init/mod.rs index d37acb7763..6d24fb8c28 100644 --- a/applications/minotari_console_wallet/src/init/mod.rs +++ b/applications/minotari_console_wallet/src/init/mod.rs @@ -571,7 +571,7 @@ pub async fn start_wallet( .ok_or_else(|| ExitError::new(ExitCode::ConfigError, "Configured base node has no address!"))?; wallet - .set_base_node_peer(base_node.public_key.clone(), net_address.address().clone()) + .set_base_node_peer(base_node.public_key.clone(), Some(net_address.address().clone())) .await .map_err(|e| { ExitError::new( diff --git a/applications/minotari_console_wallet/src/ui/state/app_state.rs b/applications/minotari_console_wallet/src/ui/state/app_state.rs index 2c1fd54b4a..25d05adae7 100644 --- a/applications/minotari_console_wallet/src/ui/state/app_state.rs +++ b/applications/minotari_console_wallet/src/ui/state/app_state.rs @@ -1033,7 +1033,7 @@ impl AppStateInner { self.wallet .set_base_node_peer( peer.public_key.clone(), - peer.addresses.best().ok_or(UiError::NoAddress)?.address().clone(), + Some(peer.addresses.best().ok_or(UiError::NoAddress)?.address().clone()), ) .await?; @@ -1058,7 +1058,7 @@ impl AppStateInner { self.wallet .set_base_node_peer( peer.public_key.clone(), - peer.addresses.best().ok_or(UiError::NoAddress)?.address().clone(), + Some(peer.addresses.best().ok_or(UiError::NoAddress)?.address().clone()), ) .await?; @@ -1096,7 +1096,7 @@ impl AppStateInner { self.wallet .set_base_node_peer( previous.public_key.clone(), - previous.addresses.best().ok_or(UiError::NoAddress)?.address().clone(), + Some(previous.addresses.best().ok_or(UiError::NoAddress)?.address().clone()), ) .await?; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 28e2c3457f..998392bc8d 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -369,10 +369,10 @@ where pub async fn set_base_node_peer( &mut self, public_key: CommsPublicKey, - address: Multiaddr, + address: Option, ) -> Result<(), WalletError> { info!( - "Wallet setting base node peer, public key: {}, net address: {}.", + "Wallet setting base node peer, public key: {}, net address: {:?}.", public_key, address ); @@ -387,16 +387,19 @@ where let mut connectivity = self.comms.connectivity(); if let Some(mut current_peer) = peer_manager.find_by_public_key(&public_key).await? { // Only invalidate the identity signature if addresses are different - if current_peer.addresses.contains(&address) { - info!( - target: LOG_TARGET, - "Address for base node differs from storage. Was {}, setting to {}", - current_peer.addresses, - address - ); - - current_peer.addresses.add_address(&address, &PeerAddressSource::Config); - peer_manager.add_peer(current_peer.clone()).await?; + if address.is_some() { + let add = address.unwrap(); + if !current_peer.addresses.contains(&add) { + info!( + target: LOG_TARGET, + "Address for base node differs from storage. Was {}, setting to {}", + current_peer.addresses, + add + ); + + current_peer.addresses.add_address(&add, &PeerAddressSource::Config); + peer_manager.add_peer(current_peer.clone()).await?; + } } connectivity .add_peer_to_allow_list(current_peer.node_id.clone()) @@ -404,10 +407,21 @@ where self.wallet_connectivity.set_base_node(current_peer); } else { let node_id = NodeId::from_key(&public_key); + if address.is_none() { + debug!( + target: LOG_TARGET, + "Trying to add new peer without an address", + ); + return Err(WalletError::ArgumentError { + argument: "set_base_node_peer, address".to_string(), + value: "{Missing}".to_string(), + message: "New peers need the address filled in".to_string(), + }); + } let peer = Peer::new( public_key, node_id, - MultiaddressesWithStats::from_addresses_with_source(vec![address], &PeerAddressSource::Config), + MultiaddressesWithStats::from_addresses_with_source(vec![address.unwrap()], &PeerAddressSource::Config), PeerFlags::empty(), PeerFeatures::COMMUNICATION_NODE, Default::default(), diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index b793a18c90..47b739f98c 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -123,7 +123,7 @@ use tari_common_types::{ }; use tari_comms::{ multiaddr::Multiaddr, - peer_manager::NodeIdentity, + peer_manager::{NodeIdentity, PeerQuery}, transports::MemoryTransport, types::CommsPublicKey, }; @@ -5259,6 +5259,8 @@ pub unsafe extern "C" fn wallet_create( passphrase: *const c_char, seed_words: *const TariSeedWords, network_str: *const c_char, + peer_seed_str: *const c_char, + dns_sec: bool, callback_received_transaction: unsafe extern "C" fn(*mut TariPendingInboundTransaction), callback_received_transaction_reply: unsafe extern "C" fn(*mut TariCompletedTransaction), @@ -5321,24 +5323,16 @@ pub unsafe extern "C" fn wallet_create( SafePassword::from(pf) }; - let network = if network_str.is_null() { - error = LibWalletError::from(InterfaceError::NullError("network".to_string())).code; + let peer_seed = if peer_seed_str.is_null() { + error = LibWalletError::from(InterfaceError::NullError("peer seed dns".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); return ptr::null_mut(); } else { - let network = CStr::from_ptr(network_str) + let peer_seed = CStr::from_ptr(peer_seed_str) .to_str() - .expect("A non-null network should be able to be converted to string"); - info!(target: LOG_TARGET, "network set to {}", network); - - match Network::from_str(network) { - Ok(n) => n, - Err(_) => { - error = LibWalletError::from(InterfaceError::InvalidArgument("network".to_string())).code; - ptr::swap(error_out, &mut error as *mut c_int); - return ptr::null_mut(); - }, - } + .expect("A non-null peer seed should be able to be converted to string"); + info!(target: LOG_TARGET, "peer seed dns {}", peer_seed); + peer_seed }; let recovery_seed = if seed_words.is_null() { @@ -5355,6 +5349,26 @@ pub unsafe extern "C" fn wallet_create( } }; + let network = if network_str.is_null() { + error = LibWalletError::from(InterfaceError::NullError("network".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + let network = CStr::from_ptr(network_str) + .to_str() + .expect("A non-null network should be able to be converted to string"); + info!(target: LOG_TARGET, "network set to {}", network); + + match Network::from_str(network) { + Ok(n) => n, + Err(_) => { + error = LibWalletError::from(InterfaceError::InvalidArgument("network".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + }, + } + }; + let runtime = match Runtime::new() { Ok(r) => r, Err(e) => { @@ -5472,7 +5486,8 @@ pub unsafe extern "C" fn wallet_create( let peer_seeds = PeerSeedsConfig { dns_seeds_name_server: DEFAULT_DNS_NAME_SERVER.parse().unwrap(), - dns_seeds_use_dnssec: true, + dns_seeds_use_dnssec: dns_sec, + dns_seeds: StringList::from(vec![peer_seed.to_string()]), ..Default::default() }; @@ -6329,23 +6344,18 @@ pub unsafe extern "C" fn wallet_set_base_node_peer( return false; } - let parsed_addr; - if address.is_null() { - error = LibWalletError::from(InterfaceError::NullError("address".to_string())).code; - ptr::swap(error_out, &mut error as *mut c_int); - return false; + let parsed_addr = if address.is_null() { + None } else { match CStr::from_ptr(address).to_str() { - Ok(v) => { - parsed_addr = match Multiaddr::from_str(v) { - Ok(v) => v, - Err(_) => { - error = LibWalletError::from(InterfaceError::InvalidArgument("address is invalid".to_string())) - .code; - ptr::swap(error_out, &mut error as *mut c_int); - return false; - }, - } + Ok(v) => match Multiaddr::from_str(v) { + Ok(v) => Some(v), + Err(_) => { + error = + LibWalletError::from(InterfaceError::InvalidArgument("address is invalid".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + }, }, _ => { error = LibWalletError::from(InterfaceError::PointerError("address".to_string())).code; @@ -6353,7 +6363,7 @@ pub unsafe extern "C" fn wallet_set_base_node_peer( return false; }, } - } + }; if let Err(e) = (*wallet) .runtime @@ -6365,6 +6375,45 @@ pub unsafe extern "C" fn wallet_set_base_node_peer( } true } +/// Gets all seed peers known by the wallet +/// +/// ## Arguments +/// `wallet` - The TariWallet 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 +/// `TariPublicKeys` - Returns a list of all known public keys +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn wallet_get_seed_peers(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariPublicKeys { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let peer_manager = (*wallet).wallet.comms.peer_manager(); + let query = PeerQuery::new().select_where(|p| p.is_seed()); + match (*wallet).runtime.block_on(async move { + let peers = peer_manager.perform_query(query).await?; + let mut public_keys = Vec::with_capacity(peers.len()); + for peer in peers { + public_keys.push(peer.public_key); + } + Result::<_, WalletError>::Ok(public_keys) + }) { + Ok(public_keys) => Box::into_raw(Box::new(TariPublicKeys(public_keys))), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} /// Upserts a TariContact to the TariWallet. If the contact does not exist it will be Inserted. If it does exist the /// Alias will be updated. @@ -9508,6 +9557,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("Hello from Alasca").unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -9517,6 +9567,8 @@ mod test { passphrase, ptr::null(), alice_network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -9561,6 +9613,8 @@ mod test { passphrase, ptr::null(), alice_network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -9664,7 +9718,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("dolphis dancing in the coastal waters").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -9674,6 +9728,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -9888,7 +9944,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("a cat outside in Istanbul").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let wallet = wallet_create( config, ptr::null(), @@ -9898,6 +9954,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -9952,6 +10010,7 @@ mod test { let log_path: *const c_char = CString::into_raw(CString::new(temp_dir.path().join("asdf").to_str().unwrap()).unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let recovered_wallet = wallet_create( config, log_path, @@ -9961,6 +10020,8 @@ mod test { passphrase, seed_words, network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10028,7 +10089,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("Satoshi Nakamoto").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -10038,6 +10099,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10177,7 +10240,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("J-bay open corona").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -10187,6 +10250,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10310,7 +10375,7 @@ mod test { let passphrase: *const c_char = CString::into_raw(CString::new("The master and margarita").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -10320,6 +10385,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10523,7 +10590,7 @@ mod test { ); let passphrase: *const c_char = CString::into_raw(CString::new("niao").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -10533,6 +10600,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10744,7 +10813,7 @@ mod test { ); let passphrase: *const c_char = CString::into_raw(CString::new("niao").unwrap()) as *const c_char; - + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet = wallet_create( alice_config, ptr::null(), @@ -10754,6 +10823,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -10996,6 +11067,7 @@ mod test { error_ptr, ); let passphrase: *const c_char = CString::into_raw(CString::new("niao").unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let wallet_ptr = wallet_create( config, ptr::null(), @@ -11005,6 +11077,8 @@ mod test { passphrase, ptr::null(), network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -11234,6 +11308,7 @@ mod test { error_ptr, ); let passphrase: *const c_char = CString::into_raw(CString::new("niao").unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let alice_wallet_ptr = wallet_create( alice_config, ptr::null(), @@ -11243,6 +11318,8 @@ mod test { passphrase, ptr::null(), alice_network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, @@ -11294,6 +11371,7 @@ mod test { error_ptr, ); let passphrase: *const c_char = CString::into_raw(CString::new("niao").unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; let bob_wallet_ptr = wallet_create( bob_config, ptr::null(), @@ -11303,6 +11381,8 @@ mod test { passphrase, ptr::null(), bob_network_str, + dns_string, + false, received_tx_callback, received_tx_reply_callback, received_tx_finalized_callback, diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index e8bbfaa5f3..e3ac32b65b 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -2707,6 +2707,8 @@ struct TariWallet *wallet_create(TariCommsConfig *config, const char *passphrase, const struct TariSeedWords *seed_words, const char *network_str, + const char *peer_seed_str, + bool dns_sec, void (*callback_received_transaction)(TariPendingInboundTransaction*), void (*callback_received_transaction_reply)(TariCompletedTransaction*), void (*callback_received_finalized_transaction)(TariCompletedTransaction*), @@ -2999,6 +3001,23 @@ bool wallet_set_base_node_peer(struct TariWallet *wallet, const char *address, int *error_out); +/** + * Gets all seed peers known by the wallet + * + * ## Arguments + * `wallet` - The TariWallet 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 + * `TariPublicKeys` - Returns a list of all known public keys + * + * # Safety + * None + */ +struct TariPublicKeys *wallet_get_seed_peers(struct TariWallet *wallet, + int *error_out); + /** * Upserts a TariContact to the TariWallet. If the contact does not exist it will be Inserted. If it does exist the * Alias will be updated. diff --git a/integration_tests/src/ffi/ffi_import.rs b/integration_tests/src/ffi/ffi_import.rs index 3a0d0c8171..01f7405477 100644 --- a/integration_tests/src/ffi/ffi_import.rs +++ b/integration_tests/src/ffi/ffi_import.rs @@ -386,6 +386,8 @@ extern "C" { passphrase: *const c_char, seed_words: *const TariSeedWords, network_str: *const c_char, + peer_seed_str: *const c_char, + dns_sec: bool, callback_received_transaction: unsafe extern "C" fn(*mut TariPendingInboundTransaction), callback_received_transaction_reply: unsafe extern "C" fn(*mut TariCompletedTransaction), callback_received_finalized_transaction: unsafe extern "C" fn(*mut TariCompletedTransaction), diff --git a/integration_tests/src/ffi/wallet.rs b/integration_tests/src/ffi/wallet.rs index 436761e2e9..5cf63cfc8f 100644 --- a/integration_tests/src/ffi/wallet.rs +++ b/integration_tests/src/ffi/wallet.rs @@ -178,6 +178,8 @@ impl Wallet { CString::new("kensentme").unwrap().into_raw(), seed_words_ptr, CString::new("localnet").unwrap().into_raw(), + CString::new("").unwrap().into_raw(), + false, callback_received_transaction, callback_received_transaction_reply, callback_received_finalized_transaction,