Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcdump.cpp
Show First 20 Lines • Show All 1,052 Lines • ▼ Show 20 Lines | UniValue dumpwallet(const Config &config, const JSONRPCRequest &request) { | ||||
file.close(); | file.close(); | ||||
UniValue reply(UniValue::VOBJ); | UniValue reply(UniValue::VOBJ); | ||||
reply.pushKV("filename", filepath.string()); | reply.pushKV("filename", filepath.string()); | ||||
return reply; | return reply; | ||||
} | } | ||||
struct ImportData { | |||||
// Input data | |||||
//! Provided redeemScript; will be moved to `import_scripts` if relevant. | |||||
std::unique_ptr<CScript> redeemscript; | |||||
// Output data | |||||
std::set<CScript> import_scripts; | |||||
//! Import these private keys if available (the value indicates whether if | |||||
//! the key is required for solvability) | |||||
std::map<CKeyID, bool> used_keys; | |||||
}; | |||||
enum class ScriptContext { | |||||
//! Top-level scriptPubKey | |||||
TOP, | |||||
//! P2SH redeemScript | |||||
P2SH, | |||||
}; | |||||
// Analyse the provided scriptPubKey, determining which keys and which redeem | |||||
// scripts from the ImportData struct are needed to spend it, and mark them as | |||||
// used. Returns an error string, or the empty string for success. | |||||
static std::string RecurseImportData(const CScript &script, | |||||
ImportData &import_data, | |||||
const ScriptContext script_ctx) { | |||||
// Use Solver to obtain script type and parsed pubkeys or hashes: | |||||
std::vector<std::vector<uint8_t>> solverdata; | |||||
txnouttype script_type = Solver(script, solverdata); | |||||
switch (script_type) { | |||||
case TX_PUBKEY: { | |||||
CPubKey pubkey(solverdata[0].begin(), solverdata[0].end()); | |||||
import_data.used_keys.emplace(pubkey.GetID(), false); | |||||
return ""; | |||||
} | |||||
case TX_PUBKEYHASH: { | |||||
CKeyID id = CKeyID(uint160(solverdata[0])); | |||||
import_data.used_keys[id] = true; | |||||
return ""; | |||||
} | |||||
case TX_SCRIPTHASH: { | |||||
if (script_ctx == ScriptContext::P2SH) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Trying to nest P2SH inside another P2SH"); | |||||
} | |||||
assert(script_ctx == ScriptContext::TOP); | |||||
CScriptID id = CScriptID(uint160(solverdata[0])); | |||||
// Remove redeemscript from import_data to check for superfluous | |||||
// script later. | |||||
auto subscript = std::move(import_data.redeemscript); | |||||
if (!subscript) { | |||||
return "missing redeemscript"; | |||||
} | |||||
if (CScriptID(*subscript) != id) { | |||||
return "redeemScript does not match the scriptPubKey"; | |||||
} | |||||
import_data.import_scripts.emplace(*subscript); | |||||
return RecurseImportData(*subscript, import_data, | |||||
ScriptContext::P2SH); | |||||
} | |||||
case TX_MULTISIG: { | |||||
for (size_t i = 1; i + 1 < solverdata.size(); ++i) { | |||||
CPubKey pubkey(solverdata[i].begin(), solverdata[i].end()); | |||||
import_data.used_keys.emplace(pubkey.GetID(), false); | |||||
} | |||||
return ""; | |||||
} | |||||
case TX_NULL_DATA: | |||||
return "unspendable script"; | |||||
case TX_NONSTANDARD: | |||||
default: | |||||
return "unrecognized script"; | |||||
} | |||||
} | |||||
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 result(UniValue::VOBJ); | |||||
try { | try { | ||||
// 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, | ||||
Show All 25 Lines | try { | ||||
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"); | ||||
} | } | ||||
// 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"); | "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"); | "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."); | ||||
} | } | ||||
} | } | ||||
// Watchonly and private keys | // Parse all arguments | ||||
if (watchOnly && keys.size()) { | ImportData import_data; | ||||
throw JSONRPCError( | if (strRedeemScript.size()) { | ||||
RPC_INVALID_PARAMETER, | if (!IsHex(strRedeemScript)) { | ||||
"Watch-only addresses should not include private keys"); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid redeem script \"" + | |||||
strRedeemScript + | |||||
"\": must be hex string"); | |||||
} | |||||
auto parsed_redeemscript = ParseHex(strRedeemScript); | |||||
import_data.redeemscript = std::make_unique<CScript>( | |||||
parsed_redeemscript.begin(), parsed_redeemscript.end()); | |||||
} | |||||
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); | |||||
} | |||||
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); | |||||
} | |||||
privkey_map.emplace(id, key); | |||||
} | } | ||||
// Internal addresses should not have a label | // Internal addresses should not have a label | ||||
if (internal && data.exists("label")) { | if (internal && data.exists("label")) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Internal addresses should not have a label"); | "Internal addresses should not have a label"); | ||||
} | } | ||||
CScript scriptpubkey_script = script; | // Verify and process input data | ||||
CTxDestination scriptpubkey_dest = dest; | bool have_solving_data = | ||||
import_data.redeemscript || pubkey_map.size() || privkey_map.size(); | |||||
// P2SH | if (have_solving_data) { | ||||
if (!strRedeemScript.empty() && script.IsPayToScriptHash()) { | // Match up data in import_data with the scriptPubKey in script. | ||||
// Check the redeemScript is valid | auto error = | ||||
if (!IsHex(strRedeemScript)) { | RecurseImportData(script, import_data, ScriptContext::TOP); | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Invalid redeem script: must be hex string"); | // Verify whether the watchonly option corresponds to the | ||||
} | // availability of private keys. | ||||
bool spendable = std::all_of( | |||||
// Import redeem script. | import_data.used_keys.begin(), import_data.used_keys.end(), | ||||
std::vector<uint8_t> vData(ParseHex(strRedeemScript)); | [&](const std::pair<CKeyID, bool> &used_key) { | ||||
CScript redeemScript = CScript(vData.begin(), vData.end()); | return privkey_map.count(used_key.first) > 0; | ||||
CScriptID redeem_id(redeemScript); | }); | ||||
if (!watchOnly && !spendable) { | |||||
// Check that the redeemScript and scriptPubKey match | warnings.push_back("Some private keys are missing, outputs " | ||||
if (GetScriptForDestination(redeem_id) != script) { | "will be considered watchonly. If this is " | ||||
throw JSONRPCError( | "intentional, specify the watchonly flag."); | ||||
RPC_INVALID_ADDRESS_OR_KEY, | } | ||||
"The redeemScript does not match the scriptPubKey"); | 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) { | |||||
// Not a required key | |||||
continue; | |||||
} | } | ||||
if (pubkey_map.count(require_key.first) == 0 && | |||||
pwallet->MarkDirty(); | privkey_map.count(require_key.first) == 0) { | ||||
error = "some required keys are missing"; | |||||
if (!pwallet->AddWatchOnly(redeemScript, timestamp)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding address to wallet"); | |||||
} | } | ||||
if (!pwallet->HaveCScript(redeem_id) && | |||||
!pwallet->AddCScript(redeemScript)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding p2sh redeemScript to wallet"); | |||||
} | } | ||||
} | } | ||||
// (P2SH-)P2PK/P2PKH | if (!error.empty()) { | ||||
if (dest.type() == typeid(CKeyID)) { | warnings.push_back("Importing as non-solvable: " + error + | ||||
CPubKey pubkey; | ". If this is intentional, don't provide " | ||||
if (keys.size()) { | "any keys, pubkeys or redeemscript."); | ||||
pubkey = DecodeSecret(keys[0].get_str()).GetPubKey(); | import_data = ImportData(); | ||||
} | pubkey_map.clear(); | ||||
if (pubKeys.size()) { | privkey_map.clear(); | ||||
const std::string &strPubKey = pubKeys[0].get_str(); | have_solving_data = false; | ||||
if (!IsHex(strPubKey)) { | } else { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | // RecurseImportData() removes any relevant redeemscript from | ||||
"Pubkey must be a hex string"); | // 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."); | |||||
} | } | ||||
std::vector<uint8_t> vData(ParseHex(pubKeys[0].get_str())); | for (auto it = privkey_map.begin(); it != privkey_map.end();) { | ||||
CPubKey pubkey_temp(vData.begin(), vData.end()); | auto oldit = it++; | ||||
if (pubkey.size() && pubkey_temp != pubkey) { | if (import_data.used_keys.count(oldit->first) == 0) { | ||||
throw JSONRPCError( | warnings.push_back("Ignoring irrelevant private key."); | ||||
RPC_INVALID_ADDRESS_OR_KEY, | privkey_map.erase(oldit); | ||||
"Private key does not match public key for address"); | |||||
} | } | ||||
pubkey = pubkey_temp; | |||||
} | } | ||||
if (pubkey.size() > 0) { | for (auto it = pubkey_map.begin(); it != pubkey_map.end();) { | ||||
if (!pubkey.IsFullyValid()) { | auto oldit = it++; | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | auto key_data_it = import_data.used_keys.find(oldit->first); | ||||
"Pubkey is not a valid public key"); | 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."); | |||||
pubkey_map.erase(oldit); | |||||
} | } | ||||
// Check the key corresponds to the destination given | |||||
std::vector<CTxDestination> destinations = | |||||
GetAllDestinationsForKey(pubkey); | |||||
if (std::find(destinations.begin(), destinations.end(), dest) == | |||||
destinations.end()) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Key does not match address destination"); | |||||
} | |||||
// This is necessary to force the wallet to import the pubKey | |||||
CScript scriptRawPubKey = GetScriptForRawPubKey(pubkey); | |||||
if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) { | |||||
throw JSONRPCError( | |||||
RPC_WALLET_ERROR, | |||||
"The wallet already contains the private key for this " | |||||
"address or script"); | |||||
} | |||||
pwallet->MarkDirty(); | |||||
if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding address to wallet"); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// Import the address | // Check whether we have any work to do | ||||
if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) { | if (::IsMine(*pwallet, script) & 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 key " | ||||
"for this address or script"); | "for this address or script"); | ||||
} | } | ||||
// All good, time to import | |||||
pwallet->MarkDirty(); | pwallet->MarkDirty(); | ||||
if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) { | for (const auto &entry : import_data.import_scripts) { | ||||
if (!pwallet->HaveCScript(CScriptID(entry)) && | |||||
!pwallet->AddCScript(entry)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding address to wallet"); | "Error adding script to wallet"); | ||||
} | } | ||||
} | |||||
if (!watchOnly && | for (const auto &entry : privkey_map) { | ||||
!pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && | const CKey &key = entry.second; | ||||
!pwallet->AddCScript(scriptpubkey_script)) { | 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, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding scriptPubKey script to wallet"); | "Error adding key to wallet"); | ||||
} | } | ||||
pwallet->UpdateTimeFirstKey(timestamp); | |||||
// if not internal add to address book or update label | |||||
if (!internal) { | |||||
if (IsValidDestination(scriptpubkey_dest)) { | |||||
pwallet->SetAddressBook(scriptpubkey_dest, label, "receive"); | |||||
} | } | ||||
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)) { | |||||
throw JSONRPCError(RPC_WALLET_ERROR, | |||||
"Error adding address to wallet"); | |||||
} | } | ||||
// Import private keys. | |||||
for (size_t i = 0; i < keys.size(); i++) { | |||||
const std::string &strPrivkey = keys[i].get_str(); | |||||
// Checks. | |||||
CKey key = DecodeSecret(strPrivkey); | |||||
if (!key.IsValid()) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
"Invalid private key encoding"); | |||||
} | } | ||||
if (!have_solving_data || !::IsMine(*pwallet, script)) { | |||||
CPubKey pubKey = key.GetPubKey(); | // Always call AddWatchOnly for non-solvable watch-only, so that | ||||
assert(key.VerifyPubKey(pubKey)); | // watch timestamp gets updated | ||||
if (!pwallet->AddWatchOnly(script, timestamp)) { | |||||
CKeyID vchAddress = pubKey.GetID(); | |||||
pwallet->MarkDirty(); | |||||
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, | throw JSONRPCError(RPC_WALLET_ERROR, | ||||
"Error adding key to wallet"); | "Error adding address to wallet"); | ||||
} | } | ||||
} | |||||
pwallet->UpdateTimeFirstKey(timestamp); | if (!internal) { | ||||
assert(IsValidDestination(dest)); | |||||
pwallet->SetAddressBook(dest, label, "receive"); | |||||
} | } | ||||
UniValue result = UniValue(UniValue::VOBJ); | |||||
result.pushKV("success", UniValue(true)); | result.pushKV("success", UniValue(true)); | ||||
return result; | |||||
} catch (const UniValue &e) { | } catch (const UniValue &e) { | ||||
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; | |||||
} catch (...) { | } catch (...) { | ||||
UniValue result = UniValue(UniValue::VOBJ); | |||||
result.pushKV("success", UniValue(false)); | result.pushKV("success", UniValue(false)); | ||||
result.pushKV("error", | result.pushKV("error", | ||||
JSONRPCError(RPC_MISC_ERROR, "Missing required fields")); | JSONRPCError(RPC_MISC_ERROR, "Missing required fields")); | ||||
return result; | |||||
} | } | ||||
if (warnings.size()) { | |||||
result.pushKV("warnings", warnings); | |||||
} | |||||
return result; | |||||
} | } | ||||
static int64_t GetImportTimestamp(const UniValue &data, int64_t now) { | static int64_t GetImportTimestamp(const UniValue &data, int64_t now) { | ||||
if (data.exists("timestamp")) { | if (data.exists("timestamp")) { | ||||
const UniValue ×tamp = data["timestamp"]; | const UniValue ×tamp = data["timestamp"]; | ||||
if (timestamp.isNum()) { | if (timestamp.isNum()) { | ||||
return timestamp.get_int64(); | return timestamp.get_int64(); | ||||
} else if (timestamp.isStr() && timestamp.get_str() == "now") { | } else if (timestamp.isStr() && timestamp.get_str() == "now") { | ||||
Show All 15 Lines | UniValue importmulti(const Config &config, const JSONRPCRequest &mainRequest) { | ||||
if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) { | if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) { | ||||
return NullUniValue; | return NullUniValue; | ||||
} | } | ||||
if (mainRequest.fHelp || mainRequest.params.size() < 1 || | if (mainRequest.fHelp || mainRequest.params.size() < 1 || | ||||
mainRequest.params.size() > 2) { | mainRequest.params.size() > 2) { | ||||
throw std::runtime_error(RPCHelpMan{ | throw std::runtime_error(RPCHelpMan{ | ||||
"importmulti", | "importmulti", | ||||
"\nImport addresses/scripts (with private or public keys, " | "\nImport addresses/scripts (with private or public keys, redeem " | ||||
"redeem script (P2SH)), rescanning all addresses in " | "script (P2SH)), rescanning all addresses in one-shot-only (rescan " | ||||
"one-shot-only (rescan can be disabled via options). Requires " | "can be disabled via options). Requires a new wallet backup.\n", | ||||
"a new wallet backup.\n" | |||||
"\nNote: This call can take minutes 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", | {"requests", | ||||
RPCArg::Type::ARR, | RPCArg::Type::ARR, | ||||
/* opt */ false, | /* opt */ false, | ||||
/* default_val */ "", | /* default_val */ "", | ||||
"Data to be imported", | "Data to be imported", | ||||
{ | { | ||||
{ | { | ||||
Show All 37 Lines | if (mainRequest.fHelp || mainRequest.params.size() < 1 || | ||||
"hours before the earliest key\n" | "hours before the earliest key\n" | ||||
" " | " " | ||||
" creation time of all keys " | " creation time of all keys " | ||||
"being imported by the importmulti call will " | "being imported by the importmulti call will " | ||||
"be scanned.", | "be scanned.", | ||||
/* oneline_description */ "", | /* oneline_description */ "", | ||||
{"timestamp | \"now\"", "integer / string"}}, | {"timestamp | \"now\"", "integer / string"}}, | ||||
{"redeemscript", RPCArg::Type::STR, | {"redeemscript", RPCArg::Type::STR, | ||||
/* opt */ true, /* default_val */ "", | /* opt */ true, /* default_val */ "omitted", | ||||
"Allowed only if the scriptPubKey is a P2SH " | "Allowed only if the scriptPubKey is a P2SH " | ||||
"address/scriptPubKey"}, | "address/scriptPubKey"}, | ||||
{"pubkeys", | {"pubkeys", | ||||
RPCArg::Type::ARR, | RPCArg::Type::ARR, | ||||
/* opt */ true, | /* opt */ true, | ||||
/* default_val */ "", | /* default_val */ "empty array", | ||||
"Array of strings giving pubkeys that must " | "Array of strings giving pubkeys to import. They " | ||||
"occur in the output or redeemscript", | "must occur in P2PKH scripts. They are not " | ||||
"required when the private key is also provided " | |||||
"(see the \"keys\" argument).", | |||||
{ | { | ||||
{"pubKey", RPCArg::Type::STR, | {"pubKey", RPCArg::Type::STR, /* opt */ false, | ||||
/* opt */ false, /* default_val */ "", ""}, | /* default_val */ "", ""}, | ||||
}}, | }}, | ||||
{"keys", | {"keys", | ||||
RPCArg::Type::ARR, | RPCArg::Type::ARR, | ||||
/* opt */ true, | /* opt */ true, | ||||
/* default_val */ "", | /* default_val */ "empty array", | ||||
"Array of strings giving private keys whose " | "Array of strings giving private keys to import. " | ||||
"corresponding public keys must occur in the " | "The corresponding public keys must occur in the " | ||||
"output or redeemscript", | "output or redeemscript.", | ||||
{ | { | ||||
{"key", RPCArg::Type::STR, | {"key", RPCArg::Type::STR, | ||||
/* opt */ false, /* default_val */ "", ""}, | /* opt */ false, /* default_val */ "", ""}, | ||||
}}, | }}, | ||||
{"internal", RPCArg::Type::BOOL, | {"internal", RPCArg::Type::BOOL, | ||||
/* opt */ true, /* default_val */ "false", | /* opt */ true, /* default_val */ "false", | ||||
"Stating whether matching outputs should be " | "Stating whether matching outputs should be " | ||||
"treated as not incoming payments aka " | "treated as not incoming payments (also known as " | ||||
"change"}, | "change)"}, | ||||
{"watchonly", RPCArg::Type::BOOL, | {"watchonly", RPCArg::Type::BOOL, | ||||
/* opt */ true, /* default_val */ "false", | /* opt */ true, /* default_val */ "false", | ||||
"Stating whether matching outputs should be " | "Stating whether matching outputs should be " | ||||
"considered watched even when they're not " | "considered watched even when not all private " | ||||
"spendable, only allowed if keys are empty"}, | "keys are provided."}, | ||||
{"label", RPCArg::Type::STR, /* opt */ true, | {"label", RPCArg::Type::STR, /* opt */ true, | ||||
/* default_val */ "''", | /* default_val */ "''", | ||||
"Label to assign to the address, only " | "Label to assign to the address, only " | ||||
"allowed with internal=false"}, | "allowed with internal=false"}, | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
"\"requests\""}, | "\"requests\""}, | ||||
{"options", | {"options", | ||||
RPCArg::Type::OBJ, | RPCArg::Type::OBJ, | ||||
/* opt */ true, | /* opt */ true, | ||||
/* default_val */ "", | /* default_val */ "null", | ||||
"", | "", | ||||
{ | { | ||||
{"rescan", RPCArg::Type::BOOL, /* opt */ true, | {"rescan", RPCArg::Type::BOOL, /* opt */ true, | ||||
/* default_val */ "true", | /* default_val */ "true", | ||||
"Stating if should rescan the blockchain after all " | "Stating if should rescan the blockchain after all " | ||||
"imports"}, | "imports"}, | ||||
}, | }, | ||||
"\"options\""}, | "\"options\""}, | ||||
}, | }, | ||||
RPCResult{ | RPCResult{"\nResponse is an array with the same size as the input " | ||||
"\nResponse is an array with the same size as the input that " | "that has the execution result :\n" | ||||
"has the execution result :\n" | " [{\"success\": true}, {\"success\": true, " | ||||
" [{ \"success\": true } , { \"success\": false, \"error\": { " | "\"warnings\": [\"Ignoring irrelevant private key\"]}, " | ||||
"\"code\": -1, \"message\": \"Internal Server Error\"} }, ... " | "{\"success\": false, \"error\": {\"code\": -1, " | ||||
"]\n"}, | "\"message\": \"Internal Server Error\"}}, ...]\n"}, | ||||
RPCExamples{ | RPCExamples{ | ||||
HelpExampleCli( | HelpExampleCli( | ||||
"importmulti", | "importmulti", | ||||
"'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, " | "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, " | ||||
"\"timestamp\":1455191478 }, " | "\"timestamp\":1455191478 }, " | ||||
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" " | "{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" " | ||||
"}, " | "}, " | ||||
"\"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + | "\"label\": \"example 2\", \"timestamp\": 1455191480 }]'") + | ||||
▲ Show 20 Lines • Show All 156 Lines • Show Last 20 Lines |