Skip to content

Commit

Permalink
feat(console-wallet): detect local base node and prompt (#4557)
Browse files Browse the repository at this point in the history
Description
---
Auto-detects base node grpc on console wallet startup and prompts to use it

Motivation and Context
---
Resolves #4414 

Behaviour:
- if custom base node is set in the config, dont prompt
- if non interactive mode, dont prompt
- if custom base node in the database is already set to detected base node, dont prompt

How Has This Been Tested?
---
Manually, no base node set, startup with and without base node grpc available
  • Loading branch information
sdbondi committed Aug 29, 2022
1 parent 11af787 commit 887df88
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 24 deletions.
76 changes: 56 additions & 20 deletions applications/tari_console_wallet/src/init/mod.rs
Expand Up @@ -26,7 +26,10 @@ use log::*;
use rpassword::prompt_password_stdout;
use rustyline::Editor;
use tari_app_utilities::identity_management::setup_node_identity;
use tari_common::exit_codes::{ExitCode, ExitError};
use tari_common::{
configuration::bootstrap::prompt,
exit_codes::{ExitCode, ExitError},
};
use tari_comms::{
multiaddr::Multiaddr,
peer_manager::{Peer, PeerFeatures},
Expand All @@ -38,7 +41,7 @@ use tari_crypto::keys::PublicKey;
use tari_key_manager::{cipher_seed::CipherSeed, mnemonic::MnemonicLanguage};
use tari_p2p::{peer_seeds::SeedPeer, TransportType};
use tari_shutdown::ShutdownSignal;
use tari_utilities::SafePassword;
use tari_utilities::{ByteArray, SafePassword};
use tari_wallet::{
error::{WalletError, WalletStorageError},
output_manager_service::storage::database::OutputManagerDatabase,
Expand All @@ -54,7 +57,7 @@ use tari_wallet::{

use crate::{
cli::Cli,
utils::db::get_custom_base_node_peer_from_db,
utils::db::{get_custom_base_node_peer_from_db, set_custom_base_node_peer_in_db},
wallet_modes::{PeerConfig, WalletMode},
ApplicationConfig,
};
Expand Down Expand Up @@ -141,27 +144,48 @@ pub async fn change_password(
}

/// Populates the PeerConfig struct from:
/// 1. The custom peer in the wallet if it exists
/// 2. The service peers defined in config they exist
/// 3. The peer seeds defined in config
/// 1. The custom peer in the wallet config if it exists
/// 2. The custom peer in the wallet db if it exists
/// 3. The detected local base node if any
/// 4. The service peers defined in config they exist
/// 5. The peer seeds defined in config
pub async fn get_base_node_peer_config(
config: &ApplicationConfig,
wallet: &mut WalletSqlite,
non_interactive_mode: bool,
) -> Result<PeerConfig, ExitError> {
// custom
let mut base_node_custom = get_custom_base_node_peer_from_db(wallet).await;
let mut selected_base_node = match config.wallet.custom_base_node {
Some(ref custom) => SeedPeer::from_str(custom)
.map(|node| Some(Peer::from(node)))
.map_err(|err| ExitError::new(ExitCode::ConfigError, &format!("Malformed custom base node: {}", err)))?,
None => get_custom_base_node_peer_from_db(wallet).await,
};

if let Some(custom) = config.wallet.custom_base_node.clone() {
match SeedPeer::from_str(&custom) {
Ok(node) => {
base_node_custom = Some(Peer::from(node));
},
Err(err) => {
return Err(ExitError::new(
ExitCode::ConfigError,
&format!("Malformed custom base node: {}", err),
));
},
// If the user has not explicitly set a base node in the config, we try detect one
if !non_interactive_mode && config.wallet.custom_base_node.is_none() {
if let Some(detected_node) = detect_local_base_node().await {
match selected_base_node {
Some(ref base_node) if base_node.public_key == detected_node.public_key => {
// Skip asking because it's already set
},
Some(_) | None => {
println!(
"Local Base Node detected with public key {} and address {}",
detected_node.public_key,
detected_node.addresses.first().unwrap()
);
if prompt(
"Would you like to use this base node? IF YOU DID NOT START THIS BASE NODE YOU SHOULD SELECT \
NO (Y/n)",
) {
let address = detected_node.addresses.first().ok_or_else(|| {
ExitError::new(ExitCode::ConfigError, "No address found for detected base node")
})?;
set_custom_base_node_peer_in_db(wallet, &detected_node.public_key, address).await?;
selected_base_node = Some(detected_node.into());
}
},
}
}
}

Expand All @@ -185,7 +209,7 @@ pub async fn get_base_node_peer_config(
.collect::<Result<Vec<_>, _>>()
.map_err(|err| ExitError::new(ExitCode::ConfigError, format!("Malformed seed peer: {}", err)))?;

let peer_config = PeerConfig::new(base_node_custom, base_node_peers, peer_seeds);
let peer_config = PeerConfig::new(selected_base_node, base_node_peers, peer_seeds);
debug!(target: LOG_TARGET, "base node peer config: {:?}", peer_config);

Ok(peer_config)
Expand Down Expand Up @@ -380,6 +404,18 @@ pub async fn init_wallet(
Ok(wallet)
}

async fn detect_local_base_node() -> Option<SeedPeer> {
use tari_app_grpc::tari_rpc::{base_node_client::BaseNodeClient, Empty};
const COMMON_BASE_NODE_GRPC_ADDRESS: &str = "http://127.0.0.1:18142";

let mut node_conn = BaseNodeClient::connect(COMMON_BASE_NODE_GRPC_ADDRESS).await.ok()?;
let resp = node_conn.identify(Empty {}).await.ok()?;
let identity = resp.get_ref();
let public_key = CommsPublicKey::from_bytes(&identity.public_key).ok()?;
let address = Multiaddr::from_str(&identity.public_address).ok()?;
Some(SeedPeer::new(public_key, vec![address]))
}

async fn setup_identity_from_db<D: WalletBackend + 'static>(
wallet_db: &WalletDatabase<D>,
master_seed: &CipherSeed,
Expand Down
6 changes: 5 additions & 1 deletion applications/tari_console_wallet/src/main.rs
Expand Up @@ -173,7 +173,11 @@ fn main_inner() -> Result<(), ExitError> {
}

// get base node/s
let base_node_config = runtime.block_on(get_base_node_peer_config(&config, &mut wallet))?;
let base_node_config = runtime.block_on(get_base_node_peer_config(
&config,
&mut wallet,
cli.non_interactive_mode,
))?;
let base_node_selected = base_node_config.get_base_node_peer()?;

let wallet_mode = wallet_mode(&cli, boot_mode);
Expand Down
25 changes: 24 additions & 1 deletion applications/tari_console_wallet/src/utils/db.rs
Expand Up @@ -25,9 +25,10 @@ use tari_common_types::types::PublicKey;
use tari_comms::{
multiaddr::Multiaddr,
peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags},
types::CommsPublicKey,
};
use tari_utilities::hex::Hex;
use tari_wallet::WalletSqlite;
use tari_wallet::{error::WalletStorageError, WalletSqlite};

pub const LOG_TARGET: &str = "wallet::utils::db";
pub const CUSTOM_BASE_NODE_PUBLIC_KEY_KEY: &str = "console_wallet_custom_base_node_public_key";
Expand Down Expand Up @@ -88,3 +89,25 @@ pub async fn get_custom_base_node_peer_from_db(wallet: &mut WalletSqlite) -> Opt
(_, _) => None,
}
}

/// Sets the base node peer in the database
pub async fn set_custom_base_node_peer_in_db(
wallet: &mut WalletSqlite,
base_node_public_key: &CommsPublicKey,
base_node_address: &Multiaddr,
) -> Result<(), WalletStorageError> {
wallet
.db
.set_client_key_value(
CUSTOM_BASE_NODE_PUBLIC_KEY_KEY.to_string(),
base_node_public_key.to_hex(),
)
.await?;

wallet
.db
.set_client_key_value(CUSTOM_BASE_NODE_ADDRESS_KEY.to_string(), base_node_address.to_string())
.await?;

Ok(())
}
4 changes: 4 additions & 0 deletions base_layer/p2p/src/peer_seeds.rs
Expand Up @@ -86,6 +86,10 @@ pub struct SeedPeer {
}

impl SeedPeer {
pub fn new(public_key: CommsPublicKey, addresses: Vec<Multiaddr>) -> Self {
Self { public_key, addresses }
}

#[inline]
pub fn derive_node_id(&self) -> NodeId {
NodeId::from_public_key(&self.public_key)
Expand Down
4 changes: 2 additions & 2 deletions base_layer/wallet/src/wallet.rs
Expand Up @@ -69,7 +69,7 @@ use tari_p2p::{
use tari_script::{script, ExecutionStack, TariScript};
use tari_service_framework::StackBuilder;
use tari_shutdown::ShutdownSignal;
use tari_utilities::SafePassword;
use tari_utilities::{ByteArray, SafePassword};

use crate::{
base_node_service::{handle::BaseNodeServiceHandle, BaseNodeServiceInitializer},
Expand Down Expand Up @@ -758,7 +758,7 @@ pub fn derive_comms_secret_key(master_seed: &CipherSeed) -> Result<CommsSecretKe
/// Persist the one-sided payment script for the current wallet NodeIdentity for use during scanning for One-sided
/// payment outputs. This is peristed so that if the Node Identity changes the wallet will still scan for outputs
/// using old node identities.
pub async fn persist_one_sided_payment_script_for_node_identity(
async fn persist_one_sided_payment_script_for_node_identity(
output_manager_service: &mut OutputManagerHandle,
node_identity: Arc<NodeIdentity>,
) -> Result<(), WalletError> {
Expand Down

0 comments on commit 887df88

Please sign in to comment.