mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-21 11:25:50 -05:00
feat(gui): Change Monero node while running (#502)
* feat(gui): Change daemon during runtime * feat(swap-controller): Add `monero-seed` RPC command * nitpicks * amend changelog
This commit is contained in:
parent
6861f38f16
commit
7e6138570f
11 changed files with 213 additions and 17 deletions
|
|
@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- ASB + CONTROLLER: Add a `monero_seed` command to the controller shell. You can use it to export the seed and restore height of the internal Monero wallet. You can use those to import the wallet into a wallet software of your own choosing.
|
||||||
|
- GUI: You can now change the Monero Node without having to restart.
|
||||||
|
|
||||||
## [3.0.0-beta.8] - 2025-08-10
|
## [3.0.0-beta.8] - 2025-08-10
|
||||||
|
|
||||||
- GUI: Speedup startup by concurrently bootstrapping Tor and requesting the user to select a wallet
|
- GUI: Speedup startup by concurrently bootstrapping Tor and requesting the user to select a wallet
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,14 @@ pub struct AppState {
|
||||||
/// Manages background tasks for the RPC pool
|
/// Manages background tasks for the RPC pool
|
||||||
pub struct PoolHandle {
|
pub struct PoolHandle {
|
||||||
pub status_update_handle: JoinHandle<()>,
|
pub status_update_handle: JoinHandle<()>,
|
||||||
|
pub server_info: ServerInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PoolHandle {
|
||||||
|
/// Get the current server info for the pool
|
||||||
|
pub fn server_info(&self) -> &ServerInfo {
|
||||||
|
&self.server_info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for PoolHandle {
|
impl Drop for PoolHandle {
|
||||||
|
|
@ -92,6 +100,10 @@ pub async fn create_app_with_receiver(
|
||||||
|
|
||||||
let pool_handle = PoolHandle {
|
let pool_handle = PoolHandle {
|
||||||
status_update_handle,
|
status_update_handle,
|
||||||
|
server_info: ServerInfo {
|
||||||
|
port: config.port,
|
||||||
|
host: config.host.clone(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
|
|
@ -145,7 +157,7 @@ pub async fn start_server_with_random_port(
|
||||||
PoolHandle,
|
PoolHandle,
|
||||||
)> {
|
)> {
|
||||||
let host = config.host.clone();
|
let host = config.host.clone();
|
||||||
let (app, status_receiver, pool_handle) = create_app_with_receiver(config).await?;
|
let (app, status_receiver, mut pool_handle) = create_app_with_receiver(config).await?;
|
||||||
|
|
||||||
// Bind to port 0 to get a random available port
|
// Bind to port 0 to get a random available port
|
||||||
let listener = tokio::net::TcpListener::bind(format!("{}:0", host)).await?;
|
let listener = tokio::net::TcpListener::bind(format!("{}:0", host)).await?;
|
||||||
|
|
@ -156,6 +168,9 @@ pub async fn start_server_with_random_port(
|
||||||
host: host.clone(),
|
host: host.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update the pool handle with the actual server info
|
||||||
|
pool_handle.server_info = server_info.clone();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Started server on {}:{} (random port)",
|
"Started server on {}:{} (random port)",
|
||||||
server_info.host, server_info.port
|
server_info.host, server_info.port
|
||||||
|
|
|
||||||
|
|
@ -200,9 +200,10 @@ namespace Monero
|
||||||
subtract_fee_indices); // Subtract fee from all outputs
|
subtract_fee_indices); // Subtract fee from all outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool setWalletDaemon(Wallet &wallet, const std::string &daemon_address)
|
inline bool setWalletDaemon(Wallet &wallet, const std::string &daemon_address, bool try_ssl)
|
||||||
{
|
{
|
||||||
return wallet.setDaemon(daemon_address);
|
std::string ssl = try_ssl ? "autodetect" : "disabled";
|
||||||
|
return wallet.setDaemon(daemon_address, ssl);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::unique_ptr<std::string> pendingTransactionTxId(const PendingTransaction &tx)
|
inline std::unique_ptr<std::string> pendingTransactionTxId(const PendingTransaction &tx)
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,11 @@ pub mod ffi {
|
||||||
fn refreshAsync(self: Pin<&mut Wallet>) -> Result<()>;
|
fn refreshAsync(self: Pin<&mut Wallet>) -> Result<()>;
|
||||||
|
|
||||||
/// Set the daemon address.
|
/// Set the daemon address.
|
||||||
fn setWalletDaemon(wallet: Pin<&mut Wallet>, daemon_address: &CxxString) -> Result<bool>;
|
fn setWalletDaemon(
|
||||||
|
wallet: Pin<&mut Wallet>,
|
||||||
|
daemon_address: &CxxString,
|
||||||
|
try_ssl: bool,
|
||||||
|
) -> Result<bool>;
|
||||||
|
|
||||||
/// Set whether the daemon is trusted.
|
/// Set whether the daemon is trusted.
|
||||||
fn setTrustedDaemon(self: Pin<&mut Wallet>, trusted: bool) -> Result<()>;
|
fn setTrustedDaemon(self: Pin<&mut Wallet>, trusted: bool) -> Result<()>;
|
||||||
|
|
|
||||||
|
|
@ -1394,7 +1394,7 @@ impl FfiWallet {
|
||||||
.map_err(|e| anyhow!("Failed to initialize wallet: {e}"))?;
|
.map_err(|e| anyhow!("Failed to initialize wallet: {e}"))?;
|
||||||
tracing::debug!("Initialized wallet, setting daemon address");
|
tracing::debug!("Initialized wallet, setting daemon address");
|
||||||
|
|
||||||
wallet.set_daemon_address(&daemon.address)?;
|
wallet.set_daemon(&daemon)?;
|
||||||
|
|
||||||
if background_sync {
|
if background_sync {
|
||||||
tracing::debug!("Background sync enabled, starting refresh thread");
|
tracing::debug!("Background sync enabled, starting refresh thread");
|
||||||
|
|
@ -1438,13 +1438,16 @@ impl FfiWallet {
|
||||||
monero::Address::from_str(&address.to_string()).expect("wallet's own address to be valid")
|
monero::Address::from_str(&address.to_string()).expect("wallet's own address to be valid")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_daemon_address(&mut self, address: &str) -> anyhow::Result<()> {
|
pub fn set_daemon(&mut self, daemon: &Daemon) -> anyhow::Result<()> {
|
||||||
tracing::debug!(%address, "Setting daemon address");
|
let ssl = daemon.ssl;
|
||||||
|
let address = daemon.address.clone();
|
||||||
|
|
||||||
|
tracing::debug!(%address, %ssl, "Setting daemon address");
|
||||||
|
|
||||||
let_cxx_string!(address = address);
|
let_cxx_string!(address = address);
|
||||||
let raw_wallet = &mut self.inner;
|
let raw_wallet = &mut self.inner;
|
||||||
|
|
||||||
let success = ffi::setWalletDaemon(raw_wallet.pinned(), &address)
|
let success = ffi::setWalletDaemon(raw_wallet.pinned(), &address, ssl)
|
||||||
.context("Failed to set daemon address: FFI call failed with exception")?;
|
.context("Failed to set daemon address: FFI call failed with exception")?;
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import {
|
||||||
SetRestoreHeightArgs,
|
SetRestoreHeightArgs,
|
||||||
SetRestoreHeightResponse,
|
SetRestoreHeightResponse,
|
||||||
GetRestoreHeightResponse,
|
GetRestoreHeightResponse,
|
||||||
|
MoneroNodeConfig,
|
||||||
} from "models/tauriModel";
|
} from "models/tauriModel";
|
||||||
import {
|
import {
|
||||||
rpcSetBalance,
|
rpcSetBalance,
|
||||||
|
|
@ -641,3 +642,33 @@ export async function saveFilesInDialog(files: Record<string, string>) {
|
||||||
export async function dfxAuthenticate(): Promise<DfxAuthenticateResponse> {
|
export async function dfxAuthenticate(): Promise<DfxAuthenticateResponse> {
|
||||||
return await invokeNoArgs<DfxAuthenticateResponse>("dfx_authenticate");
|
return await invokeNoArgs<DfxAuthenticateResponse>("dfx_authenticate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function changeMoneroNode(
|
||||||
|
nodeConfig: MoneroNodeConfig,
|
||||||
|
): Promise<void> {
|
||||||
|
await invoke<{ node_config: MoneroNodeConfig }, void>("change_monero_node", {
|
||||||
|
node_config: nodeConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create MoneroNodeConfig from current settings
|
||||||
|
export async function getCurrentMoneroNodeConfig(): Promise<MoneroNodeConfig> {
|
||||||
|
const network = getNetwork();
|
||||||
|
const useMoneroRpcPool = store.getState().settings.useMoneroRpcPool;
|
||||||
|
const moneroNodeUrl =
|
||||||
|
store.getState().settings.nodes[network][Blockchain.Monero][0] ?? null;
|
||||||
|
|
||||||
|
const moneroNodeConfig =
|
||||||
|
useMoneroRpcPool ||
|
||||||
|
moneroNodeUrl == null ||
|
||||||
|
!(await getMoneroNodeStatus(moneroNodeUrl, network))
|
||||||
|
? { type: "Pool" as const }
|
||||||
|
: {
|
||||||
|
type: "SingleNode" as const,
|
||||||
|
content: {
|
||||||
|
url: moneroNodeUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return moneroNodeConfig;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import {
|
||||||
fetchSellersAtPresetRendezvousPoints,
|
fetchSellersAtPresetRendezvousPoints,
|
||||||
getSwapInfo,
|
getSwapInfo,
|
||||||
initializeMoneroWallet,
|
initializeMoneroWallet,
|
||||||
|
changeMoneroNode,
|
||||||
|
getCurrentMoneroNodeConfig,
|
||||||
} from "renderer/rpc";
|
} from "renderer/rpc";
|
||||||
import logger from "utils/logger";
|
import logger from "utils/logger";
|
||||||
import { contextStatusEventReceived } from "store/features/rpcSlice";
|
import { contextStatusEventReceived } from "store/features/rpcSlice";
|
||||||
|
|
@ -14,6 +16,9 @@ import {
|
||||||
addNode,
|
addNode,
|
||||||
setFetchFiatPrices,
|
setFetchFiatPrices,
|
||||||
setFiatCurrency,
|
setFiatCurrency,
|
||||||
|
setUseMoneroRpcPool,
|
||||||
|
Blockchain,
|
||||||
|
Network,
|
||||||
} from "store/features/settingsSlice";
|
} from "store/features/settingsSlice";
|
||||||
import { fetchFeedbackMessagesViaHttp, updateRates } from "renderer/api";
|
import { fetchFeedbackMessagesViaHttp, updateRates } from "renderer/api";
|
||||||
import { store } from "renderer/store/storeRenderer";
|
import { store } from "renderer/store/storeRenderer";
|
||||||
|
|
@ -22,7 +27,8 @@ import {
|
||||||
addFeedbackId,
|
addFeedbackId,
|
||||||
setConversation,
|
setConversation,
|
||||||
} from "store/features/conversationsSlice";
|
} from "store/features/conversationsSlice";
|
||||||
import { TauriContextStatusEvent } from "models/tauriModel";
|
import { TauriContextStatusEvent, MoneroNodeConfig } from "models/tauriModel";
|
||||||
|
import { getNetwork } from "store/config";
|
||||||
|
|
||||||
// Create a Map to store throttled functions per swap_id
|
// Create a Map to store throttled functions per swap_id
|
||||||
const throttledGetSwapInfoFunctions = new Map<
|
const throttledGetSwapInfoFunctions = new Map<
|
||||||
|
|
@ -128,6 +134,25 @@ export function createMainListeners() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listener for Monero node configuration changes
|
||||||
|
listener.startListening({
|
||||||
|
actionCreator: setUseMoneroRpcPool,
|
||||||
|
effect: async (action) => {
|
||||||
|
const usePool = action.payload;
|
||||||
|
logger.info(
|
||||||
|
`Monero node setting changed to: ${usePool ? "Pool" : "Single Node"}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const nodeConfig = await getCurrentMoneroNodeConfig();
|
||||||
|
await changeMoneroNode(nodeConfig);
|
||||||
|
logger.info("Changed Monero node configuration to: ", nodeConfig);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Failed to change Monero node configuration:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Listener for when a feedback id is added
|
// Listener for when a feedback id is added
|
||||||
listener.startListening({
|
listener.startListening({
|
||||||
actionCreator: addFeedbackId,
|
actionCreator: addFeedbackId,
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ use swap::cli::{
|
||||||
api::{
|
api::{
|
||||||
data,
|
data,
|
||||||
request::{
|
request::{
|
||||||
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, CheckElectrumNodeArgs,
|
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ChangeMoneroNodeArgs,
|
||||||
CheckElectrumNodeResponse, CheckMoneroNodeArgs, CheckMoneroNodeResponse, CheckSeedArgs,
|
CheckElectrumNodeArgs, CheckElectrumNodeResponse, CheckMoneroNodeArgs,
|
||||||
CheckSeedResponse, DfxAuthenticateResponse, ExportBitcoinWalletArgs,
|
CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, DfxAuthenticateResponse,
|
||||||
GetCurrentSwapArgs, GetDataDirArgs, GetHistoryArgs, GetLogsArgs,
|
ExportBitcoinWalletArgs, GetCurrentSwapArgs, GetDataDirArgs, GetHistoryArgs,
|
||||||
GetMoneroAddressesArgs, GetMoneroBalanceArgs, GetMoneroHistoryArgs,
|
GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs, GetMoneroHistoryArgs,
|
||||||
GetMoneroMainAddressArgs, GetMoneroSyncProgressArgs, GetPendingApprovalsResponse,
|
GetMoneroMainAddressArgs, GetMoneroSyncProgressArgs, GetPendingApprovalsResponse,
|
||||||
GetRestoreHeightArgs, GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs,
|
GetRestoreHeightArgs, GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs,
|
||||||
MoneroRecoveryArgs, RedactArgs, RejectApprovalArgs, RejectApprovalResponse,
|
MoneroRecoveryArgs, RedactArgs, RejectApprovalArgs, RejectApprovalResponse,
|
||||||
|
|
@ -212,6 +212,7 @@ pub fn run() {
|
||||||
reject_approval_request,
|
reject_approval_request,
|
||||||
get_restore_height,
|
get_restore_height,
|
||||||
dfx_authenticate,
|
dfx_authenticate,
|
||||||
|
change_monero_node,
|
||||||
])
|
])
|
||||||
.setup(setup)
|
.setup(setup)
|
||||||
.build(tauri::generate_context!())
|
.build(tauri::generate_context!())
|
||||||
|
|
@ -253,6 +254,7 @@ tauri_command!(list_sellers, ListSellersArgs);
|
||||||
tauri_command!(cancel_and_refund, CancelAndRefundArgs);
|
tauri_command!(cancel_and_refund, CancelAndRefundArgs);
|
||||||
tauri_command!(redact, RedactArgs);
|
tauri_command!(redact, RedactArgs);
|
||||||
tauri_command!(send_monero, SendMoneroArgs);
|
tauri_command!(send_monero, SendMoneroArgs);
|
||||||
|
tauri_command!(change_monero_node, ChangeMoneroNodeArgs);
|
||||||
|
|
||||||
// These commands require no arguments
|
// These commands require no arguments
|
||||||
tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args);
|
tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args);
|
||||||
|
|
|
||||||
|
|
@ -644,6 +644,65 @@ impl Context {
|
||||||
pub fn tauri_handle(&self) -> Option<TauriHandle> {
|
pub fn tauri_handle(&self) -> Option<TauriHandle> {
|
||||||
self.tauri_handle.clone()
|
self.tauri_handle.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the Monero node configuration for all wallets
|
||||||
|
pub async fn change_monero_node(&self, node_config: MoneroNodeConfig) -> Result<()> {
|
||||||
|
let monero_manager = self
|
||||||
|
.monero_manager
|
||||||
|
.as_ref()
|
||||||
|
.context("Monero wallet manager not available")?;
|
||||||
|
|
||||||
|
// Determine the daemon configuration based on the node config
|
||||||
|
let daemon = match node_config {
|
||||||
|
MoneroNodeConfig::Pool => {
|
||||||
|
// Use the pool handle to get server info
|
||||||
|
let pool_handle = self
|
||||||
|
.monero_rpc_pool_handle
|
||||||
|
.as_ref()
|
||||||
|
.context("Pool handle not available")?;
|
||||||
|
|
||||||
|
let server_info = pool_handle.server_info();
|
||||||
|
let pool_url: String = server_info.clone().into();
|
||||||
|
tracing::info!("Switching to Monero RPC pool: {}", pool_url);
|
||||||
|
|
||||||
|
monero_sys::Daemon {
|
||||||
|
address: pool_url,
|
||||||
|
ssl: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MoneroNodeConfig::SingleNode { url } => {
|
||||||
|
tracing::info!("Switching to single Monero node: {}", url);
|
||||||
|
|
||||||
|
// Parse the URL to determine SSL and address
|
||||||
|
let (address, ssl) = if url.starts_with("https://") {
|
||||||
|
(
|
||||||
|
url.strip_prefix("https://").unwrap_or(&url).to_string(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else if url.starts_with("http://") {
|
||||||
|
(
|
||||||
|
url.strip_prefix("http://").unwrap_or(&url).to_string(),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Default to HTTP if no protocol specified
|
||||||
|
(url, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
monero_sys::Daemon { address, ssl }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update the wallet manager's daemon configuration
|
||||||
|
monero_manager
|
||||||
|
.change_monero_node(daemon.clone())
|
||||||
|
.await
|
||||||
|
.context("Failed to change Monero node in wallet manager")?;
|
||||||
|
|
||||||
|
tracing::info!(?daemon, "Switched Monero node");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Context {
|
impl fmt::Debug for Context {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::tauri_bindings::TauriHandle;
|
use super::tauri_bindings::TauriHandle;
|
||||||
use crate::bitcoin::{wallet, CancelTimelock, ExpiredTimelocks, PunishTimelock};
|
use crate::bitcoin::{wallet, CancelTimelock, ExpiredTimelocks, PunishTimelock};
|
||||||
use crate::cli::api::tauri_bindings::{
|
use crate::cli::api::tauri_bindings::{
|
||||||
ApprovalRequestType, SelectMakerDetails, SendMoneroDetails, TauriEmitter,
|
ApprovalRequestType, MoneroNodeConfig, SelectMakerDetails, SendMoneroDetails, TauriEmitter,
|
||||||
TauriSwapProgressEvent,
|
TauriSwapProgressEvent,
|
||||||
};
|
};
|
||||||
use crate::cli::api::Context;
|
use crate::cli::api::Context;
|
||||||
|
|
@ -2006,3 +2006,37 @@ pub struct DfxAuthenticateResponse {
|
||||||
pub access_token: String,
|
pub access_token: String,
|
||||||
pub kyc_url: String,
|
pub kyc_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeMoneroNode
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ChangeMoneroNodeArgs {
|
||||||
|
pub node_config: MoneroNodeConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct ChangeMoneroNodeResponse {
|
||||||
|
pub success: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Request for ChangeMoneroNodeArgs {
|
||||||
|
type Response = ChangeMoneroNodeResponse;
|
||||||
|
|
||||||
|
async fn request(self, ctx: Arc<Context>) -> Result<Self::Response> {
|
||||||
|
change_monero_node(self, ctx).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(fields(method = "change_monero_node"), skip(context))]
|
||||||
|
pub async fn change_monero_node(
|
||||||
|
args: ChangeMoneroNodeArgs,
|
||||||
|
context: Arc<Context>,
|
||||||
|
) -> Result<ChangeMoneroNodeResponse> {
|
||||||
|
context
|
||||||
|
.change_monero_node(args.node_config)
|
||||||
|
.await
|
||||||
|
.context("Failed to change Monero node")?;
|
||||||
|
|
||||||
|
Ok(ChangeMoneroNodeResponse { success: true })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use anyhow::{Context, Result};
|
||||||
use monero::{Address, Network};
|
use monero::{Address, Network};
|
||||||
use monero_sys::WalletEventListener;
|
use monero_sys::WalletEventListener;
|
||||||
pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener};
|
pub use monero_sys::{Daemon, WalletHandle as Wallet, WalletHandleListener};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::cli::api::{
|
use crate::cli::api::{
|
||||||
|
|
@ -29,7 +30,7 @@ pub struct Wallets {
|
||||||
/// The network we're on.
|
/// The network we're on.
|
||||||
network: Network,
|
network: Network,
|
||||||
/// The monero node we connect to.
|
/// The monero node we connect to.
|
||||||
daemon: Daemon,
|
daemon: Arc<RwLock<Daemon>>,
|
||||||
/// Keep the main wallet open and synced.
|
/// Keep the main wallet open and synced.
|
||||||
main_wallet: Arc<Wallet>,
|
main_wallet: Arc<Wallet>,
|
||||||
/// Since Network::Regtest isn't a thing we have to use an extra flag.
|
/// Since Network::Regtest isn't a thing we have to use an extra flag.
|
||||||
|
|
@ -275,6 +276,8 @@ impl Wallets {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let daemon = Arc::new(RwLock::new(daemon));
|
||||||
|
|
||||||
let wallets = Self {
|
let wallets = Self {
|
||||||
wallet_dir,
|
wallet_dir,
|
||||||
network,
|
network,
|
||||||
|
|
@ -323,6 +326,8 @@ impl Wallets {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let daemon = Arc::new(RwLock::new(daemon));
|
||||||
|
|
||||||
let wallets = Self {
|
let wallets = Self {
|
||||||
wallet_dir,
|
wallet_dir,
|
||||||
network,
|
network,
|
||||||
|
|
@ -367,6 +372,8 @@ impl Wallets {
|
||||||
.await
|
.await
|
||||||
.context("Couldn't fetch blockchain height")?;
|
.context("Couldn't fetch blockchain height")?;
|
||||||
|
|
||||||
|
let daemon = self.daemon.read().await.clone();
|
||||||
|
|
||||||
let wallet = Wallet::open_or_create_from_keys(
|
let wallet = Wallet::open_or_create_from_keys(
|
||||||
wallet_path.clone(),
|
wallet_path.clone(),
|
||||||
None,
|
None,
|
||||||
|
|
@ -376,7 +383,7 @@ impl Wallets {
|
||||||
spend_key,
|
spend_key,
|
||||||
blockheight,
|
blockheight,
|
||||||
false, // We don't sync the swap wallet, just import the transaction
|
false, // We don't sync the swap wallet, just import the transaction
|
||||||
self.daemon.clone(),
|
daemon,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context(format!(
|
.context(format!(
|
||||||
|
|
@ -460,6 +467,18 @@ impl Wallets {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn change_monero_node(&self, new_daemon: Daemon) -> Result<()> {
|
||||||
|
{
|
||||||
|
let mut daemon = self.daemon.write().await;
|
||||||
|
*daemon = new_daemon.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.main_wallet
|
||||||
|
.call(move |wallet| wallet.set_daemon(&new_daemon))
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the last 5 recently used wallets
|
/// Get the last 5 recently used wallets
|
||||||
pub async fn get_recent_wallets(&self) -> Result<Vec<String>> {
|
pub async fn get_recent_wallets(&self) -> Result<Vec<String>> {
|
||||||
if let Some(db) = &self.wallet_database {
|
if let Some(db) = &self.wallet_database {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue