Skip to content
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
51 changes: 51 additions & 0 deletions monero-wallet-ng/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ pub trait ProvidesTransactionStatus: Sync {
) -> impl Send + Future<Output = Result<TransactionStatus, TransactionStatusError>>;
}

#[derive(Debug, thiserror::Error)]
pub enum NodeInfoError {
#[error("Interface error: {0}")]
Interface(#[from] InterfaceError),
#[error("Failed to parse get_info response: {0}")]
Parse(String),
}

/// Sync status of a remote daemon, as reported by `get_info`.
#[derive(Debug, Clone)]
pub struct NodeSyncStatus {
/// Whether the node considers itself on the same height as the network.
pub synchronized: bool,
/// The height the node has synced to.
pub height: u64,
}

/// Provides the ability to query a remote daemon's sync status.
pub trait ProvidesNodeInfo: Sync {
fn node_sync_status(
&self,
) -> impl Send + Future<Output = Result<NodeSyncStatus, NodeInfoError>>;
}

/// Data structures we get back from the RPC server
///
/// See: https://github.com/monero-project/monero/blob/48ad374b0d6d6e045128729534dc2508e6999afe/src/rpc/core_rpc_server_commands_defs.h#L358-L439
Expand All @@ -54,6 +78,14 @@ mod monerod {
pub(crate) txs: Vec<TransactionInfo>,
}

// The daemon `get_info` endpoint.
// See: https://github.com/monero-project/monero/blob/48ad374b0d6d6e045128729534dc2508e6999afe/src/rpc/core_rpc_server_commands_defs.h#L633-L673
#[derive(Deserialize)]
pub(crate) struct GetInfoResponse {
pub(crate) synchronized: bool,
pub(crate) height: u64,
}

// See: https://github.com/SNeedlewoods/seraphis_wallet/blob/dbbccecc89e1121762a4ad6b531638ece82aa0c7/src/rpc/core_rpc_server_commands_defs.h#L406-L428
#[derive(Deserialize)]
pub(crate) struct TransactionInfo {
Expand Down Expand Up @@ -113,3 +145,22 @@ impl<T: HttpTransport> ProvidesTransactionStatus for MoneroDaemon<T> {
}
}
}

impl<T: HttpTransport> ProvidesNodeInfo for MoneroDaemon<T> {
fn node_sync_status(
&self,
) -> impl Send + Future<Output = Result<NodeSyncStatus, NodeInfoError>> {
async move {
// 16kb, fairly arbitrary, but get_info is small
let response = self.rpc_call("get_info", None, 16384).await?;

let info: monerod::GetInfoResponse = serde_json::from_str(&response)
.map_err(|e| NodeInfoError::Parse(e.to_string()))?;

Ok(NodeSyncStatus {
synchronized: info.synchronized,
height: info.height,
})
}
}
}
17 changes: 14 additions & 3 deletions monero-wallet/src/wallets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! - send money from one wallet to another.
pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener};

use anyhow::{Context, Result};
use anyhow::{Context, Result, bail};
use monero_address::Network;
use monero_daemon_rpc::MoneroDaemon;
use monero_oxide_wallet::transaction::{NotPruned, Transaction};
Expand Down Expand Up @@ -299,12 +299,23 @@ impl Wallets {
Ok(rpc_client)
}

/// Check that the daemon RPC is reachable, connecting if necessary.
/// Check that the daemon RPC is reachable and the node is fully synced.
pub async fn rpc_health_check(&self) -> Result<()> {
self.direct_rpc_block_height()
use monero_wallet_ng::rpc::ProvidesNodeInfo;

let rpc_client = self.rpc_client().await?;
let status = rpc_client
.node_sync_status()
.await
.context("Monero daemon RPC health check failed")?;

if !status.synchronized {
bail!(
"Monero node is not fully synced (synced to height {})",
status.height
);
}

Ok(())
}

Expand Down
Loading