aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/init.cpp15
-rw-r--r--src/wallet/rpcdump.cpp394
-rw-r--r--src/wallet/rpcwallet.cpp18
-rw-r--r--src/wallet/test/wallet_tests.cpp33
-rw-r--r--src/wallet/wallet.cpp43
-rw-r--r--src/wallet/wallet.h20
-rw-r--r--src/wallet/wallettool.cpp1
8 files changed, 330 insertions, 202 deletions
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index ae40553268..c64a85c910 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -126,11 +126,18 @@ void BerkeleyEnvironment::Close()
}
}
+ FILE* error_file = nullptr;
+ dbenv->get_errfile(&error_file);
+
int ret = dbenv->close(0);
if (ret != 0)
LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
if (!fMockDb)
DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
+
+ if (error_file) fclose(error_file);
+
+ UnlockDirectory(strPath, ".walletlock");
}
void BerkeleyEnvironment::Reset()
@@ -147,6 +154,7 @@ BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir
BerkeleyEnvironment::~BerkeleyEnvironment()
{
+ LOCK(cs_db);
g_dbenvs.erase(strPath);
Close();
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 87cd264c3d..20d540c8db 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -127,21 +127,6 @@ bool WalletInit::ParameterInteraction() const
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
- if (gArgs.IsArgSet("-maxtxfee"))
- {
- CAmount nMaxFee = 0;
- if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
- if (nMaxFee > HIGH_MAX_TX_FEE)
- InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
- maxTxFee = nMaxFee;
- if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
- {
- return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
- }
- }
-
return true;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 0da2dda092..7552722a8e 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -9,6 +9,7 @@
#include <merkleblock.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
@@ -66,7 +67,7 @@ static std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
-static bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel)
+static bool GetWalletAddressesForKey(CWallet* const pwallet, const CKeyID& keyid, std::string& strAddr, std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
bool fLabelFound = false;
CKey key;
@@ -814,7 +815,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
const Optional<int> tip_height = locked_chain->getHeight();
- file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)");
+ file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.get_value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)");
file << strprintf("# mined on %s\n", tip_height ? FormatISO8601DateTime(locked_chain->getBlockTime(*tip_height)) : "(missing block time)");
file << "\n";
@@ -964,159 +965,273 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
}
-static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data)
{
UniValue warnings(UniValue::VARR);
- UniValue result(UniValue::VOBJ);
- try {
- // First ensure scriptPubKey has either a script or JSON with "address" string
- const UniValue& scriptPubKey = data["scriptPubKey"];
- bool isScript = scriptPubKey.getType() == UniValue::VSTR;
- if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
+ // First ensure scriptPubKey has either a script or JSON with "address" string
+ const UniValue& scriptPubKey = data["scriptPubKey"];
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
+ }
+ const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+
+ // Optional fields.
+ const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
+ const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
+ const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
+ const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
+ const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
+
+ if (data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for a non-descriptor import");
+ }
+
+ // Generate the script and destination for the scriptPubKey provided
+ CScript script;
+ if (!isScript) {
+ CTxDestination dest = DecodeDestination(output);
+ if (!IsValidDestination(dest)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
+ }
+ script = GetScriptForDestination(dest);
+ } else {
+ if (!IsHex(output)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
+ }
+ std::vector<unsigned char> vData(ParseHex(output));
+ script = CScript(vData.begin(), vData.end());
+ CTxDestination dest;
+ if (!ExtractDestination(script, dest) && !internal) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
}
- const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
+ }
+ script_pub_keys.emplace(script);
- // Optional fields.
- const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
- const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
- const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
- const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
- const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
- const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
- const std::string& label = data.exists("label") ? data["label"].get_str() : "";
+ // Parse all arguments
+ if (strRedeemScript.size()) {
+ if (!IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
+ }
+ auto parsed_redeemscript = ParseHex(strRedeemScript);
+ import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
+ }
+ if (witness_script_hex.size()) {
+ if (!IsHex(witness_script_hex)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
+ }
+ auto parsed_witnessscript = ParseHex(witness_script_hex);
+ import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ }
+ for (size_t i = 0; i < pubKeys.size(); ++i) {
+ const auto& str = pubKeys[i].get_str();
+ if (!IsHex(str)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
+ }
+ auto parsed_pubkey = ParseHex(str);
+ CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
+ }
+ pubkey_map.emplace(pubkey.GetID(), pubkey);
+ }
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const auto& str = keys[i].get_str();
+ CKey key = DecodeSecret(str);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+ CPubKey pubkey = key.GetPubKey();
+ CKeyID id = pubkey.GetID();
+ if (pubkey_map.count(id)) {
+ pubkey_map.erase(id);
+ }
+ privkey_map.emplace(id, key);
+ }
- // If private keys are disabled, abort if private keys are being imported
- if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.isNull()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
+
+ // Verify and process input data
+ have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
+ if (have_solving_data) {
+ // Match up data in import_data with the scriptPubKey in script.
+ auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
+
+ // Verify whether the watchonly option corresponds to the availability of private keys.
+ bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
+ if (!watchOnly && !spendable) {
+ warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
+ }
+ if (watchOnly && spendable) {
+ warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
}
- // Generate the script and destination for the scriptPubKey provided
- CScript script;
- CTxDestination dest;
- if (!isScript) {
- dest = DecodeDestination(output);
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address \"" + output + "\"");
+ // Check that all required keys for solvability are provided.
+ if (error.empty()) {
+ for (const auto& require_key : import_data.used_keys) {
+ if (!require_key.second) continue; // Not a required key
+ if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
+ error = "some required keys are missing";
+ }
}
- script = GetScriptForDestination(dest);
+ }
+
+ if (!error.empty()) {
+ warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
+ import_data = ImportData();
+ pubkey_map.clear();
+ privkey_map.clear();
+ have_solving_data = false;
} else {
- if (!IsHex(output)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey \"" + output + "\"");
+ // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
+ if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
+ if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
+ for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
+ auto oldit = it++;
+ if (import_data.used_keys.count(oldit->first) == 0) {
+ warnings.push_back("Ignoring irrelevant private key.");
+ privkey_map.erase(oldit);
+ }
}
- std::vector<unsigned char> vData(ParseHex(output));
- script = CScript(vData.begin(), vData.end());
- if (!ExtractDestination(script, dest) && !internal) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
+ for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
+ auto oldit = it++;
+ auto key_data_it = import_data.used_keys.find(oldit->first);
+ if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
+ warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
+ pubkey_map.erase(oldit);
+ }
}
}
+ }
- // Parse all arguments
- ImportData import_data;
- if (strRedeemScript.size()) {
- if (!IsHex(strRedeemScript)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script \"" + strRedeemScript + "\": must be hex string");
- }
- auto parsed_redeemscript = ParseHex(strRedeemScript);
- import_data.redeemscript = MakeUnique<CScript>(parsed_redeemscript.begin(), parsed_redeemscript.end());
+ return warnings;
+}
+
+static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool& have_solving_data, const UniValue& data)
+{
+ UniValue warnings(UniValue::VARR);
+
+ const std::string& descriptor = data["desc"].get_str();
+ FlatSigningProvider keys;
+ auto parsed_desc = Parse(descriptor, keys);
+ if (!parsed_desc) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor is invalid");
+ }
+
+ have_solving_data = parsed_desc->IsSolvable();
+ const bool watch_only = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
+
+ int64_t range_start = 0, range_end = 0;
+ if (!parsed_desc->IsRange() && data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
+ } else if (parsed_desc->IsRange()) {
+ if (!data.exists("range")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
}
- if (witness_script_hex.size()) {
- if (!IsHex(witness_script_hex)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script \"" + witness_script_hex + "\": must be hex string");
- }
- auto parsed_witnessscript = ParseHex(witness_script_hex);
- import_data.witnessscript = MakeUnique<CScript>(parsed_witnessscript.begin(), parsed_witnessscript.end());
+ const UniValue& range = data["range"];
+ range_start = range.exists("start") ? range["start"].get_int64() : 0;
+ if (!range.exists("end")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range for descriptor must be specified");
}
- std::map<CKeyID, CPubKey> pubkey_map;
- for (size_t i = 0; i < pubKeys.size(); ++i) {
- const auto& str = pubKeys[i].get_str();
- if (!IsHex(str)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" must be a hex string");
- }
- auto parsed_pubkey = ParseHex(str);
- CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end());
- if (!pubkey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
- }
- pubkey_map.emplace(pubkey.GetID(), pubkey);
+ range_end = range["end"].get_int64();
+ if (range_end < range_start || range_start < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid descriptor range specified");
}
- std::map<CKeyID, CKey> privkey_map;
- for (size_t i = 0; i < keys.size(); ++i) {
- const auto& str = keys[i].get_str();
- CKey key = DecodeSecret(str);
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
- CPubKey pubkey = key.GetPubKey();
- CKeyID id = pubkey.GetID();
- if (pubkey_map.count(id)) {
- pubkey_map.erase(id);
- }
+ }
+
+ const UniValue& priv_keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
+
+ FlatSigningProvider out_keys;
+
+ // Expand all descriptors to get public keys and scripts.
+ // TODO: get private keys from descriptors too
+ for (int i = range_start; i <= range_end; ++i) {
+ std::vector<CScript> scripts_temp;
+ parsed_desc->Expand(i, keys, scripts_temp, out_keys);
+ std::copy(scripts_temp.begin(), scripts_temp.end(), std::inserter(script_pub_keys, script_pub_keys.end()));
+ }
+
+ for (const auto& x : out_keys.scripts) {
+ import_data.import_scripts.emplace(x.second);
+ }
+
+ std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
+
+ for (size_t i = 0; i < priv_keys.size(); ++i) {
+ const auto& str = priv_keys[i].get_str();
+ CKey key = DecodeSecret(str);
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
+ }
+ CPubKey pubkey = key.GetPubKey();
+ CKeyID id = pubkey.GetID();
+
+ // Check if this private key corresponds to a public key from the descriptor
+ if (!pubkey_map.count(id)) {
+ warnings.push_back("Ignoring irrelevant private key.");
+ } else {
privkey_map.emplace(id, key);
}
+ }
+
+ // Check if all the public keys have corresponding private keys in the import for spendability.
+ // This does not take into account threshold multisigs which could be spendable without all keys.
+ // Thus, threshold multisigs without all keys will be considered not spendable here, even if they are,
+ // perhaps triggering a false warning message. This is consistent with the current wallet IsMine check.
+ bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
+ [&](const std::pair<CKeyID, CPubKey>& used_key) {
+ return privkey_map.count(used_key.first) > 0;
+ });
+ if (!watch_only && !spendable) {
+ warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
+ }
+ if (watch_only && spendable) {
+ warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
+ }
+
+ return warnings;
+}
+
+static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+{
+ UniValue warnings(UniValue::VARR);
+ UniValue result(UniValue::VOBJ);
+ try {
+ const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
// Internal addresses should not have a label
if (internal && data.exists("label")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
+ const std::string& label = data.exists("label") ? data["label"].get_str() : "";
- // Verify and process input data
- bool have_solving_data = import_data.redeemscript || import_data.witnessscript || pubkey_map.size() || privkey_map.size();
- if (have_solving_data) {
- // Match up data in import_data with the scriptPubKey in script.
- auto error = RecurseImportData(script, import_data, ScriptContext::TOP);
-
- // Verify whether the watchonly option corresponds to the availability of private keys.
- bool spendable = std::all_of(import_data.used_keys.begin(), import_data.used_keys.end(), [&](const std::pair<CKeyID, bool>& used_key){ return privkey_map.count(used_key.first) > 0; });
- if (!watchOnly && !spendable) {
- warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
- }
- if (watchOnly && spendable) {
- warnings.push_back("All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag.");
- }
-
- // Check that all required keys for solvability are provided.
- if (error.empty()) {
- for (const auto& require_key : import_data.used_keys) {
- if (!require_key.second) continue; // Not a required key
- if (pubkey_map.count(require_key.first) == 0 && privkey_map.count(require_key.first) == 0) {
- error = "some required keys are missing";
- }
- }
- }
+ ImportData import_data;
+ std::map<CKeyID, CPubKey> pubkey_map;
+ std::map<CKeyID, CKey> privkey_map;
+ std::set<CScript> script_pub_keys;
+ bool have_solving_data;
+
+ if (data.exists("scriptPubKey") && data.exists("desc")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Both a descriptor and a scriptPubKey should not be provided.");
+ } else if (data.exists("scriptPubKey")) {
+ warnings = ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
+ } else if (data.exists("desc")) {
+ warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Either a descriptor or scriptPubKey must be provided.");
+ }
- if (!error.empty()) {
- warnings.push_back("Importing as non-solvable: " + error + ". If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.");
- import_data = ImportData();
- pubkey_map.clear();
- privkey_map.clear();
- have_solving_data = false;
- } else {
- // RecurseImportData() removes any relevant redeemscript/witnessscript from import_data, so we can use that to discover if a superfluous one was provided.
- if (import_data.redeemscript) warnings.push_back("Ignoring redeemscript as this is not a P2SH script.");
- if (import_data.witnessscript) warnings.push_back("Ignoring witnessscript as this is not a (P2SH-)P2WSH script.");
- for (auto it = privkey_map.begin(); it != privkey_map.end(); ) {
- auto oldit = it++;
- if (import_data.used_keys.count(oldit->first) == 0) {
- warnings.push_back("Ignoring irrelevant private key.");
- privkey_map.erase(oldit);
- }
- }
- for (auto it = pubkey_map.begin(); it != pubkey_map.end(); ) {
- auto oldit = it++;
- auto key_data_it = import_data.used_keys.find(oldit->first);
- if (key_data_it == import_data.used_keys.end() || !key_data_it->second) {
- warnings.push_back("Ignoring public key \"" + HexStr(oldit->first) + "\" as it doesn't appear inside P2PKH or P2WPKH.");
- pubkey_map.erase(oldit);
- }
- }
- }
+ // If private keys are disabled, abort if private keys are being imported
+ if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
// Check whether we have any work to do
- if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ for (const CScript& script : script_pub_keys) {
+ if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script.begin(), script.end()) + "\")");
+ }
}
// All good, time to import
@@ -1146,14 +1261,18 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
}
- if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+
+ for (const CScript& script : script_pub_keys) {
+ if (!have_solving_data || !::IsMine(*pwallet, script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated
+ if (!pwallet->AddWatchOnly(script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
+ }
+ CTxDestination dest;
+ ExtractDestination(script, dest);
+ if (!internal && IsValidDestination(dest)) {
+ pwallet->SetAddressBook(dest, label, "receive");
}
- }
- if (!internal) {
- assert(IsValidDestination(dest));
- pwallet->SetAddressBook(dest, label, "receive");
}
result.pushKV("success", UniValue(true));
@@ -1204,7 +1323,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
{
{"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
{
- {"scriptPubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Type of scriptPubKey (string for script, json for address)",
+ {"desc", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
+ {"scriptPubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor",
/* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"}
},
{"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
@@ -1227,6 +1347,12 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
{"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
}
},
+ {"range", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "", "If a ranged descriptor is used, this specifies the start and end of the range to import",
+ {
+ {"start", RPCArg::Type::NUM, /* opt */ true, /* default_val */ "0", "Start of the range to import"},
+ {"end", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "End of the range to import (inclusive)"},
+ }
+ },
{"internal", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
{"watchonly", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Stating whether matching outputs should be considered watchonly."},
{"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "''", "Label to assign to the address, only allowed with internal=false"},
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 674c10879f..9bbbdc6132 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1320,7 +1320,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -3044,7 +3044,8 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
- "The inputs added will not be signed, use signrawtransaction for that.\n"
+ "The inputs added will not be signed, use signrawtransactionwithkey\n"
+ " or signrawtransactionwithwallet for that.\n"
"Note that all existing inputs must have their previous output transaction be in the wallet.\n"
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
@@ -3093,7 +3094,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nAdd sufficient unsigned inputs to meet the output value\n"
+ HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
"\nSign the transaction\n"
- + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
+ + HelpExampleCli("signrawtransactionwithwallet", "\"fundedtransactionhex\"") +
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
@@ -3428,8 +3429,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
},
RPCResult{
"{\n"
- " \"start_height\" (numeric) The block height where the rescan has started.\n"
- " \"stop_height\" (numeric) The height of the last rescanned block.\n"
+ " \"start_height\" (numeric) The block height where the rescan started (the requested height or 0)\n"
+ " \"stop_height\" (numeric) The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background.\n"
"}\n"
},
RPCExamples{
@@ -3475,6 +3476,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (tip_height) {
start_block = locked_chain->getBlockHash(start_height);
+ // If called with a stop_height, set the stop_height here to
+ // trigger a rescan to that height.
+ // If called without a stop height, leave stop_height as null here
+ // so rescan continues to the tip (even if the tip advances during
+ // rescan).
if (stop_height) {
stop_block = locked_chain->getBlockHash(*stop_height);
}
@@ -3494,7 +3500,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", start_height);
- response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue());
+ response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
return response;
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 1d4eae73a4..e674b2faea 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -44,6 +44,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = chainActive.Tip();
+ LockAnnotation lock(::cs_main);
auto locked_chain = chain->lock();
// Verify ScanForWalletTransactions accommodates a null start block.
@@ -54,9 +55,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK(result.failed_block.IsNull());
- BOOST_CHECK(result.stop_block.IsNull());
- BOOST_CHECK(!result.stop_height);
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ BOOST_CHECK(result.last_scanned_block.IsNull());
+ BOOST_CHECK(!result.last_scanned_height);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
}
@@ -69,9 +70,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK(result.failed_block.IsNull());
- BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
+ BOOST_CHECK(result.last_failed_block.IsNull());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
@@ -88,9 +89,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
- BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash());
- BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
+ BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
@@ -106,9 +107,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
- BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash());
- BOOST_CHECK(result.stop_block.IsNull());
- BOOST_CHECK(!result.stop_height);
+ BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
+ BOOST_CHECK(result.last_scanned_block.IsNull());
+ BOOST_CHECK(!result.last_scanned_height);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
}
}
@@ -123,6 +124,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = chainActive.Tip();
+ LockAnnotation lock(::cs_main);
auto locked_chain = chain->lock();
// Prune the older block file.
@@ -268,6 +270,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
+ LockAnnotation lock(::cs_main);
auto locked_chain = wallet.chain().lock();
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
@@ -345,9 +348,9 @@ public:
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
- BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash());
- BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height());
- BOOST_CHECK(result.failed_block.IsNull());
+ BOOST_CHECK_EQUAL(result.last_scanned_block, chainActive.Tip()->GetBlockHash());
+ BOOST_CHECK_EQUAL(*result.last_scanned_height, chainActive.Height());
+ BOOST_CHECK(result.last_failed_block.IsNull());
}
~ListCoinsTestingSetup()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 8201b58e0e..d38c15220f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1646,7 +1646,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update);
if (result.status == ScanResult::FAILURE) {
int64_t time_max;
- if (!chain().findBlock(result.failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
+ if (!chain().findBlock(result.last_failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
throw std::logic_error("ScanForWalletTransactions returned invalid block hash");
}
return time_max + TIMESTAMP_WINDOW + 1;
@@ -1660,15 +1660,17 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
- * @param[in] start_block if not null, the scan will start at this block instead
- * of the genesis block
- * @param[in] stop_block if not null, the scan will stop at this block instead
- * of the chain tip
+ * @param[in] start_block Scan starting block. If block is not on the active
+ * chain, the scan will return SUCCESS immediately.
+ * @param[in] stop_block Scan ending block. If block is not on the active
+ * chain, the scan will continue until it reaches the
+ * chain tip.
*
- * @return ScanResult indicating success or failure of the scan. SUCCESS if
- * scan was successful. FAILURE if a complete rescan was not possible (due to
- * pruning or corruption). USER_ABORT if the rescan was aborted before it
- * could complete.
+ * @return ScanResult returning scan information and indicating success or
+ * failure. Return status will be set to SUCCESS if scan was
+ * successful. FAILURE if a complete rescan was not possible (due to
+ * pruning or corruption). USER_ABORT if the rescan was aborted before
+ * it could complete.
*
* @pre Caller needs to make sure start_block (and the optional stop_block) are on
* the main chain after to the addition of any new keys you want to detect
@@ -1721,7 +1723,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
// marking transactions as coming from the wrong block.
// TODO: This should return success instead of failure, see
// https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518
- result.failed_block = block_hash;
+ result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
break;
}
@@ -1729,11 +1731,11 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
- result.stop_block = block_hash;
- result.stop_height = *block_height;
+ result.last_scanned_block = block_hash;
+ result.last_scanned_height = *block_height;
} else {
// could not scan block, keep scanning but record this block as the most recent failure
- result.failed_block = block_hash;
+ result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
}
if (block_hash == stop_block) {
@@ -1763,10 +1765,10 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
if (block_height && fAbortRescan) {
- WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height.value_or(0), progress_current);
+ WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
result.status = ScanResult::USER_ABORT;
} else if (block_height && ShutdownRequested()) {
- WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height.value_or(0), progress_current);
+ WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", *block_height, progress_current);
result.status = ScanResult::USER_ABORT;
}
}
@@ -2627,7 +2629,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain)
*/
static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_chain)
{
- uint32_t const height = locked_chain.getHeight().value_or(-1);
+ uint32_t const height = locked_chain.getHeight().get_value_or(-1);
uint32_t locktime;
// Discourage fee sniping.
//
@@ -3214,7 +3216,7 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
{
bool fUpdated = false;
{
- LOCK(cs_wallet); // mapAddressBook
+ LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);
fUpdated = mi != mapAddressBook.end();
mapAddressBook[address].name = strName;
@@ -3231,7 +3233,7 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
{
- LOCK(cs_wallet); // mapAddressBook
+ LOCK(cs_wallet);
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
@@ -3915,7 +3917,6 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st
std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
{
- LOCK(cs_wallet);
std::vector<std::string> values;
for (const auto& address : mapAddressBook) {
for (const auto& data : address.second.destdata) {
@@ -4134,7 +4135,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup
- walletInstance->ChainStateFlushed(locked_chain->getLocator());
+ walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
@@ -4281,7 +4282,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
- walletInstance->ChainStateFlushed(locked_chain->getLocator());
+ walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
walletInstance->database->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 0f7f3bab18..c455b7cdad 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -800,7 +800,7 @@ public:
int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
uint64_t nAccountingEntryNumber = 0;
- std::map<CTxDestination, CAddressBookData> mapAddressBook;
+ std::map<CTxDestination, CAddressBookData> mapAddressBook GUARDED_BY(cs_wallet);
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
@@ -877,15 +877,15 @@ public:
bool LoadCScript(const CScript& redeemScript);
//! Adds a destination data tuple to the store, and saves it to disk
- bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
+ bool AddDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Erases a destination data tuple in the store and on disk
- bool EraseDestData(const CTxDestination &dest, const std::string &key);
+ bool EraseDestData(const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a destination data tuple to the store, without saving it to disk
- void LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
+ void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Look up a destination data tuple in the store, return true if found false otherwise
- bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
+ bool GetDestData(const CTxDestination& dest, const std::string& key, std::string* value) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Get all destination values matching a prefix.
- std::vector<std::string> GetDestValues(const std::string& prefix) const;
+ std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -924,14 +924,14 @@ public:
//! Hash and height of most recent block that was successfully scanned.
//! Unset if no blocks were scanned due to read errors or the chain
//! being empty.
- uint256 stop_block;
- Optional<int> stop_height;
+ uint256 last_scanned_block;
+ Optional<int> last_scanned_height;
//! Height of the most recent block that could not be scanned due to
//! read errors or pruning. Will be set if status is FAILURE, unset if
//! status is SUCCESS, and may or may not be set if status is
//! USER_ABORT.
- uint256 failed_block;
+ uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
@@ -1053,7 +1053,7 @@ public:
bool DelAddressBook(const CTxDestination& address);
- const std::string& GetLabelName(const CScript& scriptPubKey) const;
+ const std::string& GetLabelName(const CScript& scriptPubKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 30b0c48eef..628f3fe803 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -94,7 +94,6 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
static void WalletShowInfo(CWallet* wallet_instance)
{
- // lock required because of some AssertLockHeld()
LOCK(wallet_instance->cs_wallet);
fprintf(stdout, "Wallet info\n===========\n");