Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcdump.cpp
Show First 20 Lines • Show All 1,056 Lines • ▼ Show 20 Lines | UniValue dumpwallet(const Config &config, const JSONRPCRequest &request) { | ||||
return reply; | return reply; | ||||
} | } | ||||
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) { | ||||
try { | try { | ||||
bool success = false; | // First ensure scriptPubKey has either a script or JSON with "address" | ||||
// string | |||||
// Required fields. | |||||
const UniValue &scriptPubKey = data["scriptPubKey"]; | const UniValue &scriptPubKey = data["scriptPubKey"]; | ||||
bool isScript = scriptPubKey.getType() == UniValue::VSTR; | |||||
// Should have script or JSON with "address". | if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && | ||||
if (!(scriptPubKey.getType() == UniValue::VOBJ && | scriptPubKey.exists("address"))) { | ||||
scriptPubKey.exists("address")) && | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
!(scriptPubKey.getType() == UniValue::VSTR)) { | "scriptPubKey must be string with script or " | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey"); | "JSON with address string"); | ||||
} | } | ||||
const std::string &output = isScript | |||||
? scriptPubKey.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 = | const bool internal = | ||||
data.exists("internal") ? data["internal"].get_bool() : false; | 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 = | const std::string &label = | ||||
data.exists("label") && !internal ? data["label"].get_str() : ""; | 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"); | ||||
} | } | ||||
bool isScript = scriptPubKey.getType() == UniValue::VSTR; | // Generate the script and destination for the scriptPubKey provided | ||||
bool isP2SH = strRedeemScript.length() > 0; | |||||
const std::string &output = isScript | |||||
? scriptPubKey.get_str() | |||||
: scriptPubKey["address"].get_str(); | |||||
// Parse the output. | |||||
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"); | "Invalid address"); | ||||
Show All 13 Lines | try { | ||||
"nonstandard scriptPubKey imports."); | "nonstandard scriptPubKey imports."); | ||||
} | } | ||||
} | } | ||||
// Watchonly and private keys | // Watchonly and private keys | ||||
if (watchOnly && keys.size()) { | if (watchOnly && keys.size()) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_INVALID_PARAMETER, | RPC_INVALID_PARAMETER, | ||||
"Incompatibility found between watchonly and keys"); | "Watch-only addresses should not include private keys"); | ||||
} | } | ||||
// Internal + Label | // Internal addresses should not have a label | ||||
if (internal && data.exists("label")) { | if (internal && data.exists("label")) { | ||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
"Incompatibility found between internal and label"); | |||||
} | |||||
// Keys / PubKeys size check. | |||||
if (!isP2SH && | |||||
(keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"More than private key given for one address"); | "Internal addresses should not have a label"); | ||||
} | } | ||||
// Invalid P2SH redeemScript | CScript scriptpubkey_script = script; | ||||
if (isP2SH && !IsHex(strRedeemScript)) { | CTxDestination scriptpubkey_dest = dest; | ||||
// P2SH | |||||
if (!strRedeemScript.empty() && script.IsPayToScriptHash()) { | |||||
// Check the redeemScript is valid | |||||
if (!IsHex(strRedeemScript)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid redeem script"); | "Invalid redeem script: must be hex string"); | ||||
} | } | ||||
// Process. // | |||||
// P2SH | |||||
if (isP2SH) { | |||||
// Import redeem script. | // Import redeem script. | ||||
std::vector<uint8_t> vData(ParseHex(strRedeemScript)); | std::vector<uint8_t> vData(ParseHex(strRedeemScript)); | ||||
CScript redeemScript = CScript(vData.begin(), vData.end()); | CScript redeemScript = CScript(vData.begin(), vData.end()); | ||||
CScriptID redeem_id(redeemScript); | |||||
// Invalid P2SH address | // Check that the redeemScript and scriptPubKey match | ||||
if (!script.IsPayToScriptHash()) { | if (GetScriptForDestination(redeem_id) != script) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError( | ||||
"Invalid P2SH address / script"); | RPC_INVALID_ADDRESS_OR_KEY, | ||||
"The redeemScript does not match the scriptPubKey"); | |||||
} | } | ||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
if (!pwallet->AddWatchOnly(redeemScript, timestamp)) { | if (!pwallet->AddWatchOnly(redeemScript, timestamp)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
CScriptID redeem_id(redeemScript); | |||||
if (!pwallet->HaveCScript(redeem_id) && | if (!pwallet->HaveCScript(redeem_id) && | ||||
!pwallet->AddCScript(redeemScript)) { | !pwallet->AddCScript(redeemScript)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding p2sh redeemScript to wallet"); | "Error adding p2sh redeemScript to wallet"); | ||||
} | } | ||||
CScript redeemDestination = GetScriptForDestination(redeem_id); | |||||
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"The wallet already contains the private " | |||||
"key for this address or script"); | |||||
} | |||||
pwallet->MarkDirty(); | |||||
if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding address to wallet"); | |||||
} | |||||
// if not internal add to address book or update label | |||||
if (!internal) { | |||||
assert(IsValidDestination(dest)); | |||||
pwallet->SetAddressBook(dest, label, "receive"); | |||||
} | } | ||||
// Import private keys. | // (P2SH-)P2PK/P2PKH | ||||
if (dest.type() == typeid(CKeyID)) { | |||||
CPubKey pubkey; | |||||
if (keys.size()) { | if (keys.size()) { | ||||
for (size_t i = 0; i < keys.size(); i++) { | pubkey = DecodeSecret(keys[0].get_str()).GetPubKey(); | ||||
const std::string &privkey = keys[i].get_str(); | |||||
CKey key = DecodeSecret(privkey); | |||||
if (!key.IsValid()) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Invalid private key encoding"); | |||||
} | |||||
CPubKey pubkey = key.GetPubKey(); | |||||
assert(key.VerifyPubKey(pubkey)); | |||||
CKeyID vchAddress = pubkey.GetID(); | |||||
pwallet->MarkDirty(); | |||||
pwallet->SetAddressBook(vchAddress, label, "receive"); | |||||
if (pwallet->HaveKey(vchAddress)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Already have this key"); | |||||
} | |||||
pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; | |||||
if (!pwallet->AddKeyPubKey(key, pubkey)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding key to wallet"); | |||||
} | |||||
pwallet->UpdateTimeFirstKey(timestamp); | |||||
} | } | ||||
} | if (pubKeys.size()) { | ||||
success = true; | |||||
} else { | |||||
// Import public keys. | |||||
if (pubKeys.size() && keys.size() == 0) { | |||||
const std::string &strPubKey = pubKeys[0].get_str(); | const std::string &strPubKey = pubKeys[0].get_str(); | ||||
if (!IsHex(strPubKey)) { | if (!IsHex(strPubKey)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Pubkey must be a hex string"); | "Pubkey must be a hex string"); | ||||
} | } | ||||
std::vector<uint8_t> vData(ParseHex(pubKeys[0].get_str())); | |||||
std::vector<uint8_t> vData(ParseHex(strPubKey)); | CPubKey pubkey_temp(vData.begin(), vData.end()); | ||||
CPubKey pubKey(vData.begin(), vData.end()); | if (pubkey.size() && pubkey_temp != pubkey) { | ||||
throw JSONRPCError( | |||||
if (!pubKey.IsFullyValid()) { | RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Private key does not match public key for address"); | |||||
} | |||||
pubkey = pubkey_temp; | |||||
} | |||||
if (pubkey.size() > 0) { | |||||
if (!pubkey.IsFullyValid()) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Pubkey is not a valid public key"); | "Pubkey is not a valid public key"); | ||||
} | } | ||||
CTxDestination pubkey_dest = pubKey.GetID(); | // Check the key corresponds to the destination given | ||||
std::vector<CTxDestination> destinations = | |||||
// Consistency check. | GetAllDestinationsForKey(pubkey); | ||||
if (!(pubkey_dest == dest)) { | if (std::find(destinations.begin(), destinations.end(), dest) == | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | destinations.end()) { | ||||
"Consistency check failed"); | throw JSONRPCError( | ||||
RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Key does not match address destination"); | |||||
} | } | ||||
CScript pubKeyScript = GetScriptForDestination(pubkey_dest); | // This is necessary to force the wallet to import the pubKey | ||||
CScript scriptRawPubKey = GetScriptForRawPubKey(pubkey); | |||||
if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { | if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " | throw JSONRPCError( | ||||
"contains the private " | RPC_WALLET_ERROR, | ||||
"key for this address " | "The wallet already contains the private key for this " | ||||
"or script"); | "address or script"); | ||||
} | } | ||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) { | if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
} | |||||
// add to address book or update label | |||||
if (IsValidDestination(pubkey_dest)) { | |||||
pwallet->SetAddressBook(pubkey_dest, label, "receive"); | |||||
} | } | ||||
// TODO Is this necessary? | // Import the address | ||||
CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey); | if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) { | "The wallet already contains the private key " | ||||
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " | "for this address or script"); | ||||
"contains the private " | |||||
"key for this address " | |||||
"or script"); | |||||
} | } | ||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) { | if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
success = true; | if (!watchOnly && | ||||
!pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && | |||||
!pwallet->AddCScript(scriptpubkey_script)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding scriptPubKey script to wallet"); | |||||
} | |||||
// if not internal add to address book or update label | |||||
if (!internal) { | |||||
if (IsValidDestination(scriptpubkey_dest)) { | |||||
pwallet->SetAddressBook(scriptpubkey_dest, label, "receive"); | |||||
} | |||||
} | } | ||||
// Import private keys. | // Import private keys. | ||||
if (keys.size()) { | for (size_t i = 0; i < keys.size(); i++) { | ||||
const std::string &strPrivkey = keys[0].get_str(); | const std::string &strPrivkey = keys[i].get_str(); | ||||
// Checks. | // Checks. | ||||
CKey key = DecodeSecret(strPrivkey); | CKey key = DecodeSecret(strPrivkey); | ||||
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(); | ||||
assert(key.VerifyPubKey(pubKey)); | assert(key.VerifyPubKey(pubKey)); | ||||
CTxDestination pubkey_dest = pubKey.GetID(); | |||||
// Consistency check. | |||||
if (!(pubkey_dest == dest)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Consistency check failed"); | |||||
} | |||||
CKeyID vchAddress = pubKey.GetID(); | CKeyID vchAddress = pubKey.GetID(); | ||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
pwallet->SetAddressBook(vchAddress, label, "receive"); | |||||
if (pwallet->HaveKey(vchAddress)) { | if (pwallet->HaveKey(vchAddress)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"contains the private " | "Already have this key"); | ||||
"key for this address " | |||||
"or script"); | |||||
} | } | ||||
pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; | pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; | ||||
if (!pwallet->AddKeyPubKey(key, pubKey)) { | if (!pwallet->AddKeyPubKey(key, pubKey)) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding key to wallet"); | "Error adding key to wallet"); | ||||
} | } | ||||
pwallet->UpdateTimeFirstKey(timestamp); | pwallet->UpdateTimeFirstKey(timestamp); | ||||
success = true; | |||||
} | |||||
// Import scriptPubKey only. | |||||
if (pubKeys.size() == 0 && keys.size() == 0) { | |||||
if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already " | |||||
"contains the private " | |||||
"key for this address " | |||||
"or script"); | |||||
} | |||||
pwallet->MarkDirty(); | |||||
if (!pwallet->AddWatchOnly(script, timestamp)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding address to wallet"); | |||||
} | |||||
if (!internal) { | |||||
// add to address book or update label | |||||
if (IsValidDestination(dest)) { | |||||
pwallet->SetAddressBook(dest, label, "receive"); | |||||
} | |||||
} | |||||
success = true; | |||||
} | |||||
} | } | ||||
UniValue result = UniValue(UniValue::VOBJ); | UniValue result = UniValue(UniValue::VOBJ); | ||||
result.pushKV("success", UniValue(success)); | result.pushKV("success", UniValue(true)); | ||||
return result; | return result; | ||||
} catch (const UniValue &e) { | } catch (const UniValue &e) { | ||||
UniValue result = UniValue(UniValue::VOBJ); | UniValue result = UniValue(UniValue::VOBJ); | ||||
result.pushKV("success", UniValue(false)); | result.pushKV("success", UniValue(false)); | ||||
result.pushKV("error", e); | result.pushKV("error", e); | ||||
return result; | return result; | ||||
} catch (...) { | } catch (...) { | ||||
UniValue result = UniValue(UniValue::VOBJ); | UniValue result = UniValue(UniValue::VOBJ); | ||||
▲ Show 20 Lines • Show All 322 Lines • Show Last 20 Lines |