diff --git a/doc/release-notes.md b/doc/release-notes.md
index c7aa95d60..be871b8c1 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,17 +1,38 @@
# Bitcoin ABC 0.24.3 Release Notes
Bitcoin ABC version 0.24.3 is now available from:
This release includes the following features and fixes:
- - The `-zapwallettxes` startup option has been removed and its functionality removed
- from the wallet. This functionality has been superseded with the abandon transaction
- feature.
- - `getnetworkinfo` now returns two new fields, `connections_in` and
- `connections_out`, that provide the number of inbound and outbound peer
- connections. These new fields are in addition to the existing `connections`
- field, which returns the total number of peer connections.
- - The `connections` field of `bitcoin-cli -getinfo` is expanded to return a JSON
- object with `in`, `out` and `total` numbers of peer connections. It previously
- returned a single integer value for the total number of peer connections.
+
+Wallet
+------
+
+- The `-zapwallettxes` startup option has been removed and its functionality removed
+ from the wallet. This functionality has been superseded with the abandon transaction
+ feature.
+
+Configuration
+-------------
+
+The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept
+`load_on_startup` options that modify bitcoin's dynamic configuration in
+`\/settings.json`, and can add or remove a wallet from the list of
+wallets automatically loaded at startup. Unless these options are explicitly
+set to true or false, the load on startup wallet list is not modified, so this
+change is backwards compatible.
+
+In the future, the GUI will start updating the same startup wallet list as the
+RPCs to automatically reopen wallets previously opened in the GUI.
+
+RPCs
+----
+
+- `getnetworkinfo` now returns two new fields, `connections_in` and
+ `connections_out`, that provide the number of inbound and outbound peer
+ connections. These new fields are in addition to the existing `connections`
+ field, which returns the total number of peer connections.
+- The `connections` field of `bitcoin-cli -getinfo` is expanded to return a JSON
+ object with `in`, `out` and `total` numbers of peer connections. It previously
+ returned a single integer value for the total number of peer connections.
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index e2409277d..ab3298557 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -1,472 +1,493 @@
// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace interfaces {
namespace {
bool FillBlock(const CBlockIndex *index, const FoundBlock &block,
UniqueLock &lock) {
if (!index) {
return false;
}
if (block.m_hash) {
*block.m_hash = index->GetBlockHash();
}
if (block.m_height) {
*block.m_height = index->nHeight;
}
if (block.m_time) {
*block.m_time = index->GetBlockTime();
}
if (block.m_max_time) {
*block.m_max_time = index->GetBlockTimeMax();
}
if (block.m_mtp_time) {
*block.m_mtp_time = index->GetMedianTimePast();
}
if (block.m_data) {
REVERSE_LOCK(lock);
if (!ReadBlockFromDisk(*block.m_data, index,
Params().GetConsensus())) {
block.m_data->SetNull();
}
}
return true;
}
class NotificationsProxy : public CValidationInterface {
public:
explicit NotificationsProxy(
std::shared_ptr notifications)
: m_notifications(std::move(notifications)) {}
virtual ~NotificationsProxy() = default;
void TransactionAddedToMempool(const CTransactionRef &tx) override {
m_notifications->transactionAddedToMempool(tx);
}
void
TransactionRemovedFromMempool(const CTransactionRef &tx,
MemPoolRemovalReason reason) override {
m_notifications->transactionRemovedFromMempool(tx, reason);
}
void BlockConnected(const std::shared_ptr &block,
const CBlockIndex *index) override {
m_notifications->blockConnected(*block, index->nHeight);
}
void BlockDisconnected(const std::shared_ptr &block,
const CBlockIndex *index) override {
m_notifications->blockDisconnected(*block, index->nHeight);
}
void UpdatedBlockTip(const CBlockIndex *index,
const CBlockIndex *fork_index,
bool is_ibd) override {
m_notifications->updatedBlockTip();
}
void ChainStateFlushed(const CBlockLocator &locator) override {
m_notifications->chainStateFlushed(locator);
}
std::shared_ptr m_notifications;
};
class NotificationsHandlerImpl : public Handler {
public:
explicit NotificationsHandlerImpl(
std::shared_ptr notifications)
: m_proxy(std::make_shared(
std::move(notifications))) {
RegisterSharedValidationInterface(m_proxy);
}
~NotificationsHandlerImpl() override { disconnect(); }
void disconnect() override {
if (m_proxy) {
UnregisterSharedValidationInterface(m_proxy);
m_proxy.reset();
}
}
std::shared_ptr m_proxy;
};
class RpcHandlerImpl : public Handler {
public:
explicit RpcHandlerImpl(const CRPCCommand &command)
: m_command(command), m_wrapped_command(&command) {
m_command.actor = [this](const Config &config,
const JSONRPCRequest &request,
UniValue &result, bool last_handler) {
if (!m_wrapped_command) {
return false;
}
try {
return m_wrapped_command->actor(config, request, result,
last_handler);
} catch (const UniValue &e) {
// If this is not the last handler and a wallet not found
// exception was thrown, return false so the next handler
// can try to handle the request. Otherwise, reraise the
// exception.
if (!last_handler) {
const UniValue &code = e["code"];
if (code.isNum() &&
code.get_int() == RPC_WALLET_NOT_FOUND) {
return false;
}
}
throw;
}
};
::tableRPC.appendCommand(m_command.name, &m_command);
}
void disconnect() final {
if (m_wrapped_command) {
m_wrapped_command = nullptr;
::tableRPC.removeCommand(m_command.name, &m_command);
}
}
~RpcHandlerImpl() override { disconnect(); }
CRPCCommand m_command;
const CRPCCommand *m_wrapped_command;
};
class ChainImpl : public Chain {
public:
explicit ChainImpl(NodeContext &node, const CChainParams ¶ms)
: m_node(node), m_params(params) {}
std::optional getHeight() override {
LOCK(::cs_main);
int height = ::ChainActive().Height();
if (height >= 0) {
return height;
}
return std::nullopt;
}
std::optional getBlockHeight(const BlockHash &hash) override {
LOCK(::cs_main);
CBlockIndex *block = LookupBlockIndex(hash);
if (block && ::ChainActive().Contains(block)) {
return block->nHeight;
}
return std::nullopt;
}
BlockHash getBlockHash(int height) override {
LOCK(::cs_main);
CBlockIndex *block = ::ChainActive()[height];
assert(block);
return block->GetBlockHash();
}
bool haveBlockOnDisk(int height) override {
LOCK(cs_main);
CBlockIndex *block = ::ChainActive()[height];
return block && (block->nStatus.hasData() != 0) && block->nTx > 0;
}
std::optional
findFirstBlockWithTimeAndHeight(int64_t time, int height,
BlockHash *hash) override {
LOCK(cs_main);
CBlockIndex *block =
::ChainActive().FindEarliestAtLeast(time, height);
if (block) {
if (hash) {
*hash = block->GetBlockHash();
}
return block->nHeight;
}
return std::nullopt;
}
CBlockLocator getTipLocator() override {
LOCK(cs_main);
return ::ChainActive().GetLocator();
}
bool contextualCheckTransactionForCurrentBlock(
const CTransaction &tx, TxValidationState &state) override {
LOCK(cs_main);
return ContextualCheckTransactionForCurrentBlock(
m_params.GetConsensus(), tx, state);
}
std::optional
findLocatorFork(const CBlockLocator &locator) override {
LOCK(cs_main);
if (CBlockIndex *fork =
FindForkInGlobalIndex(::ChainActive(), locator)) {
return fork->nHeight;
}
return std::nullopt;
}
bool findBlock(const BlockHash &hash,
const FoundBlock &block) override {
WAIT_LOCK(cs_main, lock);
return FillBlock(LookupBlockIndex(hash), block, lock);
}
bool findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height,
const FoundBlock &block) override {
WAIT_LOCK(cs_main, lock);
return FillBlock(
ChainActive().FindEarliestAtLeast(min_time, min_height), block,
lock);
}
bool findNextBlock(const BlockHash &block_hash, int block_height,
const FoundBlock &next, bool *reorg) override {
WAIT_LOCK(cs_main, lock);
CBlockIndex *block = ChainActive()[block_height];
if (block && block->GetBlockHash() != block_hash) {
block = nullptr;
}
if (reorg) {
*reorg = !block;
}
return FillBlock(block ? ChainActive()[block_height + 1] : nullptr,
next, lock);
}
bool findAncestorByHeight(const BlockHash &block_hash,
int ancestor_height,
const FoundBlock &ancestor_out) override {
WAIT_LOCK(cs_main, lock);
if (const CBlockIndex *block = LookupBlockIndex(block_hash)) {
if (const CBlockIndex *ancestor =
block->GetAncestor(ancestor_height)) {
return FillBlock(ancestor, ancestor_out, lock);
}
}
return FillBlock(nullptr, ancestor_out, lock);
}
bool findAncestorByHash(const BlockHash &block_hash,
const BlockHash &ancestor_hash,
const FoundBlock &ancestor_out) override {
WAIT_LOCK(cs_main, lock);
const CBlockIndex *block = LookupBlockIndex(block_hash);
const CBlockIndex *ancestor = LookupBlockIndex(ancestor_hash);
if (block && ancestor &&
block->GetAncestor(ancestor->nHeight) != ancestor) {
ancestor = nullptr;
}
return FillBlock(ancestor, ancestor_out, lock);
}
bool findCommonAncestor(const BlockHash &block_hash1,
const BlockHash &block_hash2,
const FoundBlock &ancestor_out,
const FoundBlock &block1_out,
const FoundBlock &block2_out) override {
WAIT_LOCK(cs_main, lock);
const CBlockIndex *block1 = LookupBlockIndex(block_hash1);
const CBlockIndex *block2 = LookupBlockIndex(block_hash2);
const CBlockIndex *ancestor =
block1 && block2 ? LastCommonAncestor(block1, block2) : nullptr;
// Using & instead of && below to avoid short circuiting and leaving
// output uninitialized.
return FillBlock(ancestor, ancestor_out, lock) &
FillBlock(block1, block1_out, lock) &
FillBlock(block2, block2_out, lock);
}
void findCoins(std::map &coins) override {
return FindCoins(m_node, coins);
}
double guessVerificationProgress(const BlockHash &block_hash) override {
LOCK(cs_main);
return GuessVerificationProgress(Params().TxData(),
LookupBlockIndex(block_hash));
}
bool hasBlocks(const BlockHash &block_hash, int min_height,
std::optional max_height) override {
// hasBlocks returns true if all ancestors of block_hash in
// specified range have block data (are not pruned), false if any
// ancestors in specified range are missing data.
//
// For simplicity and robustness, min_height and max_height are only
// used to limit the range, and passing min_height that's too low or
// max_height that's too high will not crash or change the result.
LOCK(::cs_main);
if (CBlockIndex *block = LookupBlockIndex(block_hash)) {
if (max_height && block->nHeight >= *max_height) {
block = block->GetAncestor(*max_height);
}
for (; block->nStatus.hasData(); block = block->pprev) {
// Check pprev to not segfault if min_height is too low
if (block->nHeight <= min_height || !block->pprev) {
return true;
}
}
}
return false;
}
bool hasDescendantsInMempool(const TxId &txid) override {
if (!m_node.mempool) {
return false;
}
LOCK(m_node.mempool->cs);
auto it = m_node.mempool->GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
}
bool broadcastTransaction(const Config &config,
const CTransactionRef &tx,
const Amount &max_tx_fee, bool relay,
std::string &err_string) override {
const TransactionError err = BroadcastTransaction(
m_node, config, tx, err_string, max_tx_fee, relay,
/*wait_callback*/ false);
// Chain clients only care about failures to accept the tx to the
// mempool. Disregard non-mempool related failures. Note: this will
// need to be updated if BroadcastTransactions() is updated to
// return other non-mempool failures that Chain clients do not need
// to know about.
return err == TransactionError::OK;
}
void getTransactionAncestry(const TxId &txid, size_t &ancestors,
size_t &descendants) override {
ancestors = descendants = 0;
if (!m_node.mempool) {
return;
}
m_node.mempool->GetTransactionAncestry(txid, ancestors,
descendants);
}
void getPackageLimits(size_t &limit_ancestor_count,
size_t &limit_descendant_count) override {
limit_ancestor_count = size_t(
std::max(1, gArgs.GetArg("-limitancestorcount",
DEFAULT_ANCESTOR_LIMIT)));
limit_descendant_count = size_t(
std::max(1, gArgs.GetArg("-limitdescendantcount",
DEFAULT_DESCENDANT_LIMIT)));
}
bool checkChainLimits(const CTransactionRef &tx) override {
if (!m_node.mempool) {
return true;
}
LockPoints lp;
CTxMemPoolEntry entry(tx, Amount(), 0, 0, false, 0, lp);
CTxMemPool::setEntries ancestors;
auto limit_ancestor_count =
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
auto limit_ancestor_size =
gArgs.GetArg("-limitancestorsize",
DEFAULT_ANCESTOR_SIZE_LIMIT) *
1000;
auto limit_descendant_count =
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
auto limit_descendant_size =
gArgs.GetArg("-limitdescendantsize",
DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000;
std::string unused_error_string;
LOCK(m_node.mempool->cs);
return m_node.mempool->CalculateMemPoolAncestors(
entry, ancestors, limit_ancestor_count, limit_ancestor_size,
limit_descendant_count, limit_descendant_size,
unused_error_string);
}
CFeeRate estimateFee() const override {
if (!m_node.mempool) {
return {};
}
return m_node.mempool->estimateFee();
}
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayDustFee() override { return ::dustRelayFee; }
bool havePruned() override {
LOCK(cs_main);
return ::fHavePruned;
}
bool isReadyToBroadcast() override {
return !::fImporting && !::fReindex && !isInitialBlockDownload();
}
bool isInitialBlockDownload() override {
return ::ChainstateActive().IsInitialBlockDownload();
}
bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string &message) override {
::uiInterface.InitMessage(message);
}
void initWarning(const bilingual_str &message) override {
InitWarning(message);
}
void initError(const bilingual_str &message) override {
InitError(message);
}
void showProgress(const std::string &title, int progress,
bool resume_possible) override {
::uiInterface.ShowProgress(title, progress, resume_possible);
}
std::unique_ptr handleNotifications(
std::shared_ptr notifications) override {
return std::make_unique(
std::move(notifications));
}
void
waitForNotificationsIfTipChanged(const BlockHash &old_tip) override {
if (!old_tip.IsNull()) {
LOCK(::cs_main);
if (old_tip == ::ChainActive().Tip()->GetBlockHash()) {
return;
}
}
SyncWithValidationInterfaceQueue();
}
std::unique_ptr
handleRpc(const CRPCCommand &command) override {
return std::make_unique(command);
}
bool rpcEnableDeprecated(const std::string &method) override {
return IsDeprecatedRPCEnabled(gArgs, method);
}
void rpcRunLater(const std::string &name, std::function fn,
int64_t seconds) override {
RPCRunLater(name, std::move(fn), seconds);
}
int rpcSerializationFlags() override { return RPCSerializationFlags(); }
+ 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 ¬ifications) override {
if (!m_node.mempool) {
return;
}
LOCK2(::cs_main, m_node.mempool->cs);
for (const CTxMemPoolEntry &entry : m_node.mempool->mapTx) {
notifications.transactionAddedToMempool(entry.GetSharedTx());
}
}
const CChainParams ¶ms() const override { return m_params; }
NodeContext &m_node;
const CChainParams &m_params;
};
} // namespace
std::unique_ptr MakeChain(NodeContext &node,
const CChainParams ¶ms) {
return std::make_unique(node, params);
}
} // namespace interfaces
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 899cc4c8f..92aa4720b 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -1,355 +1,363 @@
// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
#include
#include
+#include // For util::SettingsValue
#include
#include
#include
#include
#include
#include
#include
class ArgsManager;
class CBlock;
class CChainParams;
class Coin;
class Config;
class CRPCCommand;
class CScheduler;
class TxValidationState;
enum class MemPoolRemovalReason;
struct BlockHash;
struct bilingual_str;
struct CBlockLocator;
struct NodeContext;
namespace Consensus {
struct Params;
}
namespace interfaces {
class Handler;
class Wallet;
//! Helper for findBlock to selectively return pieces of block data.
class FoundBlock {
public:
FoundBlock &hash(BlockHash &hash) {
m_hash = &hash;
return *this;
}
FoundBlock &height(int &height) {
m_height = &height;
return *this;
}
FoundBlock &time(int64_t &time) {
m_time = &time;
return *this;
}
FoundBlock &maxTime(int64_t &max_time) {
m_max_time = &max_time;
return *this;
}
FoundBlock &mtpTime(int64_t &mtp_time) {
m_mtp_time = &mtp_time;
return *this;
}
//! Read block data from disk. If the block exists but doesn't have data
//! (for example due to pruning), the CBlock variable will be set to null.
FoundBlock &data(CBlock &data) {
m_data = &data;
return *this;
}
BlockHash *m_hash = nullptr;
int *m_height = nullptr;
int64_t *m_time = nullptr;
int64_t *m_max_time = nullptr;
int64_t *m_mtp_time = nullptr;
CBlock *m_data = nullptr;
};
//! Interface giving clients (wallet processes, maybe other analysis tools in
//! the future) ability to access to the chain state, receive notifications,
//! estimate fees, and submit transactions.
//!
//! TODO: Current chain methods are too low level, exposing too much of the
//! internal workings of the bitcoin node, and not being very convenient to use.
//! Chain methods should be cleaned up and simplified over time. Examples:
//!
//! * The initMessages() and showProgress() methods which the wallet uses to
//! send
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
//! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096).
//!
//! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC
//! methods can go away if wallets listen for HTTP requests on their own
//! ports instead of registering to handle requests on the node HTTP port.
//!
//! * Move fee estimation queries to an asynchronous interface and let the
//! wallet cache it, fee estimation being driven by node mempool, wallet
//! should be the consumer.
//!
//! * The `guessVerificationProgress`, `getBlockHeight`, `getBlockHash`, etc
//! methods can go away if rescan logic is moved on the node side, and wallet
//! only register rescan request.
class Chain {
public:
virtual ~Chain() {}
//! Get current chain height, not including genesis block (returns 0 if
//! chain only contains genesis block, std::nullopt if chain does not
//! contain any blocks)
virtual std::optional getHeight() = 0;
//! Get block height above genesis block. Returns 0 for genesis block,
//! 1 for following block, and so on. Returns std::nullopt for a block not
//! included in the current chain.
virtual std::optional getBlockHeight(const BlockHash &hash) = 0;
//! Get block hash. Height must be valid or this function will abort.
virtual BlockHash getBlockHash(int height) = 0;
//! Check that the block is available on disk (i.e. has not been
//! pruned), and contains transactions.
virtual bool haveBlockOnDisk(int height) = 0;
//! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
//! given height, or std::nullopt if there is no block with a high enough
//! timestamp and height. Also return the block hash as an optional output
//! parameter (to avoid the cost of a second lookup in case this information
//! is needed.)
virtual std::optional
findFirstBlockWithTimeAndHeight(int64_t time, int height,
BlockHash *hash) = 0;
//! Get locator for the current chain tip.
virtual CBlockLocator getTipLocator() = 0;
//! Return height of the highest block on chain in common with the locator,
//! which will either be the original block used to create the locator,
//! or one of its ancestors.
virtual std::optional
findLocatorFork(const CBlockLocator &locator) = 0;
//! Check if transaction will be final given chain height current time.
virtual bool
contextualCheckTransactionForCurrentBlock(const CTransaction &tx,
TxValidationState &state) = 0;
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const BlockHash &hash,
const FoundBlock &block = {}) = 0;
//! Find first block in the chain with timestamp >= the given time
//! and height >= than the given height, return false if there is no block
//! with a high enough timestamp and height. Optionally return block
//! information.
virtual bool
findFirstBlockWithTimeAndHeight(int64_t min_time, int min_height,
const FoundBlock &block = {}) = 0;
//! Find next block if block is part of current chain. Also flag if
//! there was a reorg and the specified block hash is no longer in the
//! current chain, and optionally return block information.
virtual bool findNextBlock(const BlockHash &block_hash, int block_height,
const FoundBlock &next = {},
bool *reorg = nullptr) = 0;
//! Find ancestor of block at specified height and optionally return
//! ancestor information.
virtual bool findAncestorByHeight(const BlockHash &block_hash,
int ancestor_height,
const FoundBlock &ancestor_out = {}) = 0;
//! Return whether block descends from a specified ancestor, and
//! optionally return ancestor information.
virtual bool findAncestorByHash(const BlockHash &block_hash,
const BlockHash &ancestor_hash,
const FoundBlock &ancestor_out = {}) = 0;
//! Find most recent common ancestor between two blocks and optionally
//! return block information.
virtual bool findCommonAncestor(const BlockHash &block_hash1,
const BlockHash &block_hash2,
const FoundBlock &ancestor_out = {},
const FoundBlock &block1_out = {},
const FoundBlock &block2_out = {}) = 0;
//! Look up unspent output information. Returns coins in the mempool and in
//! the current chain UTXO set. Iterates through all the keys in the map and
//! populates the values.
virtual void findCoins(std::map &coins) = 0;
//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const BlockHash &block_hash) = 0;
//! Return true if data is available for all blocks in the specified range
//! of blocks. This checks all blocks that are ancestors of block_hash in
//! the height range from min_height to max_height, inclusive.
virtual bool hasBlocks(const BlockHash &block_hash, int min_height = 0,
std::optional max_height = {}) = 0;
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const TxId &txid) = 0;
//! Transaction is added to memory pool, if the transaction fee is below the
//! amount specified by max_tx_fee, and broadcast to all peers if relay is
//! set to true. Return false if the transaction could not be added due to
//! the fee or for another reason.
virtual bool broadcastTransaction(const Config &config,
const CTransactionRef &tx,
const Amount &max_tx_fee, bool relay,
std::string &err_string) = 0;
//! Calculate mempool ancestor and descendant counts for the given
//! transaction.
virtual void getTransactionAncestry(const TxId &txid, size_t &ancestors,
size_t &descendants) = 0;
//! Get the node's package limits.
//! Currently only returns the ancestor and descendant count limits, but
//! could be enhanced to return more policy settings.
virtual void getPackageLimits(size_t &limit_ancestor_count,
size_t &limit_descendant_count) = 0;
//! Check if transaction will pass the mempool's chain limits.
virtual bool checkChainLimits(const CTransactionRef &tx) = 0;
//! Estimate fee
virtual CFeeRate estimateFee() const = 0;
//! Relay current minimum fee (from -minrelaytxfee settings).
virtual CFeeRate relayMinFee() = 0;
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's
//! economical to spend.
virtual CFeeRate relayDustFee() = 0;
//! Check if any block has been pruned.
virtual bool havePruned() = 0;
//! Check if the node is ready to broadcast transactions.
virtual bool isReadyToBroadcast() = 0;
//! Check if in IBD.
virtual bool isInitialBlockDownload() = 0;
//! Check if shutdown requested.
virtual bool shutdownRequested() = 0;
//! Get adjusted time.
virtual int64_t getAdjustedTime() = 0;
//! Send init message.
virtual void initMessage(const std::string &message) = 0;
//! Send init warning.
virtual void initWarning(const bilingual_str &message) = 0;
//! Send init error.
virtual void initError(const bilingual_str &message) = 0;
//! Send progress indicator.
virtual void showProgress(const std::string &title, int progress,
bool resume_possible) = 0;
//! Chain notifications.
class Notifications {
public:
virtual ~Notifications() {}
virtual void transactionAddedToMempool(const CTransactionRef &tx) {}
virtual void
transactionRemovedFromMempool(const CTransactionRef &ptx,
MemPoolRemovalReason reason) {}
virtual void blockConnected(const CBlock &block, int height) {}
virtual void blockDisconnected(const CBlock &block, int height) {}
virtual void updatedBlockTip() {}
virtual void chainStateFlushed(const CBlockLocator &locator) {}
};
//! Register handler for notifications.
virtual std::unique_ptr
handleNotifications(std::shared_ptr notifications) = 0;
//! Wait for pending notifications to be processed unless block hash points
//! to the current chain tip.
virtual void waitForNotificationsIfTipChanged(const BlockHash &old_tip) = 0;
//! Register handler for RPC. Command is not copied, so reference
//! needs to remain valid until Handler is disconnected.
virtual std::unique_ptr handleRpc(const CRPCCommand &command) = 0;
//! Check if deprecated RPC is enabled.
virtual bool rpcEnableDeprecated(const std::string &method) = 0;
//! 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;
//! Current RPC serialization flags.
virtual int rpcSerializationFlags() = 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
//! notifications sent by handleNotifications, so out of date async
//! notifications from handleNotifications can arrive during and after
//! synchronous notifications from requestMempoolTransactions. Clients need
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications ¬ifications) = 0;
//! This Chain's parameters
virtual const CChainParams ¶ms() const = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
//! monitoring and analysis in the future).
class ChainClient {
public:
virtual ~ChainClient() {}
//! Register rpcs.
virtual void registerRpcs() = 0;
//! Check for errors before loading.
virtual bool verify() = 0;
//! Load saved state.
virtual bool load() = 0;
//! Start client execution and provide a scheduler.
virtual void start(CScheduler &scheduler) = 0;
//! Save state to disk.
virtual void flush() = 0;
//! Shut down client.
virtual void stop() = 0;
//! Set mock time.
virtual void setMockTime(int64_t time) = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr MakeChain(NodeContext &node, const CChainParams ¶ms);
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 407d0501f..4a8f00d26 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -1,256 +1,259 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
class CRPCConvertParam {
public:
std::string methodName; //!< method whose params want conversion
int paramIdx; //!< 0-based idx of param to convert
std::string paramName; //!< parameter name
};
/**
* Specify a (method, idx, name) here if the argument is a non-string RPC
* argument and needs to be converted from JSON.
*
* @note Parameter indexes start from 0.
*/
static const CRPCConvertParam vRPCConvertParams[] = {
{"setmocktime", 0, "timestamp"},
{"mockscheduler", 0, "delta_time"},
{"utxoupdatepsbt", 1, "descriptors"},
{"generatetoaddress", 0, "nblocks"},
{"generatetoaddress", 2, "maxtries"},
{"generatetodescriptor", 0, "num_blocks"},
{"generatetodescriptor", 2, "maxtries"},
{"generateblock", 1, "transactions"},
{"getnetworkhashps", 0, "nblocks"},
{"getnetworkhashps", 1, "height"},
{"sendtoaddress", 1, "amount"},
{"sendtoaddress", 4, "subtractfeefromamount"},
{"sendtoaddress", 5, "avoid_reuse"},
{"settxfee", 0, "amount"},
{"sethdseed", 0, "newkeypool"},
{"getreceivedbyaddress", 1, "minconf"},
{"getreceivedbylabel", 1, "minconf"},
{"listreceivedbyaddress", 0, "minconf"},
{"listreceivedbyaddress", 1, "include_empty"},
{"listreceivedbyaddress", 2, "include_watchonly"},
{"listreceivedbylabel", 0, "minconf"},
{"listreceivedbylabel", 1, "include_empty"},
{"listreceivedbylabel", 2, "include_watchonly"},
{"getbalance", 1, "minconf"},
{"getbalance", 2, "include_watchonly"},
{"getbalance", 3, "avoid_reuse"},
{"getblockhash", 0, "height"},
{"waitforblockheight", 0, "height"},
{"waitforblockheight", 1, "timeout"},
{"waitforblock", 1, "timeout"},
{"waitfornewblock", 0, "timeout"},
{"listtransactions", 1, "count"},
{"listtransactions", 2, "skip"},
{"listtransactions", 3, "include_watchonly"},
{"walletpassphrase", 1, "timeout"},
{"getblocktemplate", 0, "template_request"},
{"listsinceblock", 1, "target_confirmations"},
{"listsinceblock", 2, "include_watchonly"},
{"listsinceblock", 3, "include_removed"},
{"sendmany", 1, "amounts"},
{"sendmany", 2, "minconf"},
{"sendmany", 4, "subtractfeefrom"},
{"deriveaddresses", 1, "range"},
{"scantxoutset", 1, "scanobjects"},
{"addmultisigaddress", 0, "nrequired"},
{"addmultisigaddress", 1, "keys"},
{"createmultisig", 0, "nrequired"},
{"createmultisig", 1, "keys"},
{"listunspent", 0, "minconf"},
{"listunspent", 1, "maxconf"},
{"listunspent", 2, "addresses"},
{"listunspent", 3, "include_unsafe"},
{"listunspent", 4, "query_options"},
{"getblock", 1, "verbosity"},
{"getblock", 1, "verbose"},
{"getblockheader", 1, "verbose"},
{"getchaintxstats", 0, "nblocks"},
{"gettransaction", 1, "include_watchonly"},
{"gettransaction", 2, "verbose"},
{"getrawtransaction", 1, "verbose"},
{"createrawtransaction", 0, "inputs"},
{"createrawtransaction", 1, "outputs"},
{"createrawtransaction", 2, "locktime"},
{"signrawtransactionwithkey", 1, "privkeys"},
{"signrawtransactionwithkey", 2, "prevtxs"},
{"signrawtransactionwithwallet", 1, "prevtxs"},
{"sendrawtransaction", 1, "maxfeerate"},
{"testmempoolaccept", 0, "rawtxs"},
{"testmempoolaccept", 1, "maxfeerate"},
{"combinerawtransaction", 0, "txs"},
{"fundrawtransaction", 1, "options"},
{"walletcreatefundedpsbt", 0, "inputs"},
{"walletcreatefundedpsbt", 1, "outputs"},
{"walletcreatefundedpsbt", 2, "locktime"},
{"walletcreatefundedpsbt", 3, "options"},
{"walletcreatefundedpsbt", 4, "bip32derivs"},
{"walletprocesspsbt", 1, "sign"},
{"walletprocesspsbt", 3, "bip32derivs"},
{"createpsbt", 0, "inputs"},
{"createpsbt", 1, "outputs"},
{"createpsbt", 2, "locktime"},
{"combinepsbt", 0, "txs"},
{"joinpsbts", 0, "txs"},
{"finalizepsbt", 1, "extract"},
{"converttopsbt", 1, "permitsigdata"},
{"gettxout", 1, "n"},
{"gettxout", 2, "include_mempool"},
{"gettxoutproof", 0, "txids"},
{"lockunspent", 0, "unlock"},
{"lockunspent", 1, "transactions"},
{"importprivkey", 2, "rescan"},
{"importaddress", 2, "rescan"},
{"importaddress", 3, "p2sh"},
{"importpubkey", 2, "rescan"},
{"importmulti", 0, "requests"},
{"importmulti", 1, "options"},
{"importdescriptors", 0, "requests"},
{"verifychain", 0, "checklevel"},
{"verifychain", 1, "nblocks"},
{"getblockstats", 0, "hash_or_height"},
{"getblockstats", 1, "stats"},
{"pruneblockchain", 0, "height"},
{"keypoolrefill", 0, "newsize"},
{"getrawmempool", 0, "verbose"},
{"prioritisetransaction", 1, "dummy"},
{"prioritisetransaction", 2, "fee_delta"},
{"setban", 2, "bantime"},
{"setban", 3, "absolute"},
{"setnetworkactive", 0, "state"},
{"setwalletflag", 1, "value"},
{"getmempoolancestors", 1, "verbose"},
{"getmempooldescendants", 1, "verbose"},
{"disconnectnode", 1, "nodeid"},
{"logging", 0, "include"},
{"logging", 1, "exclude"},
{"upgradewallet", 0, "version"},
// Echo with conversion (For testing only)
{"echojson", 0, "arg0"},
{"echojson", 1, "arg1"},
{"echojson", 2, "arg2"},
{"echojson", 3, "arg3"},
{"echojson", 4, "arg4"},
{"echojson", 5, "arg5"},
{"echojson", 6, "arg6"},
{"echojson", 7, "arg7"},
{"echojson", 8, "arg8"},
{"echojson", 9, "arg9"},
{"rescanblockchain", 0, "start_height"},
{"rescanblockchain", 1, "stop_height"},
{"createwallet", 1, "disable_private_keys"},
{"createwallet", 2, "blank"},
{"createwallet", 4, "avoid_reuse"},
{"createwallet", 5, "descriptors"},
+ {"createwallet", 6, "load_on_startup"},
+ {"loadwallet", 1, "load_on_startup"},
+ {"unloadwallet", 1, "load_on_startup"},
{"getnodeaddresses", 0, "count"},
{"addpeeraddress", 1, "port"},
{"stop", 0, "wait"},
// Avalanche
{"addavalanchenode", 0, "nodeid"},
{"buildavalancheproof", 0, "sequence"},
{"buildavalancheproof", 1, "expiration"},
{"buildavalancheproof", 3, "stakes"},
};
class CRPCConvertTable {
private:
std::set> members;
std::set> membersByName;
public:
CRPCConvertTable();
bool convert(const std::string &method, int idx) {
return (members.count(std::make_pair(method, idx)) > 0);
}
bool convert(const std::string &method, const std::string &name) {
return (membersByName.count(std::make_pair(method, name)) > 0);
}
};
CRPCConvertTable::CRPCConvertTable() {
const unsigned int n_elem =
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0]));
for (unsigned int i = 0; i < n_elem; i++) {
members.insert(std::make_pair(vRPCConvertParams[i].methodName,
vRPCConvertParams[i].paramIdx));
membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName,
vRPCConvertParams[i].paramName));
}
}
static CRPCConvertTable rpcCvtTable;
/**
* Non-RFC4627 JSON parser, accepts internal values (such as numbers, true,
* false, null) as well as objects and arrays.
*/
UniValue ParseNonRFCJSONValue(const std::string &strVal) {
UniValue jVal;
if (!jVal.read(std::string("[") + strVal + std::string("]")) ||
!jVal.isArray() || jVal.size() != 1) {
throw std::runtime_error(std::string("Error parsing JSON: ") + strVal);
}
return jVal[0];
}
UniValue RPCConvertValues(const std::string &strMethod,
const std::vector &strParams) {
UniValue params(UniValue::VARR);
for (unsigned int idx = 0; idx < strParams.size(); idx++) {
const std::string &strVal = strParams[idx];
if (!rpcCvtTable.convert(strMethod, idx)) {
// insert string value directly
params.push_back(strVal);
} else {
// parse string as JSON, insert bool/number/object/etc. value
params.push_back(ParseNonRFCJSONValue(strVal));
}
}
return params;
}
UniValue RPCConvertNamedValues(const std::string &strMethod,
const std::vector &strParams) {
UniValue params(UniValue::VOBJ);
for (const std::string &s : strParams) {
size_t pos = s.find('=');
if (pos == std::string::npos) {
throw(std::runtime_error("No '=' in named argument '" + s +
"', this needs to be present for every "
"argument (even if it is empty)"));
}
std::string name = s.substr(0, pos);
std::string value = s.substr(pos + 1);
if (!rpcCvtTable.convert(strMethod, name)) {
// insert string value directly
params.pushKV(name, value);
} else {
// parse string as JSON, insert bool/number/object/etc. value
params.pushKV(name, ParseNonRFCJSONValue(value));
}
}
return params;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 283f50bbe..9c45c8944 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -1,212 +1,221 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin Core developers
// Copyright (c) 2018-2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
class WalletInit : public WalletInitInterface {
public:
//! Was the wallet component compiled in.
bool HasWalletSupport() const override { return true; }
//! Return the wallets help message.
void AddWalletOptions(ArgsManager &argsman) const override;
//! Wallets parameter interaction
bool ParameterInteraction() const override;
//! Add wallets that should be opened to list of chain clients.
void Construct(NodeContext &node) const override;
};
const WalletInitInterface &g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions(ArgsManager &argsman) const {
argsman.AddArg(
"-avoidpartialspends",
strprintf("Group outputs by address, selecting all or none, instead of "
"selecting on a per-output basis. Privacy is improved as an "
"address is only used once (unless someone sends to it after "
"spending from it), but may result in slightly higher fees "
"as suboptimal coin selection may result due to the added "
"limitation (default: %u (always enabled for wallets with "
"\"avoid_reuse\" enabled))",
DEFAULT_AVOIDPARTIALSPENDS),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-disablewallet",
"Do not load the wallet and disable wallet RPC calls",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
const auto &ticker = Currency::get().ticker;
argsman.AddArg("-fallbackfee=",
strprintf("A fee rate (in %s/kB) that will be used when fee "
"estimation has insufficient data. 0 to entirely "
"disable the fallbackfee feature. (default: %s)",
ticker, FormatMoney(DEFAULT_FALLBACK_FEE)),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-keypool=",
strprintf("Set key pool size to (default: %u). Warning: Smaller "
"sizes may increase the risk of losing funds when restoring "
"from an old backup, if none of the addresses in the "
"original keypool have been used.",
DEFAULT_KEYPOOL_SIZE),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-maxapsfee=",
strprintf(
"Spend up to this amount in additional (absolute) fees (in %s) if "
"it allows the use of partial spend avoidance (default: %s)",
ticker, FormatMoney(DEFAULT_MAX_AVOIDPARTIALSPEND_FEE)),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-maxtxfee=",
strprintf("Maximum total fees (in %s) to use in a single wallet "
"transaction or raw transaction; setting this too low may "
"abort large transactions (default: %s)",
ticker, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)),
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-mintxfee=",
strprintf("Fees (in %s/kB) smaller than this are considered "
"zero fee for transaction creation (default: %s)",
ticker,
FormatMoney(DEFAULT_TRANSACTION_MINFEE_PER_KB)),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-paytxfee=",
strprintf(
"Fee (in %s/kB) to add to transactions you send (default: %s)",
ticker, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-rescan",
"Rescan the block chain for missing wallet transactions on startup",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-spendzeroconfchange",
strprintf(
"Spend unconfirmed change when sending transactions (default: %d)",
DEFAULT_SPEND_ZEROCONF_CHANGE),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg(
"-wallet=",
"Specify wallet database path. Can be specified multiple "
"times to load multiple wallets. Path is interpreted relative "
"to if it is not absolute, and will be created if "
"it does not exist (as a directory containing a wallet.dat "
"file and log files). For backwards compatibility this will "
"also accept names of existing data files in .)",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::WALLET);
argsman.AddArg(
"-walletbroadcast",
strprintf("Make the wallet broadcast transactions (default: %d)",
DEFAULT_WALLETBROADCAST),
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletdir=",
"Specify directory to hold wallets (default: "
"/wallets if it exists, otherwise )",
ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY,
OptionsCategory::WALLET);
#if defined(HAVE_SYSTEM)
argsman.AddArg(
"-walletnotify=",
"Execute command when a wallet transaction changes. %s in cmd "
"is replaced by TxID and %w is replaced by wallet name. %w is "
"not currently implemented on windows. On systems where %w is "
"supported, it should NOT be quoted because this would break "
"shell escaping used to invoke the command.",
ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg(
"-dblogsize=",
strprintf("Flush wallet database activity from memory to disk "
"log every megabytes (default: %u)",
DEFAULT_WALLET_DBLOGSIZE),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg(
"-flushwallet",
strprintf("Run a thread to flush wallet periodically (default: %d)",
DEFAULT_FLUSHWALLET),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-privdb",
strprintf("Sets the DB_PRIVATE flag in the wallet db "
"environment (default: %d)",
DEFAULT_WALLET_PRIVDB),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-walletrejectlongchains",
strprintf("Wallet will not create transactions that violate "
"mempool chain limits (default: %d)",
DEFAULT_WALLET_REJECT_LONG_CHAINS),
ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY,
OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddHiddenArgs({"-zapwallettxes"});
}
bool WalletInit::ParameterInteraction() const {
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
for (const std::string &wallet : gArgs.GetArgs("-wallet")) {
LogPrintf("%s: parameter interaction: -disablewallet -> ignoring "
"-wallet=%s\n",
__func__, wallet);
}
return true;
}
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) &&
gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-walletbroadcast=0\n",
__func__);
}
if (gArgs.IsArgSet("-zapwallettxes")) {
return InitError(
Untranslated("-zapwallettxes has been removed. If you are "
"attempting to remove a stuck transaction from your "
"wallet, please use abandontransaction instead."));
}
if (gArgs.GetBoolArg("-sysperms", false)) {
return InitError(
Untranslated("-sysperms is not allowed in combination with enabled "
"wallet functionality"));
}
return true;
}
void WalletInit::Construct(NodeContext &node) const {
ArgsManager &args = *Assert(node.args);
if (args.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
return;
}
- args.SoftSetArg("-wallet", "");
+ // If there's no -wallet setting with a list of wallets to load, set it to
+ // load the default "" wallet.
+ if (!args.IsArgSet("wallet")) {
+ args.LockSettings([&](util::Settings &settings) {
+ util::SettingsValue wallets(util::SettingsValue::VARR);
+ wallets.push_back(""); // Default wallet name is ""
+ settings.rw_settings["wallet"] = wallets;
+ });
+ }
auto wallet_client = interfaces::MakeWalletClient(*node.chain, args,
args.GetArgs("-wallet"));
node.wallet_client = wallet_client.get();
node.chain_clients.emplace_back(std::move(wallet_client));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index 04bac7d35..8001d50da 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -1,144 +1,179 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include
#include
#include
#include
#include
#include
#include
+#include
+
bool VerifyWallets(interfaces::Chain &chain,
const std::vector &wallet_files) {
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
boost::system::error_code error;
// The canonical path cleans the path, preventing >1 Berkeley
// environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) {
chain.initError(
strprintf(_("Specified -walletdir \"%s\" does not exist"),
wallet_dir.string()));
return false;
} else if (!fs::is_directory(wallet_dir)) {
chain.initError(
strprintf(_("Specified -walletdir \"%s\" is not a directory"),
wallet_dir.string()));
return false;
// The canonical path transforms relative paths into absolute ones,
// so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
chain.initError(
strprintf(_("Specified -walletdir \"%s\" is a relative path"),
wallet_dir.string()));
return false;
}
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
}
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
chain.initMessage(_("Verifying wallet(s)...").translated);
// Keep track of each wallet absolute path to detect duplicates.
std::set wallet_paths;
for (const auto &wallet_file : wallet_files) {
WalletLocation location(wallet_file);
if (!wallet_paths.insert(location.GetPath()).second) {
chain.initError(strprintf(_("Error loading wallet %s. Duplicate "
"-wallet filename specified."),
wallet_file));
return false;
}
bilingual_str error_string;
std::vector warnings;
bool verify_success =
CWallet::Verify(chain, location, error_string, warnings);
if (!warnings.empty()) {
chain.initWarning(Join(warnings, Untranslated("\n")));
}
if (!verify_success) {
chain.initError(error_string);
return false;
}
}
return true;
}
bool LoadWallets(interfaces::Chain &chain,
const std::vector &wallet_files) {
try {
for (const std::string &walletFile : wallet_files) {
bilingual_str error;
std::vector warnings;
std::shared_ptr pwallet = CWallet::CreateWalletFromFile(
chain, WalletLocation(walletFile), error, warnings);
if (!warnings.empty()) {
chain.initWarning(Join(warnings, Untranslated("\n")));
}
if (!pwallet) {
chain.initError(error);
return false;
}
AddWallet(pwallet);
}
return true;
} catch (const std::runtime_error &e) {
chain.initError(Untranslated(e.what()));
return false;
}
}
void StartWallets(CScheduler &scheduler, const ArgsManager &args) {
for (const std::shared_ptr &pwallet : GetWallets()) {
pwallet->postInitProcess();
}
// Schedule periodic wallet flushes and tx rebroadcasts
if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
scheduler.scheduleEvery(
[] {
MaybeCompactWalletDB();
return true;
},
std::chrono::milliseconds{500});
}
scheduler.scheduleEvery(
[] {
MaybeResendWalletTxs();
return true;
},
std::chrono::milliseconds{1000});
}
void FlushWallets() {
for (const std::shared_ptr &pwallet : GetWallets()) {
pwallet->Flush();
}
}
void StopWallets() {
for (const std::shared_ptr &pwallet : GetWallets()) {
pwallet->Close();
}
}
void UnloadWallets() {
auto wallets = GetWallets();
while (!wallets.empty()) {
auto wallet = wallets.back();
wallets.pop_back();
RemoveWallet(wallet);
UnloadWallet(std::move(wallet));
}
}
+
+bool AddWalletSetting(interfaces::Chain &chain,
+ const std::string &wallet_name) {
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) {
+ setting_value.setArray();
+ }
+ for (const util::SettingsValue &value : setting_value.getValues()) {
+ if (value.isStr() && value.get_str() == wallet_name) {
+ return true;
+ }
+ }
+ setting_value.push_back(wallet_name);
+ return chain.updateRwSetting("wallet", setting_value);
+}
+
+bool RemoveWalletSetting(interfaces::Chain &chain,
+ const std::string &wallet_name) {
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) {
+ return true;
+ }
+ util::SettingsValue new_value(util::SettingsValue::VARR);
+ for (const util::SettingsValue &value : setting_value.getValues()) {
+ if (!value.isStr() || value.get_str() != wallet_name) {
+ new_value.push_back(value);
+ }
+ }
+ if (new_value.size() == setting_value.size()) {
+ return true;
+ }
+ return chain.updateRwSetting("wallet", new_value);
+}
diff --git a/src/wallet/load.h b/src/wallet/load.h
index dd6893e16..78dbf28f7 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -1,40 +1,48 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_LOAD_H
#define BITCOIN_WALLET_LOAD_H
#include
#include
class ArgsManager;
class CScheduler;
namespace interfaces {
class Chain;
} // namespace interfaces
//! Responsible for reading and validating the -wallet arguments and verifying
//! the wallet database.
bool VerifyWallets(interfaces::Chain &chain,
const std::vector &wallet_files);
//! Load wallet databases.
bool LoadWallets(interfaces::Chain &chain,
const std::vector &wallet_files);
//! Complete startup of wallets.
void StartWallets(CScheduler &scheduler, const ArgsManager &args);
//! Flush all wallets in preparation for shutdown.
void FlushWallets();
//! Stop all wallets. Wallets will be flushed first.
void StopWallets();
//! Close all wallets.
void UnloadWallets();
+//! Add wallet name to persistent configuration so it will be loaded on startup.
+bool AddWalletSetting(interfaces::Chain &chain, const std::string &wallet_name);
+
+//! Remove wallet name from persistent configuration so it will not be loaded on
+//! startup.
+bool RemoveWalletSetting(interfaces::Chain &chain,
+ const std::string &wallet_name);
+
#endif // BITCOIN_WALLET_LOAD_H
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 96b56ef24..ae42e78f8 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,4983 +1,5030 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include
#include // for GetConsensus.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include