Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcdump.cpp
Show First 20 Lines • Show All 1,133 Lines • ▼ Show 20 Lines | switch (script_type) { | ||||
case TX_NULL_DATA: | case TX_NULL_DATA: | ||||
return "unspendable script"; | return "unspendable script"; | ||||
case TX_NONSTANDARD: | case TX_NONSTANDARD: | ||||
default: | default: | ||||
return "unrecognized script"; | return "unrecognized script"; | ||||
} | } | ||||
} | } | ||||
static UniValue ProcessImportLegacy(ImportData &import_data, | static UniValue ProcessImportLegacy(CWallet *const pwallet, | ||||
ImportData &import_data, | |||||
std::map<CKeyID, CPubKey> &pubkey_map, | std::map<CKeyID, CPubKey> &pubkey_map, | ||||
std::map<CKeyID, CKey> &privkey_map, | std::map<CKeyID, CKey> &privkey_map, | ||||
std::set<CScript> &script_pub_keys, | std::set<CScript> &script_pub_keys, | ||||
bool &have_solving_data, | bool &have_solving_data, | ||||
const UniValue &data) { | const UniValue &data) { | ||||
UniValue warnings(UniValue::VARR); | UniValue warnings(UniValue::VARR); | ||||
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() : ""; | |||||
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; | |||||
warnings = | |||||
ProcessImportLegacy(import_data, pubkey_map, privkey_map, | |||||
script_pub_keys, have_solving_data, data); | |||||
// First ensure scriptPubKey has either a script or JSON with "address" | // First ensure scriptPubKey has either a script or JSON with "address" | ||||
// string | // string | ||||
const UniValue &scriptPubKey = data["scriptPubKey"]; | const UniValue &scriptPubKey = data["scriptPubKey"]; | ||||
bool isScript = scriptPubKey.getType() == UniValue::VSTR; | bool isScript = scriptPubKey.getType() == UniValue::VSTR; | ||||
if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && | if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && | ||||
scriptPubKey.exists("address"))) { | scriptPubKey.exists("address"))) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"scriptPubKey must be string with script or " | "scriptPubKey must be string with script or JSON " | ||||
"JSON with address string"); | "with address string"); | ||||
} | } | ||||
const std::string &output = isScript | const std::string &output = | ||||
? scriptPubKey.get_str() | isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str(); | ||||
: scriptPubKey["address"].get_str(); | |||||
// Optional fields. | // Optional fields. | ||||
const std::string &strRedeemScript = | const std::string &strRedeemScript = | ||||
data.exists("redeemscript") ? data["redeemscript"].get_str() : ""; | data.exists("redeemscript") ? data["redeemscript"].get_str() : ""; | ||||
const UniValue &pubKeys = | const UniValue &pubKeys = | ||||
data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue(); | data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue(); | ||||
const UniValue &keys = | const UniValue &keys = | ||||
data.exists("keys") ? data["keys"].get_array() : UniValue(); | data.exists("keys") ? data["keys"].get_array() : UniValue(); | ||||
const bool internal = | |||||
data.exists("internal") ? data["internal"].get_bool() : false; | |||||
const bool watchOnly = | const bool watchOnly = | ||||
data.exists("watchonly") ? data["watchonly"].get_bool() : false; | data.exists("watchonly") ? data["watchonly"].get_bool() : false; | ||||
// 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"); | |||||
} | |||||
// Generate the script and destination for the scriptPubKey provided | // Generate the script and destination for the scriptPubKey provided | ||||
CScript script; | CScript script; | ||||
CTxDestination dest; | CTxDestination dest; | ||||
if (!isScript) { | if (!isScript) { | ||||
dest = DecodeDestination(output, pwallet->chainParams); | dest = DecodeDestination(output, pwallet->chainParams); | ||||
if (!IsValidDestination(dest)) { | if (!IsValidDestination(dest)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid address \"" + output + "\""); | "Invalid address \"" + output + "\""); | ||||
} | } | ||||
script = GetScriptForDestination(dest); | script = GetScriptForDestination(dest); | ||||
} else { | } else { | ||||
if (!IsHex(output)) { | if (!IsHex(output)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid scriptPubKey \"" + output + "\""); | "Invalid scriptPubKey \"" + output + "\""); | ||||
} | } | ||||
std::vector<uint8_t> vData(ParseHex(output)); | std::vector<uint8_t> vData(ParseHex(output)); | ||||
script = CScript(vData.begin(), vData.end()); | script = CScript(vData.begin(), vData.end()); | ||||
if (!ExtractDestination(script, dest) && !internal) { | if (!ExtractDestination(script, dest) && !internal) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Internal must be set to true for " | "Internal must be set to true for " | ||||
"nonstandard scriptPubKey imports."); | "nonstandard scriptPubKey imports."); | ||||
} | } | ||||
} | } | ||||
script_pub_keys.emplace(script); | script_pub_keys.emplace(script); | ||||
// Parse all arguments | // Parse all arguments | ||||
if (strRedeemScript.size()) { | if (strRedeemScript.size()) { | ||||
if (!IsHex(strRedeemScript)) { | if (!IsHex(strRedeemScript)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid redeem script \"" + | "Invalid redeem script \"" + strRedeemScript + | ||||
strRedeemScript + | |||||
"\": must be hex string"); | "\": must be hex string"); | ||||
} | } | ||||
auto parsed_redeemscript = ParseHex(strRedeemScript); | auto parsed_redeemscript = ParseHex(strRedeemScript); | ||||
import_data.redeemscript = std::make_unique<CScript>( | import_data.redeemscript = std::make_unique<CScript>( | ||||
parsed_redeemscript.begin(), parsed_redeemscript.end()); | parsed_redeemscript.begin(), parsed_redeemscript.end()); | ||||
} | } | ||||
for (size_t i = 0; i < pubKeys.size(); ++i) { | for (size_t i = 0; i < pubKeys.size(); ++i) { | ||||
const auto &str = pubKeys[i].get_str(); | const auto &str = pubKeys[i].get_str(); | ||||
if (!IsHex(str)) { | if (!IsHex(str)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Pubkey \"" + str + | "Pubkey \"" + str + "\" must be a hex string"); | ||||
"\" must be a hex string"); | |||||
} | } | ||||
auto parsed_pubkey = ParseHex(str); | auto parsed_pubkey = ParseHex(str); | ||||
CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end()); | CPubKey pubkey(parsed_pubkey.begin(), parsed_pubkey.end()); | ||||
if (!pubkey.IsFullyValid()) { | if (!pubkey.IsFullyValid()) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Pubkey \"" + str + | "Pubkey \"" + str + | ||||
"\" is not a valid public key"); | "\" is not a valid public key"); | ||||
} | } | ||||
pubkey_map.emplace(pubkey.GetID(), pubkey); | pubkey_map.emplace(pubkey.GetID(), pubkey); | ||||
} | } | ||||
for (size_t i = 0; i < keys.size(); ++i) { | for (size_t i = 0; i < keys.size(); ++i) { | ||||
const auto &str = keys[i].get_str(); | const auto &str = keys[i].get_str(); | ||||
CKey key = DecodeSecret(str); | CKey key = DecodeSecret(str); | ||||
if (!key.IsValid()) { | if (!key.IsValid()) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid private key encoding"); | "Invalid private key encoding"); | ||||
} | } | ||||
CPubKey pubkey = key.GetPubKey(); | CPubKey pubkey = key.GetPubKey(); | ||||
CKeyID id = pubkey.GetID(); | CKeyID id = pubkey.GetID(); | ||||
if (pubkey_map.count(id)) { | if (pubkey_map.count(id)) { | ||||
pubkey_map.erase(id); | pubkey_map.erase(id); | ||||
} | } | ||||
privkey_map.emplace(id, key); | privkey_map.emplace(id, key); | ||||
} | } | ||||
// Verify and process input data | // Verify and process input data | ||||
have_solving_data = | have_solving_data = | ||||
import_data.redeemscript || pubkey_map.size() || privkey_map.size(); | import_data.redeemscript || pubkey_map.size() || privkey_map.size(); | ||||
if (have_solving_data) { | if (have_solving_data) { | ||||
// Match up data in import_data with the scriptPubKey in script. | // Match up data in import_data with the scriptPubKey in script. | ||||
auto error = | auto error = RecurseImportData(script, import_data, ScriptContext::TOP); | ||||
RecurseImportData(script, import_data, ScriptContext::TOP); | |||||
// Verify whether the watchonly option corresponds to the | // Verify whether the watchonly option corresponds to the | ||||
// availability of private keys. | // availability of private keys. | ||||
bool spendable = std::all_of( | bool spendable = std::all_of( | ||||
import_data.used_keys.begin(), import_data.used_keys.end(), | import_data.used_keys.begin(), import_data.used_keys.end(), | ||||
[&](const std::pair<CKeyID, bool> &used_key) { | [&](const std::pair<CKeyID, bool> &used_key) { | ||||
return privkey_map.count(used_key.first) > 0; | return privkey_map.count(used_key.first) > 0; | ||||
}); | }); | ||||
if (!watchOnly && !spendable) { | if (!watchOnly && !spendable) { | ||||
warnings.push_back("Some private keys are missing, outputs " | warnings.push_back("Some private keys are missing, outputs " | ||||
"will be considered watchonly. If this is " | "will be considered watchonly. If this is " | ||||
"intentional, specify the watchonly flag."); | "intentional, specify the watchonly flag."); | ||||
} | } | ||||
if (watchOnly && spendable) { | if (watchOnly && spendable) { | ||||
warnings.push_back( | warnings.push_back( | ||||
"All private keys are provided, outputs will be considered " | "All private keys are provided, outputs will be considered " | ||||
"spendable. If this is intentional, do not specify the " | "spendable. If this is intentional, do not specify the " | ||||
"watchonly flag."); | "watchonly flag."); | ||||
} | } | ||||
// Check that all required keys for solvability are provided. | // Check that all required keys for solvability are provided. | ||||
if (error.empty()) { | if (error.empty()) { | ||||
for (const auto &require_key : import_data.used_keys) { | for (const auto &require_key : import_data.used_keys) { | ||||
if (!require_key.second) { | if (!require_key.second) { | ||||
// Not a required key | // Not a required key | ||||
continue; | continue; | ||||
} | } | ||||
if (pubkey_map.count(require_key.first) == 0 && | if (pubkey_map.count(require_key.first) == 0 && | ||||
privkey_map.count(require_key.first) == 0) { | privkey_map.count(require_key.first) == 0) { | ||||
error = "some required keys are missing"; | error = "some required keys are missing"; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!error.empty()) { | if (!error.empty()) { | ||||
warnings.push_back("Importing as non-solvable: " + error + | warnings.push_back("Importing as non-solvable: " + error + | ||||
". If this is intentional, don't provide " | ". If this is intentional, don't provide " | ||||
"any keys, pubkeys or redeemscript."); | "any keys, pubkeys or redeemscript."); | ||||
import_data = ImportData(); | import_data = ImportData(); | ||||
pubkey_map.clear(); | pubkey_map.clear(); | ||||
privkey_map.clear(); | privkey_map.clear(); | ||||
have_solving_data = false; | have_solving_data = false; | ||||
} else { | } else { | ||||
// RecurseImportData() removes any relevant redeemscript from | // RecurseImportData() removes any relevant redeemscript from | ||||
// import_data, so we can use that to discover if a superfluous | // import_data, so we can use that to discover if a superfluous | ||||
// one was provided. | // one was provided. | ||||
if (import_data.redeemscript) { | if (import_data.redeemscript) { | ||||
warnings.push_back( | warnings.push_back( | ||||
"Ignoring redeemscript as this is not a P2SH script."); | "Ignoring redeemscript as this is not a P2SH script."); | ||||
} | } | ||||
for (auto it = privkey_map.begin(); it != privkey_map.end();) { | for (auto it = privkey_map.begin(); it != privkey_map.end();) { | ||||
auto oldit = it++; | auto oldit = it++; | ||||
if (import_data.used_keys.count(oldit->first) == 0) { | if (import_data.used_keys.count(oldit->first) == 0) { | ||||
warnings.push_back("Ignoring irrelevant private key."); | warnings.push_back("Ignoring irrelevant private key."); | ||||
privkey_map.erase(oldit); | privkey_map.erase(oldit); | ||||
} | } | ||||
} | } | ||||
for (auto it = pubkey_map.begin(); it != pubkey_map.end();) { | for (auto it = pubkey_map.begin(); it != pubkey_map.end();) { | ||||
auto oldit = it++; | auto oldit = it++; | ||||
auto key_data_it = import_data.used_keys.find(oldit->first); | auto key_data_it = import_data.used_keys.find(oldit->first); | ||||
if (key_data_it == import_data.used_keys.end() || | if (key_data_it == import_data.used_keys.end() || | ||||
!key_data_it->second) { | !key_data_it->second) { | ||||
warnings.push_back( | warnings.push_back("Ignoring public key \"" + | ||||
"Ignoring public key \"" + HexStr(oldit->first) + | HexStr(oldit->first) + | ||||
"\" as it doesn't appear inside P2PKH."); | "\" as it doesn't appear inside P2PKH."); | ||||
pubkey_map.erase(oldit); | pubkey_map.erase(oldit); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
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() : ""; | |||||
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; | |||||
warnings = | |||||
ProcessImportLegacy(pwallet, import_data, pubkey_map, privkey_map, | |||||
script_pub_keys, have_solving_data, data); | |||||
// 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 | // Check whether we have any work to do | ||||
for (const CScript &s : script_pub_keys) { | for (const CScript &s : script_pub_keys) { | ||||
if (::IsMine(*pwallet, s) & ISMINE_SPENDABLE) { | if (::IsMine(*pwallet, s) & ISMINE_SPENDABLE) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"The wallet already contains the private " | "The wallet already contains the private " | ||||
"key for this address or script"); | "key for this address or script"); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 381 Lines • Show Last 20 Lines |