Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc improvements in node gui. #1869

Open
wants to merge 2 commits into
base: refactor/upgrade-iced-version
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion common/src/chain/config/regtest_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use super::{regtest::GenesisStakingSettings, ChainConfig};
use anyhow::{anyhow, ensure, Result};
use paste::paste;

#[derive(Args, Clone, Debug)]
#[derive(Args, Clone, Debug, Default)]
pub struct ChainConfigOptions {
/// Magic bytes.
#[clap(long)]
Expand Down
2 changes: 1 addition & 1 deletion dns-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async fn run(options: DnsServerRunOptions) -> anyhow::Result<Never> {

let data_dir = prepare_data_dir(
|| default_data_dir_for_chain(chain_type.name()),
&config.datadir,
config.datadir.as_ref(),
None,
)
.expect("Failed to prepare data directory");
Expand Down
2 changes: 1 addition & 1 deletion node-daemon/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

pub async fn run() -> anyhow::Result<()> {
let opts = node_lib::Options::from_args(std::env::args_os());
let setup_result = node_lib::setup(opts).await?;
let setup_result = node_lib::setup(opts.with_resolved_command()).await?;
match setup_result {
node_lib::NodeSetupResult::Node(node) => {
node.main().await;
Expand Down
1 change: 1 addition & 0 deletions node-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ wallet-cli-commands = { path = "../wallet/wallet-cli-commands"}
anyhow.workspace = true
chrono.workspace = true
futures.workspace = true
heck.workspace = true
iced = { workspace = true, features = ["canvas", "debug", "tokio", "lazy"] }
iced_aw = { workspace = true, features = ["cupertino"] }
iced_fonts = { workspace = true, features = ["bootstrap"] }
Expand Down
12 changes: 7 additions & 5 deletions node-gui/backend/src/chainstate_event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@

use std::sync::Arc;

use chainstate::ChainstateEvent;
use anyhow::Context as _;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

use chainstate::ChainstateEvent;
use utils::tap_log::TapLog;

use super::{backend_impl::Backend, messages::BackendEvent};
Expand All @@ -32,7 +34,7 @@ impl ChainstateEventHandler {
pub async fn new(
chainstate: chainstate::ChainstateHandle,
event_tx: UnboundedSender<BackendEvent>,
) -> Self {
) -> anyhow::Result<Self> {
let (chainstate_event_tx, chainstate_event_rx) = unbounded_channel();
chainstate
.call_mut(|this| {
Expand All @@ -45,14 +47,14 @@ impl ChainstateEventHandler {
));
})
.await
.expect("Failed to subscribe to chainstate");
.context("Error subscribing to chainstate events")?;

Self {
Ok(Self {
chainstate,
chainstate_event_rx,
event_tx,
chain_info_updated: false,
}
})
}

pub async fn run(&mut self) {
Expand Down
152 changes: 93 additions & 59 deletions node-gui/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,35 @@ mod p2p_event_handler;
mod wallet_events;

mod account_id;
pub use account_id::AccountId;

use std::fmt::Debug;
use std::sync::Arc;

use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

use chainstate::ChainInfo;
use common::{
address::{Address, AddressError},
chain::{ChainConfig, Destination},
primitives::{Amount, BlockHeight},
time_getter::TimeGetter,
};
use node_lib::{Command, RunOptions};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use logging::log;
use node_lib::OptionsWithResolvedCommand;

use crate::chainstate_event_handler::ChainstateEventHandler;
use crate::p2p_event_handler::P2pEventHandler;
use crate::{chainstate_event_handler::ChainstateEventHandler, p2p_event_handler::P2pEventHandler};

use self::error::BackendError;
use self::messages::{BackendEvent, BackendRequest};
use self::{
error::BackendError,
messages::{BackendEvent, BackendRequest},
};

pub use account_id::AccountId;

#[derive(Debug, Clone, Copy)]
pub enum InitNetwork {
Mainnet,
Testnet,
Regtest,
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -111,11 +115,16 @@ pub struct InitializedNode {
pub chain_info: ChainInfo,
}

#[derive(Debug)]
pub enum NodeInitializationOutcome {
BackendControls(BackendControls),
DataDirCleanedUp,
}

pub async fn node_initialize(
_time_getter: TimeGetter,
network: InitNetwork,
opts: node_lib::OptionsWithResolvedCommand,
mode: WalletMode,
) -> anyhow::Result<BackendControls> {
) -> anyhow::Result<NodeInitializationOutcome> {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var(
"RUST_LOG",
Expand All @@ -124,20 +133,14 @@ pub async fn node_initialize(
}

let opts = {
let mut opts = node_lib::Options::from_args(std::env::args_os());
let run_opts = {
// For the GUI, we configure different defaults, such as disabling RPC server binding
// and enabling logging to a file.
let mut run_opts =
opts.command.map_or(RunOptions::default(), |c| c.run_options().clone());
run_opts.rpc_enabled = Some(run_opts.rpc_enabled.unwrap_or(false));
run_opts.log_to_file = Some(run_opts.log_to_file.unwrap_or(true));
run_opts
};
opts.command = match network {
InitNetwork::Mainnet => Some(Command::Mainnet(run_opts)),
InitNetwork::Testnet => Some(Command::Testnet(run_opts)),
};
let mut opts = opts;
let run_opts = opts.command.run_options_mut();

// For the GUI, we configure different defaults, such as disabling RPC server binding
// and enabling logging to a file.
run_opts.rpc_enabled = Some(run_opts.rpc_enabled.unwrap_or(false));
opts.top_level.log_to_file = Some(opts.top_level.log_to_file.unwrap_or(true));

opts
};

Expand All @@ -152,8 +155,7 @@ pub async fn node_initialize(
let node = match setup_result {
node_lib::NodeSetupResult::Node(node) => node,
node_lib::NodeSetupResult::DataDirCleanedUp => {
// TODO: find more friendly way to report the message and shut down GUI
anyhow::bail!("Data directory is now clean. Please restart the node without `--clean-data` flag");
return Ok(NodeInitializationOutcome::DataDirCleanedUp);
}
};

Expand All @@ -163,9 +165,10 @@ pub async fn node_initialize(

// Subscribe to chainstate before getting the current chain_info!
let chainstate_event_handler =
ChainstateEventHandler::new(controller.chainstate.clone(), event_tx.clone()).await;
ChainstateEventHandler::new(controller.chainstate.clone(), event_tx.clone())
.await?;

let p2p_event_handler = P2pEventHandler::new(&controller.p2p, event_tx.clone()).await;
let p2p_event_handler = P2pEventHandler::new(&controller.p2p, event_tx.clone()).await?;

let chain_config =
controller.chainstate.call(|this| Arc::clone(this.get_chain_config())).await?;
Expand All @@ -192,35 +195,14 @@ pub async fn node_initialize(
});
(chain_config, chain_info)
}
WalletMode::Cold => {
let chain_config = Arc::new(match network {
InitNetwork::Mainnet => common::chain::config::create_mainnet(),
InitNetwork::Testnet => common::chain::config::create_testnet(),
});
let chain_info = ChainInfo {
best_block_id: chain_config.genesis_block_id(),
best_block_height: BlockHeight::zero(),
median_time: chain_config.genesis_block().timestamp(),
best_block_timestamp: chain_config.genesis_block().timestamp(),
is_initial_block_download: false,
};

let manager_join_handle = tokio::spawn(async move {});

let backend = backend_impl::Backend::new_cold(
chain_config.clone(),
event_tx,
low_priority_event_tx,
wallet_updated_tx,
manager_join_handle,
);

tokio::spawn(async move {
backend_impl::run_cold(backend, request_rx, wallet_updated_rx).await;
});

(chain_config, chain_info)
}
WalletMode::Cold => spawn_cold_backend(
opts,
event_tx,
request_rx,
low_priority_event_tx,
wallet_updated_tx,
wallet_updated_rx,
)?,
};

let initialized_node = InitializedNode {
Expand All @@ -235,5 +217,57 @@ pub async fn node_initialize(
low_priority_backend_receiver: low_priority_event_rx,
};

Ok(backend_controls)
Ok(NodeInitializationOutcome::BackendControls(backend_controls))
}

fn spawn_cold_backend(
options: OptionsWithResolvedCommand,
event_tx: UnboundedSender<BackendEvent>,
request_rx: UnboundedReceiver<BackendRequest>,
low_priority_event_tx: UnboundedSender<BackendEvent>,
wallet_updated_tx: UnboundedSender<messages::WalletId>,
wallet_updated_rx: UnboundedReceiver<messages::WalletId>,
) -> anyhow::Result<(Arc<ChainConfig>, ChainInfo)> {
logging::init_logging();

let chain_config = Arc::new(handle_options_in_cold_wallet_mode(options)?);
let chain_info = ChainInfo {
best_block_id: chain_config.genesis_block_id(),
best_block_height: BlockHeight::zero(),
median_time: chain_config.genesis_block().timestamp(),
best_block_timestamp: chain_config.genesis_block().timestamp(),
is_initial_block_download: false,
};

let manager_join_handle = tokio::spawn(async move {});

let backend = backend_impl::Backend::new_cold(
chain_config.clone(),
event_tx,
low_priority_event_tx,
wallet_updated_tx,
manager_join_handle,
);

tokio::spawn(async move {
backend_impl::run_cold(backend, request_rx, wallet_updated_rx).await;
});

Ok((chain_config, chain_info))
}

fn handle_options_in_cold_wallet_mode(
options: OptionsWithResolvedCommand,
) -> anyhow::Result<ChainConfig> {
if options.clean_data_option_set() {
log::warn!("Ignoring clean-data option in cold wallet mode");
}

if options.log_to_file_option_set() {
log::warn!("Log-to-file disabled in cold wallet mode");
}

// TODO: check all other options?

options.command.create_chain_config()
}
20 changes: 14 additions & 6 deletions node-gui/backend/src/p2p_event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@

use std::sync::Arc;

use anyhow::Context;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

use p2p::{interface::p2p_interface::P2pInterface, P2pEvent};
use subsystem::Handle;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use utils::tap_log::TapLog;

use super::{backend_impl::Backend, messages::BackendEvent};
Expand All @@ -31,10 +33,16 @@ impl P2pEventHandler {
pub async fn new(
p2p: &Handle<dyn P2pInterface>,
event_tx: UnboundedSender<BackendEvent>,
) -> Self {
) -> anyhow::Result<Self> {
// TODO: Fix race in p2p events subscribe (if some peers are connected before the subscription is complete)

// TODO: need a way to propagate subsystem initialization errors. E.g. if the p2p port is busy, we'll
// currently report "Error subscribing to P2P events: Callee subsystem did not respond", which is not
// very informative.
// Same for ChainstateEventHandler.

let (p2p_event_tx, p2p_event_rx) = unbounded_channel();
let error_context = "Error subscribing to P2P events";
p2p.call_mut(|this| {
this.subscribe_to_events(Arc::new(move |p2p_event: P2pEvent| {
_ = p2p_event_tx
Expand All @@ -43,13 +51,13 @@ impl P2pEventHandler {
}))
})
.await
.expect("Failed to subscribe to P2P event")
.expect("Failed to subscribe to P2P event");
.context(error_context)?
.context(error_context)?;

Self {
Ok(Self {
p2p_event_rx,
event_tx,
}
})
}

pub async fn run(&mut self) {
Expand Down
Loading
Loading