diff --git a/doc/files.md b/doc/files.md index ae05693e750f..da38c7e87d50 100644 --- a/doc/files.md +++ b/doc/files.md @@ -52,7 +52,7 @@ Subdirectory | File(s) | Description `evodb/` | |special txes and quorums database `llmq/` | |quorum signatures database `./` | `banlist.dat` | Stores the IPs/subnets of banned nodes -`./` | `dash.conf` | Contains [configuration settings](dash-conf.md) for `dashd` or `dash-qt`; can be specified by `-conf` option +`./` | `dash.conf` | User-defined [configuration settings](dash-conf.md) for `dashd` or `dash-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option `./` | `dashd.pid` | Stores the process ID (PID) of `dashd` or `dash-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option `./` | `debug.log` | Contains debug information and general logging generated by `dashd` or `dash-qt`; can be specified by `-debuglogfile` option `./` | `governance.dat` | stores data for governance objects @@ -63,6 +63,7 @@ Subdirectory | File(s) | Description `./` | `mempool.dat` | Dump of the mempool's transactions `./` | `onion_v3_private_key` | Cached Tor hidden service private key for `-listenonion` option `./` | `peers.dat` | Peer IP address database (custom format) +`./` | `settings.json` | Read-write settings set through GUI or RPC interfaces, augmenting manual settings from [dash.conf](dash-conf.md). File is created automatically if read-write settings storage is not disabled with `-nosettings` option. Path can be specified with `-settings` option `./` | `.cookie` | Session RPC authentication cookie; if used, created at start and deleted on shutdown; can be specified by `-rpccookiefile` option `./` | `.lock` | Data directory lock file diff --git a/doc/release-notes-15937.md b/doc/release-notes-15937.md new file mode 100644 index 000000000000..1ab817b0e596 --- /dev/null +++ b/doc/release-notes-15937.md @@ -0,0 +1,15 @@ +Configuration +------------- + +Wallets created or loaded in the GUI will now be automatically loaded on +startup, so they don't need to be manually reloaded next time Bitcoin is +started. The list of wallets to load on startup is stored in +`\/settings.json` and augments any command line or `bitcoin.conf` +`-wallet=` settings that specify more wallets to load. Wallets that are +unloaded in the GUI get removed from the settings list so they won't load again +automatically next startup. (#19754) + +The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept +`load_on_startup` options to modify the settings list. Unless these options are +explicitly set to true or false, the list is not modified, so the RPC methods +remain backwards compatible. (#15937) diff --git a/src/dashd.cpp b/src/dashd.cpp index ca7b57121e24..33090fd97cef 100644 --- a/src/dashd.cpp +++ b/src/dashd.cpp @@ -45,7 +45,6 @@ static void WaitForShutdown(NodeContext& node) static bool AppInit(int argc, char* argv[]) { NodeContext node; - node.chain = interfaces::MakeChain(node); bool fRet = false; @@ -102,6 +101,11 @@ static bool AppInit(int argc, char* argv[]) } } + if (!gArgs.InitSettings(error)) { + InitError(Untranslated(error)); + return false; + } + // -server defaults to true for dashd but not for the GUI so do this here args.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console @@ -145,7 +149,7 @@ static bool AppInit(int argc, char* argv[]) // If locking the data directory failed, exit immediately return false; } - fRet = AppInitMain(context, node); + fRet = AppInitInterfaces(node) && AppInitMain(context, node); } catch (...) { PrintExceptionContinue(std::current_exception(), "AppInit()"); } diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index 45888110c8bd..939a6d466ff8 100644 --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -5,11 +5,8 @@ #include #include #include -#include class CWallet; -enum class WalletCreationStatus; -struct bilingual_str; namespace interfaces { class Chain; @@ -75,37 +72,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); -fs::path GetWalletDir() -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - -std::vector ListWalletDir() -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - -std::vector> GetWallets() -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - -std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector& warnings) -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - -WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& result) -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - -using LoadWalletFn = std::function wallet)>; -std::unique_ptr HandleLoadWallet(LoadWalletFn load_wallet) -{ - throw std::logic_error("Wallet function called in non-wallet build."); -} - namespace interfaces { std::unique_ptr MakeWallet(const std::shared_ptr& wallet) diff --git a/src/init.cpp b/src/init.cpp index a94f03f6e9c9..597e991d0abd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -503,7 +503,7 @@ void SetupServerArgs(NodeContext& node) #endif argsman.AddArg("-blockreconstructionextratxn=", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless '-whitelistforcerelay' is '1', in which case whitelisted peers' transactions will be relayed. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-conf=", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-conf=", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-dbcache=", strprintf("Maximum database cache size MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -522,6 +522,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-prune=", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -rescan and -disablegovernance=false. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-settings=", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-syncmempool", strprintf("Sync mempool from other nodes on start (default: %u)", DEFAULT_SYNC_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #ifndef WIN32 argsman.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -574,7 +575,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-socketevents=", "Socket events mode, which must be one of 'select', 'poll', 'epoll' or 'kqueue', depending on your system (default: Linux - 'epoll', FreeBSD/Apple - 'kqueue', Windows - 'select')", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-timeout=", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-torcontrol=:", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-torpassword=", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-torpassword=", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP argsman.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -746,14 +747,14 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcallowip=", "Allow JSON-RPC connections from specified source. Valid for are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); - argsman.AddArg("-rpcauth=", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=/rpcpassword= pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); - argsman.AddArg("-rpcbind=[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); + argsman.AddArg("-rpcauth=", "Username and HMAC-SHA-256 hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=/rpcpassword= pair of arguments. This option can be specified multiple times", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); + argsman.AddArg("-rpcbind=[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY | ArgsManager::SENSITIVE, OptionsCategory::RPC); argsman.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); - argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcthreads=", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); - argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); argsman.AddArg("-rpcwhitelist=", "Set a whitelist to filter incoming RPC calls for a specific user. The field comes in the format: :,,...,. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC); argsman.AddArg("-rpcworkqueue=", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); @@ -1607,6 +1608,17 @@ bool AppInitLockDataDirectory() return true; } +bool AppInitInterfaces(NodeContext& node) +{ + node.chain = interfaces::MakeChain(node); + // Create client interfaces for wallets that are supposed to be loaded + // according to -wallet and -disablewallet options. This only constructs + // the interfaces, it doesn't load wallet data. Wallets actually get loaded + // when load() and start() interface methods are called below. + g_wallet_init_interface.Construct(node); + return true; +} + bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) { const ArgsManager& args = *Assert(node.args); @@ -1645,6 +1657,9 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string()); } + // Log the config arguments to debug.log + args.LogArgs(); + LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. @@ -1718,12 +1733,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA tableRPC.InitPlatformRestrictions(); - // Create client interfaces for wallets that are supposed to be loaded - // according to -wallet and -disablewallet options. This only constructs - // the interfaces, it doesn't load wallet data. Wallets actually get loaded - // when load() and start() interface methods are called below. - g_wallet_init_interface.Construct(node); - /* Register RPC commands regardless of -server setting so they will be * available in the GUI RPC console even if external calls are disabled. */ diff --git a/src/init.h b/src/init.h index 0f632be14a35..f384d53971f5 100644 --- a/src/init.h +++ b/src/init.h @@ -52,6 +52,10 @@ bool AppInitSanityChecks(); * @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called. */ bool AppInitLockDataDirectory(); +/** + * Initialize node and wallet interface pointers. Has no prerequisites or side effects besides allocating memory. + */ +bool AppInitInterfaces(NodeContext& node); /** * Dash Core main initialization. * @note This should only be done after daemonization. Call Shutdown() if this function fails. diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 72148d1c5ace..0ec655aa6e2a 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -360,6 +360,27 @@ class ChainImpl : public Chain { RPCRunLater(name, std::move(fn), seconds); } + util::SettingsValue getRwSetting(const std::string& name) override + { + util::SettingsValue result; + gArgs.LockSettings([&](const util::Settings& settings) { + if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) { + result = *value; + } + }); + return result; + } + bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override + { + gArgs.LockSettings([&](util::Settings& settings) { + if (value.isNull()) { + settings.rw_settings.erase(name); + } else { + settings.rw_settings[name] = value; + } + }); + return gArgs.WriteSettingsFile(); + } void requestMempoolTransactions(Notifications& notifications) override { LOCK2(::cs_main, ::mempool.cs); diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index bfa4a8e86ca3..360ad9db6222 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -7,6 +7,7 @@ #include // For Optional and nullopt #include // For CTransactionRef +#include // For util::SettingsValue #include #include @@ -244,6 +245,12 @@ class Chain //! Run function after given number of seconds. Cancel any previous calls with same name. virtual void rpcRunLater(const std::string& name, std::function fn, int64_t seconds) = 0; + //! Return /settings.json setting value. + virtual util::SettingsValue getRwSetting(const std::string& name) = 0; + + //! Write a setting to /settings.json. + virtual bool updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0; + //! Synchronously send TransactionAddedToMempool notifications about all //! current mempool transactions to the specified handler and return after //! the last one is sent. These notifications aren't coordinated with async @@ -282,24 +289,11 @@ class ChainClient //! Set mock time. virtual void setMockTime(int64_t time) = 0; - - //! Return interfaces for accessing wallets (if any). - virtual std::vector> getWallets() = 0; }; //! Return implementation of Chain interface. std::unique_ptr MakeChain(NodeContext& node); -//! Return implementation of ChainClient interface for a wallet client. This -//! function will be undefined in builds where ENABLE_WALLET is false. -//! -//! Currently, wallets are the only chain clients. But in the future, other -//! types of chain clients could be added, such as tools for monitoring, -//! analysis, or fee estimation. These clients need to expose their own -//! MakeXXXClient functions returning their implementations of the ChainClient -//! interface. -std::unique_ptr MakeWalletClient(Chain& chain, std::vector wallet_filenames); - } // namespace interfaces #endif // BITCOIN_INTERFACES_CHAIN_H diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index b1db4d297ff2..6924a8722348 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -48,16 +49,7 @@ #include -class CWallet; -fs::path GetWalletDir(); -std::vector ListWalletDir(); -std::vector> GetWallets(); -std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector& warnings); -WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::shared_ptr& result); -std::unique_ptr HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet); - namespace interfaces { - namespace { class EVOImpl : public EVO @@ -187,6 +179,7 @@ class NodeImpl : public Node bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { SelectParams(network); } + bool initSettings(std::string& error) override { return gArgs.InitSettings(error); } uint64_t getAssumedBlockchainSize() override { return Params().AssumedBlockchainSize(); } uint64_t getAssumedChainStateSize() override { return Params().AssumedChainStateSize(); } std::string getNetwork() override { return Params().NetworkIDString(); } @@ -197,11 +190,10 @@ class NodeImpl : public Node bool baseInitialize() override { return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() && - AppInitLockDataDirectory(); + AppInitLockDataDirectory() && AppInitInterfaces(*m_context); } bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override { - m_context->chain = MakeChain(*m_context); return AppInitMain(m_context_ref, *m_context, tip_info); } void appShutdown() override @@ -367,30 +359,9 @@ class NodeImpl : public Node LOCK(::cs_main); return ::ChainstateActive().CoinsTip().GetCoin(output, coin); } - std::string getWalletDir() override - { - return GetWalletDir().string(); - } - std::vector listWalletDir() override - { - std::vector paths; - for (auto& path : ListWalletDir()) { - paths.push_back(path.string()); - } - return paths; - } - std::vector> getWallets() override + WalletClient& walletClient() override { - std::vector> wallets; - for (auto& client : m_context->chain_clients) { - auto client_wallets = client->getWallets(); - std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets)); - } - return wallets; - } - std::unique_ptr loadWallet(const std::string& name, bilingual_str& error, std::vector& warnings) override - { - return MakeWallet(LoadWallet(*m_context->chain, name, error, warnings)); + return *Assert(m_context->wallet_client); } EVO& evo() override { return m_evo; } @@ -399,13 +370,6 @@ class NodeImpl : public Node Masternode::Sync& masternodeSync() override { return m_masternodeSync; } CoinJoin::Options& coinJoinOptions() override { return m_coinjoin; } - WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::unique_ptr& result) override - { - std::shared_ptr wallet; - WalletCreationStatus status = CreateWallet(*m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet); - result = MakeWallet(wallet); - return status; - } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage_connect(fn)); @@ -422,10 +386,6 @@ class NodeImpl : public Node { return MakeHandler(::uiInterface.ShowProgress_connect(fn)); } - std::unique_ptr handleLoadWallet(LoadWalletFn fn) override - { - return HandleLoadWallet(std::move(fn)); - } std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override { return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn)); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 1e928bbbc1b4..3aed7542ca9f 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -31,13 +31,14 @@ class RPCTimerInterface; class UniValue; class proxyType; struct bilingual_str; +enum class SynchronizationState; struct CNodeStateStats; enum class WalletCreationStatus; struct NodeContext; namespace interfaces { class Handler; -class Wallet; +class WalletClient; //! Interface for the src/evo part of a dash node (dashd process). class EVO @@ -136,6 +137,11 @@ class Node //! Choose network parameters. virtual void selectParams(const std::string& network) = 0; + //! Read and update /settings.json file with saved settings. This + //! needs to be called after selectParams() because the settings file + //! location is network-specific. + virtual bool initSettings(std::string& error) = 0; + //! Get the (assumed) blockchain size. virtual uint64_t getAssumedBlockchainSize() = 0; @@ -269,19 +275,8 @@ class Node //! Get unspent outputs associated with a transaction. virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0; - //! Return default wallet directory. - virtual std::string getWalletDir() = 0; - - //! Return available wallets in wallet directory. - virtual std::vector listWalletDir() = 0; - - //! Return interfaces for accessing wallets (if any). - virtual std::vector> getWallets() = 0; - - //! Attempts to load a wallet from file or directory. - //! The loaded wallet is also notified to handlers previously registered - //! with handleLoadWallet. - virtual std::unique_ptr loadWallet(const std::string& name, bilingual_str& error, std::vector& warnings) = 0; + //! Get wallet client. + virtual WalletClient& walletClient() = 0; //! Return interface for accessing evo related handler. virtual EVO& evo() = 0; @@ -298,9 +293,6 @@ class Node //! Return interface for accessing coinjoin related handler. virtual CoinJoin::Options& coinJoinOptions() = 0; - //! Create a wallet from file - virtual WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector& warnings, std::unique_ptr& result) = 0; - //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; @@ -321,10 +313,6 @@ class Node using ShowProgressFn = std::function; virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; - //! Register handler for load wallet messages. - using LoadWalletFn = std::function wallet)>; - virtual std::unique_ptr handleLoadWallet(LoadWalletFn fn) = 0; - //! Register handler for number of connections changed messages. using NotifyNumConnectionsChangedFn = std::function; virtual std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index facf25cf593b..66ceed744524 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -506,7 +506,7 @@ class WalletImpl : public Wallet CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; } void remove() override { - RemoveWallet(m_wallet); + RemoveWallet(m_wallet, false /* load_on_start */); } std::unique_ptr handleUnload(UnloadFn fn) override { @@ -555,7 +555,7 @@ class WalletImpl : public Wallet CoinJoinImpl m_coinjoin; }; -class WalletClientImpl : public ChainClient +class WalletClientImpl : public WalletClient { public: WalletClientImpl(Chain& chain, std::vector wallet_filenames) @@ -563,6 +563,9 @@ class WalletClientImpl : public ChainClient { m_context.chain = &chain; } + ~WalletClientImpl() override { UnloadWallets(); } + + //! ChainClient methods void registerRpcs() override { for (const CRPCCommand& command : GetWalletRPCCommands()) { @@ -578,6 +581,30 @@ class WalletClientImpl : public ChainClient void flush() override { return FlushWallets(); } void stop() override { return StopWallets(); } void setMockTime(int64_t time) override { return SetMockTime(time); } + + //! WalletClient methods + std::unique_ptr createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, WalletCreationStatus& status, bilingual_str& error, std::vector& warnings) override + { + std::shared_ptr wallet; + status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, true /* load_on_start */, error, warnings, wallet); + return MakeWallet(std::move(wallet)); + } + std::unique_ptr loadWallet(const std::string& name, bilingual_str& error, std::vector& warnings) override + { + return MakeWallet(LoadWallet(*m_context.chain, WalletLocation(name), true /* load_on_start */, error, warnings)); + } + std::string getWalletDir() override + { + return GetWalletDir().string(); + } + std::vector listWalletDir() override + { + std::vector paths; + for (auto& path : ListWalletDir()) { + paths.push_back(path.string()); + } + return paths; + } std::vector> getWallets() override { std::vector> wallets; @@ -586,7 +613,10 @@ class WalletClientImpl : public ChainClient } return wallets; } - ~WalletClientImpl() override { UnloadWallets(); } + std::unique_ptr handleLoadWallet(LoadWalletFn fn) override + { + return HandleLoadWallet(std::move(fn)); + } WalletContext m_context; std::vector m_wallet_filenames; @@ -598,7 +628,7 @@ class WalletClientImpl : public ChainClient std::unique_ptr MakeWallet(const std::shared_ptr& wallet) { return wallet ? MakeUnique(wallet) : nullptr; } -std::unique_ptr MakeWalletClient(Chain& chain, std::vector wallet_filenames) +std::unique_ptr MakeWalletClient(Chain& chain, std::vector wallet_filenames) { return MakeUnique(chain, std::move(wallet_filenames)); } diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 353d82cdcda9..45bbb3b828a0 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -7,10 +7,12 @@ #include // For CAmount #include // For fs::path +#include // For ChainClient #include // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include