Global config instance (#769)

* config: global instance

* fix: cli tests

* fix: owner api tests

* config: save tor config

* fix: owner rpc tests

* fix: set config file path on init and owner api call

* tor: store launched services, get global config at command loop, remove unused listen args, check for empty global config to set default, do not save empty tor config at owner

* api: update config in memory after successful file write

* arti: restart proxy in loop

* fix: comment for tor config save

* config: set or init, update without error

* config: scoped tor config

* build: update lock file

* config: ability to store multiple configurations at memory from config path

* tor: restart service on config change

* fix: owner api test

* foreign: pass tor config from args, stop service, error handling

* arti: move runtime wrapper to single file

* config: read existing config from file at create_config

* config: handle error on read

* arti: single dir for cache

* foreign: register config listener before launch

* config: save temp

* config: remove unused error type

* config: make path and members not optional, return an error on config creation if it exists, pass default tor config to send if not set

* arti: non public runtime wrapper

* config: migrate comments

* fix: rpc tests

* config: return from cache if exists

* config: save disabled tor config when null provided at owner and tor section was not set

* config: update tor with args at listener

* config: pass pathbuf directly on creation

* config: unique tmp file name, setting permissions and providing config version

* fix global config handling

* fix config persistence

* fix tor service restart

* fix config synchronization

* require wallet close before changing data directory

* unify global config updates

---------

Co-authored-by: wiesche <wiesche89@googlemail.com>
This commit is contained in:
ardocrat
2026-07-24 22:15:19 +02:00
committed by GitHub
co-authored by wiesche
parent 7fb47646e9
commit d2fe2b90f6
46 changed files with 4880 additions and 3311 deletions
+28 -34
View File
@@ -14,9 +14,9 @@
//! Foreign API External Definition
use crate::config::TorConfig;
use crate::keychain::Keychain;
use crate::libwallet::api_impl::foreign;
use crate::libwallet::api_impl::types::update_tx_slate_state;
use crate::libwallet::{
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletInst,
WalletLCProvider,
@@ -24,7 +24,7 @@ use crate::libwallet::{
use crate::try_slatepack_sync_workflow;
use crate::util::secp::key::SecretKey;
use crate::util::Mutex;
use libwallet::api_impl::types::update_tx_slate_state;
use std::sync::Arc;
/// ForeignAPI Middleware Check callback
@@ -67,15 +67,14 @@ where
{
/// Wallet instance
pub wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
/// Wallet configuration path
config_path: crate::ConfigPath,
/// Flag to normalize some output during testing. Can mostly be ignored.
pub doctest_mode: bool,
/// foreign check middleware
middleware: Option<ForeignCheckMiddleware>,
/// Stored keychain mask (in case the stored wallet seed is tokenized)
keychain_mask: Option<SecretKey>,
/// Optional TOR configuration, holding address of sender and
/// data directory
tor_config: Mutex<Option<TorConfig>>,
}
impl<'a, L, C, K> Foreign<'a, L, C, K>
@@ -95,6 +94,7 @@ where
/// # Arguments
/// * `wallet_in` - A reference-counted mutex containing an implementation of the
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
/// * `config_path` - Path to the wallet configuration file
/// * `keychain_mask` - Mask value stored internally to use when calling a wallet
/// whose seed has been XORed with a token value (such as when running the foreign
/// and owner listeners in the same instance)
@@ -106,6 +106,7 @@ where
///
/// # Example
/// ```
/// use std::path::PathBuf;
/// use grin_keychain as keychain;
/// use grin_util as util;
/// use grin_core;
@@ -164,39 +165,30 @@ where
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
///
/// let api_foreign = Foreign::new(wallet.clone(), None, None, false);
/// let api_foreign = Foreign::new(wallet.clone(), PathBuf::from(dir).join("grin-wallet.toml"), None, None, false);
/// // .. perform wallet operations
///
/// ```
pub fn new(
pub fn new<P>(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
config_path: P,
keychain_mask: Option<SecretKey>,
middleware: Option<ForeignCheckMiddleware>,
doctest_mode: bool,
) -> Self {
) -> Self
where
P: Into<crate::ConfigPath>,
{
Foreign {
wallet_inst,
config_path: config_path.into(),
doctest_mode,
middleware,
keychain_mask,
tor_config: Mutex::new(None),
}
}
/// Set the TOR configuration for this instance of the ForeignAPI, used during
/// `recieve_tx` when a return address is specified
///
/// # Arguments
/// * `tor_config` - The optional [TorConfig](#) to use
/// # Returns
/// * Nothing
pub fn set_tor_config(&self, tor_config: Option<TorConfig>) {
let mut lock = self.tor_config.lock();
*lock = tor_config;
}
/// Return the version capabilities of the running ForeignApi Node
/// # Arguments
/// None
@@ -207,7 +199,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
/// let mut api_foreign = Foreign::new(wallet.clone(), std::path::PathBuf::from("grin-wallet.toml"), None, None, false);
///
/// let version_info = api_foreign.check_version();
/// // check and proceed accordingly
@@ -259,7 +251,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
/// let mut api_foreign = Foreign::new(wallet.clone(), std::path::PathBuf::from("grin-wallet.toml"), None, None, false);
///
/// let block_fees = BlockFees {
/// fees: 800000,
@@ -334,7 +326,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
/// let mut api_foreign = Foreign::new(wallet.clone(), std::path::PathBuf::from("grin-wallet.toml"), None, None, false);
/// # let slate = Slate::blank(2, false);
///
/// // . . .
@@ -354,6 +346,10 @@ where
r_addr: Option<String>,
) -> Result<Slate, Error> {
let mut w_lock = self.wallet_inst.lock();
let tor_config = r_addr
.as_ref()
.map(|_| crate::tor_config::load(&self.config_path.get()))
.transpose()?;
let w = w_lock.lc_provider()?.wallet_inst()?;
if let Some(m) = self.middleware.as_ref() {
m(
@@ -371,16 +367,14 @@ where
)?;
match r_addr {
Some(a) => {
let tc = self.tor_config.lock();
let can_send = if let Some(tc) = tc.as_ref() {
tc.send_tor(None)
} else {
false
};
let tc = tor_config.ok_or_else(|| {
Error::TorConfig("Tor config was not loaded with a return address".into())
})?;
let can_send = tc.send_tor(None);
if self.doctest_mode || !can_send {
return Ok(ret_slate);
}
let res = try_slatepack_sync_workflow(&ret_slate, &a, tc.clone(), None, true);
let res = try_slatepack_sync_workflow(&ret_slate, &a, Some(tc), None, true);
match res {
Ok(s) => {
let parent_key_id = w.parent_key_id();
@@ -433,8 +427,8 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let mut api_foreign = Foreign::new(wallet.clone(), std::path::PathBuf::from("grin-wallet.toml"), None, None, false);
///
/// // . . .
/// // Issue the invoice tx via the owner API
+24 -2
View File
@@ -21,6 +21,8 @@ use crate::libwallet::{
};
use crate::{Foreign, ForeignCheckMiddlewareFn};
use easy_jsonrpc_mw;
use grin_wallet_config::initial_setup_wallet;
use std::path::PathBuf;
/// Public definition used to generate Foreign jsonrpc api.
/// * When running `grin-wallet listen` with defaults, the V2 api is available at
@@ -364,6 +366,14 @@ pub fn run_doctest_foreign(
let _ = fs::remove_dir_all(test_dir);
global::set_local_chain_type(ChainTypes::AutomatedTesting);
let _ = fs::create_dir_all(test_dir);
let config = initial_setup_wallet(
&ChainTypes::AutomatedTesting,
Some(PathBuf::from(test_dir)),
false,
)
.unwrap();
let mut wallet_proxy: WalletProxy<
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
@@ -512,8 +522,20 @@ pub fn run_doctest_foreign(
}
let mut api_foreign = match init_invoice_tx {
false => Foreign::new(wallet1, mask1, Some(test_check_middleware), true),
true => Foreign::new(wallet2, mask2, Some(test_check_middleware), true),
false => Foreign::new(
wallet1,
config.config_file_path,
mask1,
Some(test_check_middleware),
true,
),
true => Foreign::new(
wallet2,
config.config_file_path,
mask2,
Some(test_check_middleware),
true,
),
};
api_foreign.doctest_mode = true;
let foreign_api = &api_foreign as &dyn ForeignRpc;
+3
View File
@@ -42,12 +42,15 @@ mod foreign_rpc;
mod owner;
mod owner_rpc;
mod tor_config;
mod types;
pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn};
pub use crate::foreign_rpc::ForeignRpc;
pub use crate::owner::{try_slatepack_sync_workflow, Owner};
pub use crate::owner_rpc::OwnerRpc;
#[doc(hidden)]
pub use crate::tor_config::ConfigPath;
pub use crate::foreign_rpc::foreign_rpc as foreign_rpc_client;
pub use crate::foreign_rpc::run_doctest_foreign;
+157 -96
View File
@@ -14,12 +14,6 @@
//! Owner API External Definition
use chrono::prelude::*;
use ed25519_dalek::SigningKey as DalekSecretKey;
use grin_wallet_libwallet::mwixnet::{MixnetReqCreationParams, SwapReq};
use grin_wallet_libwallet::RetrieveTxQueryArgs;
use uuid::Uuid;
use crate::config::{TorConfig, WalletConfig};
use crate::core::core::OutputFeatures;
use crate::core::global;
@@ -27,6 +21,7 @@ use crate::impls::SlateSender as _;
use crate::impls::TorSlateSender;
use crate::keychain::{Identifier, Keychain};
use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
use crate::libwallet::api_impl::types::update_tx_slate_state;
use crate::libwallet::api_impl::{owner, owner_updater};
use crate::libwallet::{
AcctPathMapping, BuiltOutput, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
@@ -36,16 +31,25 @@ use crate::libwallet::{
use crate::util::logger::LoggingConfig;
use crate::util::secp::{key::SecretKey, pedersen::Commitment};
use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString};
use grin_wallet_config::config::{
reload_global_config, update_global_config, WALLET_CONFIG_FILE_NAME,
};
use grin_wallet_libwallet::mwixnet::{MixnetReqCreationParams, SwapReq};
use grin_wallet_libwallet::RetrieveTxQueryArgs;
use grin_wallet_util::OnionV3Address;
use libwallet::api_impl::types::update_tx_slate_state;
use chrono::prelude::*;
use ed25519_dalek::SigningKey as DalekSecretKey;
use std::convert::TryFrom;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use uuid::Uuid;
/// Main interface into all wallet API functions.
/// Wallet APIs are split into two seperate blocks of functionality
@@ -67,11 +71,13 @@ where
C: NodeClient + 'static,
K: Keychain + 'static,
{
/// contain all methods to manage the wallet
/// Contains all methods to manage the wallet
pub wallet_inst: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
/// Wallet configuration path
config_path: crate::ConfigPath,
/// Flag to normalize some output during testing. Can mostly be ignored.
pub doctest_mode: bool,
/// retail TLD during doctest
/// Retail TLD during doctest
pub doctest_retain_tld: bool,
/// Share ECDH key
pub shared_key: Arc<Mutex<Option<SecretKey>>>,
@@ -84,9 +90,6 @@ where
/// Holds all update and status messages returned by the
/// updater process
updater_messages: Arc<Mutex<Vec<StatusMessage>>>,
/// Optional TOR configuration, holding address of sender and
/// data directory
tor_config: Mutex<Option<TorConfig>>,
}
impl<L, C, K> Owner<L, C, K>
@@ -105,15 +108,17 @@ where
///
/// # Arguments
/// * `wallet_in` - A reference-counted mutex containing an implementation of the
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
/// * `custom_channel` - A custom MPSC Tx/Rx pair to capture status
/// updates
/// [`WalletBackend`](../grin_wallet_libwallet/types/trait.WalletBackend.html) trait.
/// * `config_path` - Path to the wallet configuration file
///
/// # Returns
/// * An instance of the OwnerApi holding a reference to the provided wallet
///
/// # Example
/// ```
/// use std::path::PathBuf;
/// use grin_keychain as keychain;
/// use grin_util as util;
/// use grin_core;
@@ -171,15 +176,19 @@ where
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
/// let mut wallet = Arc::new(Mutex::new(wallet));
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, PathBuf::from(dir).join("grin-wallet.toml"));
/// // .. perform wallet operations
///
/// ```
pub fn new(
pub fn new<P>(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
custom_channel: Option<Sender<StatusMessage>>,
) -> Self {
config_path: P,
) -> Self
where
P: Into<crate::ConfigPath>,
{
let updater_running = Arc::new(AtomicBool::new(false));
let updater = Arc::new(Mutex::new(owner_updater::Updater::new(
wallet_inst.clone(),
@@ -198,6 +207,7 @@ where
Owner {
wallet_inst,
config_path: config_path.into(),
doctest_mode: false,
doctest_retain_tld: false,
shared_key: Arc::new(Mutex::new(None)),
@@ -205,21 +215,49 @@ where
updater_running,
status_tx: Mutex::new(Some(tx)),
updater_messages,
tor_config: Mutex::new(None),
}
}
/// Return the active wallet configuration path.
pub fn config_path(&self) -> PathBuf {
self.config_path.get()
}
/// Return the shared wallet configuration path.
#[doc(hidden)]
pub fn shared_config_path(&self) -> crate::ConfigPath {
self.config_path.clone()
}
/// Set the TOR configuration for this instance of the OwnerAPI, used during
/// `init_send_tx` when send args are present and a TOR address is specified
/// `init_send_tx` when send args are present and a TOR address is specified,
/// this will also update persistent config and will restart foreign listener
///
/// # Arguments
/// * `tor_config` - The optional [TorConfig](#) to use
/// # Returns
/// * Nothing
/// * Result Containing:
/// * `Ok(())` if the config was correctly saved
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
///
pub fn set_tor_config(&self, tor_config: Option<TorConfig>) {
let mut lock = self.tor_config.lock();
*lock = tor_config;
pub fn set_tor_config(&self, tor_config: Option<TorConfig>) -> Result<(), Error> {
let _wallet_lock = self.wallet_inst.lock();
update_global_config(&self.config_path(), |config| {
if let Some(tor_config) = tor_config {
config.members.tor = Some(tor_config);
} else if let Some(tor) = config.members.tor.as_mut() {
tor.use_tor_listener = false;
tor.skip_send_attempt = Some(true);
} else {
let mut tor_config = config.tor_config();
tor_config.use_tor_listener = false;
tor_config.skip_send_attempt = Some(true);
config.members.tor = Some(tor_config);
}
Ok(())
})
.map_err(|e| Error::TorConfig(format!("{}", e)))
}
/// Returns a list of accounts stored in the wallet (i.e. mappings between
@@ -244,7 +282,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let result = api_owner.accounts(None);
///
@@ -295,7 +333,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let result = api_owner.create_account_path(None, "account1");
///
@@ -342,7 +380,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let result = api_owner.create_account_path(None, "account1");
///
@@ -398,7 +436,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let show_spent = false;
/// let update_from_node = true;
/// let tx_id = None;
@@ -469,7 +507,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let update_from_node = true;
/// let tx_id = None;
/// let tx_slate_id = None;
@@ -548,7 +586,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let update_from_node = true;
/// let minimum_confirmations=10;
///
@@ -635,7 +673,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// // Attempt to create a transaction using the 'default' account
/// let args = InitTxArgs {
/// src_acct_name: None,
@@ -665,26 +703,28 @@ where
args: InitTxArgs,
) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let slate = {
let (slate, tor_config) = {
let mut w_lock = self.wallet_inst.lock();
let tor_config = send_args
.as_ref()
.map(|_| crate::tor_config::load(&self.config_path()))
.transpose()?;
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::init_send_tx(w, keychain_mask, args, self.doctest_mode)?
let slate = owner::init_send_tx(w, keychain_mask, args, self.doctest_mode)?;
(slate, tor_config)
};
// Helper functionality. If send arguments exist, attempt to send sync and
// finalize
match send_args {
Some(sa) => {
let tor_config_lock = self.tor_config.lock();
let tc = tor_config_lock.clone();
let can_send = if let Some(tc) = tc.as_ref() {
tc.send_tor(sa.skip_tor)
} else {
false
};
let tc = tor_config.ok_or_else(|| {
Error::TorConfig("Tor config was not loaded with send arguments".into())
})?;
let can_send = tc.send_tor(sa.skip_tor);
if self.doctest_mode || !can_send {
return Ok(slate);
}
let res = try_slatepack_sync_workflow(&slate, &sa.dest, tc, None, false);
let res = try_slatepack_sync_workflow(&slate, &sa.dest, Some(tc), None, false);
match res {
Ok(s) => {
self.tx_lock_outputs(keychain_mask, &s)?;
@@ -737,7 +777,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let args = IssueInvoiceTxArgs {
/// amount: 60_000_000_000,
@@ -798,7 +838,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// // . . .
/// // The slate has been recieved from the invoicer, somehow
@@ -828,25 +868,28 @@ where
args: InitTxArgs,
) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let slate = {
let (slate, tor_config) = {
let mut w_lock = self.wallet_inst.lock();
let tor_config = send_args
.as_ref()
.map(|_| crate::tor_config::load(&self.config_path()))
.transpose()?;
let w = w_lock.lc_provider()?.wallet_inst()?;
owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?
let slate =
owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?;
(slate, tor_config)
};
// Helper functionality. If send arguments exist, attempt to send
match send_args {
Some(sa) => {
let tor_config_lock = self.tor_config.lock();
let tc = tor_config_lock.clone();
let can_send = if let Some(tc) = tc.as_ref() {
tc.send_tor(sa.skip_tor)
} else {
false
};
let tc = tor_config.ok_or_else(|| {
Error::TorConfig("Tor config was not loaded with send arguments".into())
})?;
let can_send = tc.send_tor(sa.skip_tor);
if self.doctest_mode || !can_send {
return Ok(slate);
}
let res = try_slatepack_sync_workflow(&slate, &sa.dest, tc, None, true);
let res = try_slatepack_sync_workflow(&slate, &sa.dest, Some(tc), None, true);
match res {
Ok(s) => {
// Update slate state.
@@ -907,7 +950,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
@@ -969,7 +1012,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
@@ -1028,7 +1071,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
@@ -1099,7 +1142,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
@@ -1170,7 +1213,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let update_from_node = true;
/// let tx_id = None;
/// let tx_slate_id = None;
@@ -1214,7 +1257,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let result = api_owner.scan(
/// None,
/// Some(20000),
@@ -1254,7 +1297,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let result = api_owner.scan(
/// None,
/// Some(20000),
@@ -1318,7 +1361,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let result = api_owner.scan(
/// None,
/// Some(20000),
@@ -1376,7 +1419,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let result = api_owner.node_height(None);
///
/// if let Ok(node_height_result) = result {
@@ -1433,7 +1476,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let result = api_owner.get_top_level_directory();
///
/// if let Ok(dir) = result {
@@ -1458,6 +1501,9 @@ where
/// Set [`get_top_level_directory`](struct.Owner.html#method.get_top_level_directory) for a
/// description of the top level directory and default paths.
///
/// The wallet must be closed and the updater stopped before changing this directory.
/// Open the wallet again afterwards to load its database from the new location.
///
/// # Arguments
///
/// * `dir`: The new top-level directory path (either relative to current directory or
@@ -1481,7 +1527,7 @@ where
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
/// # .unwrap();
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from(dir));
/// let result = api_owner.set_top_level_directory(dir);
///
/// if let Ok(dir) = result {
@@ -1490,9 +1536,25 @@ where
/// ```
pub fn set_top_level_directory(&self, dir: &str) -> Result<(), Error> {
if self.updater_running.load(Ordering::Relaxed) {
return Err(Error::Lifecycle(
"Stop the updater before changing the top-level directory".into(),
));
}
let mut w_lock = self.wallet_inst.lock();
let lc = w_lock.lc_provider()?;
lc.set_top_level_directory(dir)
if lc.wallet_inst().is_ok() {
return Err(Error::Lifecycle(
"Close the wallet before changing the top-level directory".into(),
));
}
let config_path = PathBuf::from(dir).join(WALLET_CONFIG_FILE_NAME);
if config_path.exists() {
reload_global_config(&config_path)?;
}
lc.set_top_level_directory(dir)?;
self.config_path.set(config_path);
Ok(())
}
/// Create a `grin-wallet.toml` configuration file in the top-level directory for the
@@ -1531,7 +1593,7 @@ where
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
/// # .unwrap();
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from(dir));
/// let _ = api_owner.set_top_level_directory(dir);
///
/// let result = api_owner.create_config(&ChainTypes::Mainnet, None, None, None);
@@ -1600,7 +1662,7 @@ where
/// # .to_str()
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
/// # .unwrap();
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from(dir));
/// let _ = api_owner.set_top_level_directory(dir);
///
/// // Create configuration
@@ -1667,7 +1729,7 @@ where
/// # .to_str()
/// # .ok_or("Failed to convert tmpdir path to string.".to_owned())
/// # .unwrap();
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from(dir));
/// let _ = api_owner.set_top_level_directory(dir);
///
/// // Create configuration
@@ -1724,7 +1786,7 @@ where
/// use grin_core::global::ChainTypes;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.close_wallet(None);
///
@@ -1760,7 +1822,7 @@ where
/// use grin_core::global::ChainTypes;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let pw = ZeroingString::from("my_password");
/// let res = api_owner.get_mnemonic(None, pw);
@@ -1805,7 +1867,7 @@ where
/// use grin_core::global::ChainTypes;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let old = ZeroingString::from("my_password");
/// let new = ZeroingString::from("new_password");
@@ -1848,7 +1910,7 @@ where
/// use grin_core::global::ChainTypes;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.delete_wallet(None);
///
@@ -1904,7 +1966,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.start_updater(None, Duration::from_secs(60));
///
@@ -1959,7 +2021,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.start_updater(None, Duration::from_secs(60));
///
@@ -2001,7 +2063,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.start_updater(None, Duration::from_secs(60));
///
@@ -2067,7 +2129,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.get_slatepack_address(None, 0);
///
@@ -2107,7 +2169,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.get_slatepack_secret_key(None, 0);
///
@@ -2148,9 +2210,8 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let mut api_owner = Owner::new(wallet.clone(), None);
/// let args = InitTxArgs {
/// src_acct_name: None,
/// amount: 2_000_000_000,
@@ -2218,7 +2279,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// // ... receive a slatepack from somewhere
/// # let slatepack_string = String::from("");
/// let res = api_owner.slate_from_slatepack_message(
@@ -2268,7 +2329,7 @@ where
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None);
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// # let slatepack_string = String::from("");
/// // .. receive a slatepack from somewhere
/// let res = api_owner.decode_slatepack_message(
@@ -2326,7 +2387,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let update_from_node = true;
/// let tx_id = None;
/// let tx_slate_id = Some(Uuid::parse_str("0436430c-2b02-624c-2032-570501212b00").unwrap());
@@ -2395,7 +2456,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let update_from_node = true;
/// let tx_id = None;
/// let tx_slate_id = Some(Uuid::parse_str("0436430c-2b02-624c-2032-570501212b00").unwrap());
@@ -2457,7 +2518,7 @@ where
/// ```
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// let api_owner = Owner::new(wallet.clone(), None);
/// let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
/// let keychain_mask = None;
/// let params = MixnetReqCreationParams {
/// server_keys: vec![], // Public keys here in secret key representation
@@ -2508,20 +2569,20 @@ pub fn try_slatepack_sync_workflow(
send_to_finalize: bool,
) -> Result<Slate, Error> {
let mut ret_slate = Slate::blank(2, false);
let mut send_sync = |mut sender: TorSlateSender, method_str: &str| match sender
.send_tx(&slate, send_to_finalize)
{
Ok(s) => {
ret_slate = s;
return Ok(());
}
Err(e) => {
debug!(
"Send ({}): Could not send Slate via {}: {}",
method_str, method_str, e
);
return Err(e);
}
let mut send_sync = |mut sender: TorSlateSender, method_str: &str| {
return match sender.send_tx(&slate, send_to_finalize) {
Ok(s) => {
ret_slate = s;
Ok(())
}
Err(e) => {
debug!(
"Send ({}): Could not send Slate via {}: {}",
method_str, method_str, e
);
Err(e)
}
};
};
// Try parsing Slatepack address.
+21 -5
View File
@@ -36,6 +36,7 @@ use easy_jsonrpc_mw;
use grin_wallet_util::OnionV3Address;
use rand::thread_rng;
use std::convert::TryFrom;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
@@ -1207,6 +1208,9 @@ pub trait OwnerRpc {
/**
Networked version of [Owner::set_top_level_directory](struct.Owner.html#method.set_top_level_directory).
The wallet must be closed and the updater stopped before calling this method. Running listeners
should be restarted after opening the wallet from the new directory.
```
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
# r#"
@@ -2423,7 +2427,7 @@ where
}
fn set_tor_config(&self, tor_config: Option<TorConfig>) -> Result<(), Error> {
Owner::set_tor_config(self, tor_config);
Owner::set_tor_config(self, tor_config)?;
Ok(())
}
@@ -2487,6 +2491,7 @@ pub fn run_doctest_owner(
) -> Result<Option<serde_json::Value>, String> {
use easy_jsonrpc_mw::Handler;
use grin_keychain::ExtKeychain;
use grin_wallet_config::initial_setup_wallet;
use grin_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy};
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
use grin_wallet_libwallet::{api_impl, WalletInst};
@@ -2500,6 +2505,14 @@ pub fn run_doctest_owner(
let _ = fs::remove_dir_all(test_dir);
global::set_local_chain_type(ChainTypes::AutomatedTesting);
let _ = fs::create_dir_all(test_dir);
let config = initial_setup_wallet(
&ChainTypes::AutomatedTesting,
Some(PathBuf::from(test_dir)),
false,
)
.unwrap();
let mut wallet_proxy: WalletProxy<
DefaultLCProvider<LocalWalletClient, ExtKeychain>,
LocalWalletClient,
@@ -2507,11 +2520,11 @@ pub fn run_doctest_owner(
> = WalletProxy::new(test_dir);
let chain = wallet_proxy.chain.clone();
let rec_phrase_1 = util::ZeroingString::from(
let rec_phrase_1 = ZeroingString::from(
"fat twenty mean degree forget shell check candy immense awful \
flame next during february bulb bike sun wink theory day kiwi embrace peace lunch",
);
let empty_string = util::ZeroingString::from("");
let empty_string = ZeroingString::from("");
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
let mut wallet1 =
@@ -2546,7 +2559,7 @@ pub fn run_doctest_owner(
let mut slate_outer = Slate::blank(2, false);
let rec_phrase_2 = util::ZeroingString::from(
let rec_phrase_2 = ZeroingString::from(
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
sell finish magic kid tiny wage stand panther inside settle feed song hole exile",
);
@@ -2669,8 +2682,11 @@ pub fn run_doctest_owner(
);
}
let mut api_owner = Owner::new(wallet1, None);
let mut api_owner = Owner::new(wallet1, None, config.config_file_path);
api_owner.doctest_mode = true;
if request["method"] == "set_top_level_directory" {
api_owner.close_wallet(None).unwrap();
}
let owner_api = &api_owner as &dyn OwnerRpc;
let res = owner_api.handle_request(request).as_option();
let _ = fs::remove_dir_all(test_dir);
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2026 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::TorConfig;
use crate::libwallet::Error;
use crate::util::Mutex;
use grin_wallet_config::config::get_global_config;
use std::path::{Path, PathBuf};
use std::sync::Arc;
/// Shared path to the active wallet configuration.
#[doc(hidden)]
#[derive(Clone)]
pub struct ConfigPath(Arc<Mutex<PathBuf>>);
impl ConfigPath {
pub(crate) fn get(&self) -> PathBuf {
self.0.lock().clone()
}
pub(crate) fn set(&self, path: PathBuf) {
*self.0.lock() = path;
}
}
impl From<PathBuf> for ConfigPath {
fn from(path: PathBuf) -> Self {
Self(Arc::new(Mutex::new(path)))
}
}
pub(crate) fn load(config_path: &Path) -> Result<TorConfig, Error> {
Ok(get_global_config(config_path)?.tor_config())
}