aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpcdump.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/rpcdump.cpp')
-rw-r--r--src/wallet/rpcdump.cpp156
1 files changed, 91 insertions, 65 deletions
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 7552722a8e..930274f8a1 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -13,6 +13,7 @@
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
+#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
@@ -116,9 +117,9 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
{
- {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key (see dumpprivkey)"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "current label if address exists, otherwise \"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ {"privkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The private key (see dumpprivkey)"},
+ {"label", RPCArg::Type::STR, /* default */ "current label if address exists, otherwise \"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
},
RPCResults{},
RPCExamples{
@@ -286,10 +287,10 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nNote: If you import a non-standard raw script in hex form, outputs sending to it will be treated\n"
"as change, and not show up in many RPCs.\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The Bitcoin address (or hex-encoded script)"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
- {"p2sh", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "false", "Add the P2SH version of the script as well"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"},
+ {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
+ {"p2sh", RPCArg::Type::BOOL, /* default */ "false", "Add the P2SH version of the script as well"},
},
RPCResults{},
RPCExamples{
@@ -364,8 +365,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
- {"rawtransaction", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "A raw transaction in hex funding an already-existing address in wallet"},
- {"txoutproof", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex output from gettxoutproof that contains the transaction"},
+ {"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
+ {"txoutproof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex output from gettxoutproof that contains the transaction"},
},
RPCResults{},
RPCExamples{""},
@@ -431,7 +432,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
- {"txid", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded id of the transaction you are deleting"},
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
},
RPCResults{},
RPCExamples{
@@ -475,9 +476,9 @@ UniValue importpubkey(const JSONRPCRequest& request)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n",
{
- {"pubkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The hex-encoded public key"},
- {"label", RPCArg::Type::STR, /* opt */ true, /* default_val */ "\"\"", "An optional label"},
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Rescan the wallet for transactions"},
+ {"pubkey", RPCArg::Type::STR, RPCArg::Optional::NO, "The hex-encoded public key"},
+ {"label", RPCArg::Type::STR, /* default */ "\"\"", "An optional label"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Rescan the wallet for transactions"},
},
RPCResults{},
RPCExamples{
@@ -548,7 +549,7 @@ UniValue importwallet(const JSONRPCRequest& request)
RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
{
- {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The wallet file"},
+ {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet file"},
},
RPCResults{},
RPCExamples{
@@ -709,7 +710,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
- {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address for the private key"},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
},
RPCResult{
"\"key\" (string) The private key\n"
@@ -759,7 +760,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
"Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
"only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
{
- {"filename", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The filename with path (either absolute or relative to bitcoind)"},
+ {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to bitcoind)"},
},
RPCResult{
"{ (json object)\n"
@@ -850,7 +851,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
} else {
file << "change=1";
}
- file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0 ? " hdkeypath="+pwallet->mapKeyMetadata[keyid].hdKeypath : ""));
+ file << strprintf(" # addr=%s%s\n", strAddr, (pwallet->mapKeyMetadata[keyid].has_key_origin ? " hdkeypath="+WriteHDKeypath(pwallet->mapKeyMetadata[keyid].key_origin.path) : ""));
}
}
file << "\n";
@@ -887,6 +888,7 @@ struct ImportData
// Output data
std::set<CScript> import_scripts;
std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
+ std::map<CKeyID, KeyOriginInfo> key_origins;
};
enum class ScriptContext
@@ -965,7 +967,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
}
-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)
+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, std::vector<CKeyID>& ordered_pubkeys)
{
UniValue warnings(UniValue::VARR);
@@ -1036,6 +1038,7 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + str + "\" is not a valid public key");
}
pubkey_map.emplace(pubkey.GetID(), pubkey);
+ ordered_pubkeys.push_back(pubkey.GetID());
}
for (size_t i = 0; i < keys.size(); ++i) {
const auto& str = keys[i].get_str();
@@ -1108,7 +1111,7 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
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)
+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, std::vector<CKeyID>& ordered_pubkeys)
{
UniValue warnings(UniValue::VARR);
@@ -1142,21 +1145,24 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
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) {
+ FlatSigningProvider out_keys;
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& key_pair : out_keys.pubkeys) {
+ ordered_pubkeys.push_back(key_pair.first);
+ }
- for (const auto& x : out_keys.scripts) {
- import_data.import_scripts.emplace(x.second);
- }
+ 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()));
+ std::copy(out_keys.pubkeys.begin(), out_keys.pubkeys.end(), std::inserter(pubkey_map, pubkey_map.end()));
+ import_data.key_origins.insert(out_keys.origins.begin(), out_keys.origins.end());
+ }
for (size_t i = 0; i < priv_keys.size(); ++i) {
const auto& str = priv_keys[i].get_str();
@@ -1205,19 +1211,26 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
const std::string& label = data.exists("label") ? data["label"].get_str() : "";
+ const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
+
+ // Add to keypool only works with privkeys disabled
+ if (add_keypool && !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
+ }
ImportData import_data;
std::map<CKeyID, CPubKey> pubkey_map;
std::map<CKeyID, CKey> privkey_map;
std::set<CScript> script_pub_keys;
+ std::vector<CKeyID> ordered_pubkeys;
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);
+ warnings = ProcessImportLegacy(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
} else if (data.exists("desc")) {
- warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
+ warnings = ProcessImportDescriptor(import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data, ordered_pubkeys);
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Either a descriptor or scriptPubKey must be provided.");
}
@@ -1239,27 +1252,40 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
for (const auto& entry : import_data.import_scripts) {
if (!pwallet->HaveCScript(CScriptID(entry)) && !pwallet->AddCScript(entry)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
+ }
+ }
+ for (const auto& entry : privkey_map) {
+ const CKey& key = entry.second;
+ CPubKey pubkey = key.GetPubKey();
+ const CKeyID& id = entry.first;
+ assert(key.VerifyPubKey(pubkey));
+ pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
+ // If the private key is not present in the wallet, insert it.
+ if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
+ }
+ pwallet->UpdateTimeFirstKey(timestamp);
+ }
+ for (const CKeyID& id : ordered_pubkeys) {
+ auto entry = pubkey_map.find(id);
+ if (entry == pubkey_map.end()) {
+ continue;
}
- }
- for (const auto& entry : privkey_map) {
- const CKey& key = entry.second;
- CPubKey pubkey = key.GetPubKey();
- const CKeyID& id = entry.first;
- assert(key.VerifyPubKey(pubkey));
- pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
- // If the private key is not present in the wallet, insert it.
- if (!pwallet->HaveKey(id) && !pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
- pwallet->UpdateTimeFirstKey(timestamp);
- }
- for (const auto& entry : pubkey_map) {
- const CPubKey& pubkey = entry.second;
- const CKeyID& id = entry.first;
- CPubKey temp;
- if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
+ const CPubKey& pubkey = entry->second;
+ CPubKey temp;
+ if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
+ const auto& key_orig_it = import_data.key_origins.find(id);
+ if (key_orig_it != import_data.key_origins.end()) {
+ pwallet->AddKeyOrigin(pubkey, key_orig_it->second);
+ }
+ pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
+
+ // Add to keypool only works with pubkeys
+ if (add_keypool) {
+ pwallet->AddKeypoolPubkey(pubkey, internal);
+ }
}
for (const CScript& script : script_pub_keys) {
@@ -1319,15 +1345,15 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n",
{
- {"requests", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "Data to be imported",
+ {"requests", RPCArg::Type::ARR, RPCArg::Optional::NO, "Data to be imported",
{
- {"", RPCArg::Type::OBJ, /* opt */ false, /* default_val */ "", "",
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
- {"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",
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys"},
+ {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "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"
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
" or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
" key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
@@ -1335,34 +1361,34 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" creation time of all keys being imported by the importmulti call will be scanned.",
/* oneline_description */ "", {"timestamp | \"now\"", "integer / string"}
},
- {"redeemscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
- {"witnessscript", RPCArg::Type::STR, /* opt */ true, /* default_val */ "omitted", "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
- {"pubkeys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
+ {"redeemscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey"},
+ {"witnessscript", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey"},
+ {"pubkeys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving pubkeys to import. They must occur in P2PKH or P2WPKH scripts. They are not required when the private key is also provided (see the \"keys\" argument).",
{
- {"pubKey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ {"pubKey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"keys", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
+ {"keys", RPCArg::Type::ARR, /* default */ "empty array", "Array of strings giving private keys to import. The corresponding public keys must occur in the output or redeemscript.",
{
- {"key", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", ""},
+ {"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"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",
+ {"range", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "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)"},
+ {"start", RPCArg::Type::NUM, /* default */ "0", "Start of the range to import"},
+ {"end", RPCArg::Type::NUM, RPCArg::Optional::NO, "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"},
+ {"internal", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
+ {"watchonly", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be considered watchonly."},
+ {"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
},
},
},
"\"requests\""},
- {"options", RPCArg::Type::OBJ, /* opt */ true, /* default_val */ "null", "",
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"rescan", RPCArg::Type::BOOL, /* opt */ true, /* default_val */ "true", "Stating if should rescan the blockchain after all imports"},
+ {"rescan", RPCArg::Type::BOOL, /* default */ "true", "Stating if should rescan the blockchain after all imports"},
},
"\"options\""},
},