Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcdump.cpp
Show First 20 Lines • Show All 1,132 Lines • ▼ Show 20 Lines | |||||
static UniValue ProcessImport(CWallet *const pwallet, const UniValue &data, | static UniValue ProcessImport(CWallet *const pwallet, const UniValue &data, | ||||
const int64_t timestamp) | const int64_t timestamp) | ||||
EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { | EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { | ||||
UniValue warnings(UniValue::VARR); | UniValue warnings(UniValue::VARR); | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
try { | 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; | |||||
// 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 with address string"); | "JSON with address string"); | ||||
} | } | ||||
const std::string &output = isScript | const std::string &output = isScript | ||||
? scriptPubKey.get_str() | ? 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; | ||||
const std::string &label = | |||||
data.exists("label") && !internal ? data["label"].get_str() : ""; | |||||
// If private keys are disabled, abort if private keys are being | // If private keys are disabled, abort if private keys are being | ||||
// imported | // imported | ||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | ||||
!keys.isNull()) { | !keys.isNull()) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Cannot import private keys to a wallet with " | "Cannot import private keys to a wallet with " | ||||
"private keys disabled"); | "private keys disabled"); | ||||
Show All 17 Lines | try { | ||||
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); | |||||
// Parse all arguments | // Parse all arguments | ||||
ImportData import_data; | |||||
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()); | ||||
} | } | ||||
std::map<CKeyID, CPubKey> pubkey_map; | |||||
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); | ||||
} | } | ||||
std::map<CKeyID, CKey> privkey_map; | |||||
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); | ||||
} | } | ||||
// Internal addresses should not have a label | |||||
if (internal && data.exists("label")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"Internal addresses should not have a label"); | |||||
} | |||||
// Verify and process input data | // Verify and process input data | ||||
bool 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. | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | try { | ||||
"\" as it doesn't appear inside P2PKH or P2WPKH."); | "\" as it doesn't appear inside P2PKH or P2WPKH."); | ||||
pubkey_map.erase(oldit); | pubkey_map.erase(oldit); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Check whether we have any work to do | // Check whether we have any work to do | ||||
if (::IsMine(*pwallet, script) & ISMINE_SPENDABLE) { | for (const CScript &s : script_pub_keys) { | ||||
if (::IsMine(*pwallet, s) & ISMINE_SPENDABLE) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"The wallet already contains the private key " | "The wallet already contains the private " | ||||
"for this address or script"); | "key for this address or script"); | ||||
} | |||||
} | } | ||||
// All good, time to import | // All good, time to import | ||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
for (const auto &entry : import_data.import_scripts) { | for (const auto &entry : import_data.import_scripts) { | ||||
if (!pwallet->HaveCScript(CScriptID(entry)) && | if (!pwallet->HaveCScript(CScriptID(entry)) && | ||||
!pwallet->AddCScript(entry)) { | !pwallet->AddCScript(entry)) { | ||||
Show All 20 Lines | try { | ||||
CPubKey temp; | CPubKey temp; | ||||
if (!pwallet->GetPubKey(id, temp) && | if (!pwallet->GetPubKey(id, temp) && | ||||
!pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), | !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), | ||||
timestamp)) { | timestamp)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
} | } | ||||
if (!have_solving_data || !::IsMine(*pwallet, script)) { | |||||
for (const CScript &s : script_pub_keys) { | |||||
if (!have_solving_data || !::IsMine(*pwallet, s)) { | |||||
// Always call AddWatchOnly for non-solvable watch-only, so that | // Always call AddWatchOnly for non-solvable watch-only, so that | ||||
// watch timestamp gets updated | // watch timestamp gets updated | ||||
if (!pwallet->AddWatchOnly(script, timestamp)) { | if (!pwallet->AddWatchOnly(s, timestamp)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
} | } | ||||
CTxDestination d; | |||||
ExtractDestination(s, d); | |||||
if (!internal) { | if (!internal) { | ||||
assert(IsValidDestination(dest)); | assert(IsValidDestination(d)); | ||||
pwallet->SetAddressBook(dest, label, "receive"); | pwallet->SetAddressBook(d, label, "receive"); | ||||
} | |||||
} | } | ||||
result.pushKV("success", UniValue(true)); | result.pushKV("success", UniValue(true)); | ||||
} catch (const UniValue &e) { | } catch (const UniValue &e) { | ||||
result.pushKV("success", UniValue(false)); | result.pushKV("success", UniValue(false)); | ||||
result.pushKV("error", e); | result.pushKV("error", e); | ||||
} catch (...) { | } catch (...) { | ||||
result.pushKV("success", UniValue(false)); | result.pushKV("success", UniValue(false)); | ||||
▲ Show 20 Lines • Show All 322 Lines • Show Last 20 Lines |