Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcwallet.cpp
Show All 9 Lines | |||||
#include <consensus/validation.h> | #include <consensus/validation.h> | ||||
#include <core_io.h> | #include <core_io.h> | ||||
#include <interfaces/chain.h> | #include <interfaces/chain.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <network.h> | #include <network.h> | ||||
#include <node/context.h> | #include <node/context.h> | ||||
#include <outputtype.h> | #include <outputtype.h> | ||||
#include <policy/fees.h> | #include <policy/fees.h> | ||||
#include <policy/policy.h> | |||||
#include <rpc/rawtransaction_util.h> | #include <rpc/rawtransaction_util.h> | ||||
#include <rpc/server.h> | #include <rpc/server.h> | ||||
#include <rpc/util.h> | #include <rpc/util.h> | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <util/bip32.h> | #include <util/bip32.h> | ||||
#include <util/error.h> | #include <util/error.h> | ||||
#include <util/message.h> // For MessageSign() | #include <util/message.h> // For MessageSign() | ||||
#include <util/moneystr.h> | #include <util/moneystr.h> | ||||
▲ Show 20 Lines • Show All 3,663 Lines • ▼ Show 20 Lines | if (!options.isNull()) { | ||||
// backward compatibility bool only fallback | // backward compatibility bool only fallback | ||||
coinControl.fAllowWatchOnly = options.get_bool(); | coinControl.fAllowWatchOnly = options.get_bool(); | ||||
} else { | } else { | ||||
RPCTypeCheckArgument(options, UniValue::VOBJ); | RPCTypeCheckArgument(options, UniValue::VOBJ); | ||||
RPCTypeCheckObj( | RPCTypeCheckObj( | ||||
options, | options, | ||||
{ | { | ||||
{"add_inputs", UniValueType(UniValue::VBOOL)}, | {"add_inputs", UniValueType(UniValue::VBOOL)}, | ||||
{"add_to_wallet", UniValueType(UniValue::VBOOL)}, | |||||
{"changeAddress", UniValueType(UniValue::VSTR)}, | {"changeAddress", UniValueType(UniValue::VSTR)}, | ||||
{"change_address", UniValueType(UniValue::VSTR)}, | {"change_address", UniValueType(UniValue::VSTR)}, | ||||
{"changePosition", UniValueType(UniValue::VNUM)}, | {"changePosition", UniValueType(UniValue::VNUM)}, | ||||
{"change_position", UniValueType(UniValue::VNUM)}, | {"change_position", UniValueType(UniValue::VNUM)}, | ||||
{"includeWatching", UniValueType(UniValue::VBOOL)}, | {"includeWatching", UniValueType(UniValue::VBOOL)}, | ||||
{"include_watching", UniValueType(UniValue::VBOOL)}, | {"include_watching", UniValueType(UniValue::VBOOL)}, | ||||
{"inputs", UniValueType(UniValue::VARR)}, | |||||
{"lockUnspents", UniValueType(UniValue::VBOOL)}, | {"lockUnspents", UniValueType(UniValue::VBOOL)}, | ||||
{"lock_unspents", UniValueType(UniValue::VBOOL)}, | {"lock_unspents", UniValueType(UniValue::VBOOL)}, | ||||
{"locktime", UniValueType(UniValue::VNUM)}, | |||||
// will be checked below | // will be checked below | ||||
{"feeRate", UniValueType()}, | {"feeRate", UniValueType()}, | ||||
{"fee_rate", UniValueType()}, | {"fee_rate", UniValueType()}, | ||||
{"psbt", UniValueType(UniValue::VBOOL)}, | |||||
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, | {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, | ||||
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)}, | {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)}, | ||||
}, | }, | ||||
true, true); | true, true); | ||||
if (options.exists("add_inputs")) { | if (options.exists("add_inputs")) { | ||||
coinControl.m_add_inputs = options["add_inputs"].get_bool(); | coinControl.m_add_inputs = options["add_inputs"].get_bool(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 852 Lines • ▼ Show 20 Lines | UniValue listlabels(const Config &config, const JSONRPCRequest &request) { | ||||
UniValue ret(UniValue::VARR); | UniValue ret(UniValue::VARR); | ||||
for (const std::string &name : label_set) { | for (const std::string &name : label_set) { | ||||
ret.push_back(name); | ret.push_back(name); | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
static RPCHelpMan send() { | |||||
return RPCHelpMan{ | |||||
"send", | |||||
"\nSend a transaction.\n", | |||||
{ | |||||
{ | |||||
"outputs", | |||||
RPCArg::Type::ARR, | |||||
RPCArg::Optional::NO, | |||||
"a json array with outputs (key-value pairs), where none of " | |||||
"the keys are duplicated.\n" | |||||
"That is, each address can only appear once and there can only " | |||||
"be one 'data' object.\n" | |||||
"For convenience, a dictionary, which holds the key-value " | |||||
"pairs directly, is also accepted.", | |||||
{ | |||||
{ | |||||
"", | |||||
RPCArg::Type::OBJ, | |||||
RPCArg::Optional::OMITTED, | |||||
"", | |||||
{ | |||||
{"address", RPCArg::Type::AMOUNT, | |||||
RPCArg::Optional::NO, | |||||
"A key-value pair. The key (string) is the " | |||||
"bitcoin address, the value (float or string) is " | |||||
"the amount in " + | |||||
Currency::get().ticker + ""}, | |||||
}, | |||||
}, | |||||
{ | |||||
"", | |||||
RPCArg::Type::OBJ, | |||||
RPCArg::Optional::OMITTED, | |||||
"", | |||||
{ | |||||
{"data", RPCArg::Type::STR_HEX, | |||||
RPCArg::Optional::NO, | |||||
"A key-value pair. The key must be \"data\", the " | |||||
"value is hex-encoded data"}, | |||||
}, | |||||
}, | |||||
}, | |||||
}, | |||||
{"options", | |||||
RPCArg::Type::OBJ, | |||||
RPCArg::Optional::OMITTED_NAMED_ARG, | |||||
"", | |||||
{ | |||||
{"add_inputs", RPCArg::Type::BOOL, /* default */ "false", | |||||
"If inputs are specified, automatically include more if they " | |||||
"are not enough."}, | |||||
{"add_to_wallet", RPCArg::Type::BOOL, /* default */ "true", | |||||
"When false, returns a serialized transaction which will not " | |||||
"be added to the wallet or broadcast"}, | |||||
{"change_address", RPCArg::Type::STR_HEX, | |||||
/* default */ "pool address", | |||||
"The bitcoin address to receive the change"}, | |||||
{"change_position", RPCArg::Type::NUM, /* default */ "random", | |||||
"The index of the change output"}, | |||||
{"fee_rate", RPCArg::Type::AMOUNT, /* default */ | |||||
"not set: makes wallet determine the fee", | |||||
"Set a specific fee rate in " + Currency::get().ticker + | |||||
"/kB"}, | |||||
{"include_watching", RPCArg::Type::BOOL, | |||||
/* default */ "true for watch-only wallets, otherwise false", | |||||
"Also select inputs which are watch only.\n" | |||||
"Only solvable inputs can be used. Watch-only destinations " | |||||
"are solvable if the public key and/or output script was " | |||||
"imported,\n" | |||||
"e.g. with 'importpubkey' or 'importmulti' with the " | |||||
"'pubkeys' or 'desc' field."}, | |||||
{ | |||||
"inputs", | |||||
RPCArg::Type::ARR, | |||||
/* default */ "empty array", | |||||
"Specify inputs instead of adding them automatically. A " | |||||
"json array of json objects", | |||||
{ | |||||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, | |||||
"The transaction id"}, | |||||
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, | |||||
"The output number"}, | |||||
{"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, | |||||
"The sequence number"}, | |||||
}, | |||||
}, | |||||
{"locktime", RPCArg::Type::NUM, /* default */ "0", | |||||
"Raw locktime. Non-0 value also locktime-activates inputs"}, | |||||
{"lock_unspents", RPCArg::Type::BOOL, /* default */ "false", | |||||
"Lock selected unspent outputs"}, | |||||
{"psbt", RPCArg::Type::BOOL, /* default */ "automatic", | |||||
"Always return a PSBT, implies add_to_wallet=false."}, | |||||
{ | |||||
"subtract_fee_from_outputs", | |||||
RPCArg::Type::ARR, | |||||
/* default */ "empty array", | |||||
"A json array of integers.\n" | |||||
"The fee will be equally deducted from the amount of each " | |||||
"specified output.\n" | |||||
"Those recipients will receive less bitcoins than you " | |||||
"enter in their corresponding amount field.\n" | |||||
"If no outputs are specified here, the sender pays the " | |||||
"fee.", | |||||
{ | |||||
{"vout_index", RPCArg::Type::NUM, | |||||
RPCArg::Optional::OMITTED, | |||||
"The zero-based output index, before a change output " | |||||
"is added."}, | |||||
}, | |||||
}, | |||||
}, | |||||
"options"}, | |||||
}, | |||||
RPCResult{ | |||||
RPCResult::Type::OBJ, | |||||
"", | |||||
"", | |||||
{{RPCResult::Type::BOOL, "complete", | |||||
"If the transaction has a complete set of signatures"}, | |||||
{RPCResult::Type::STR_HEX, "txid", | |||||
"The transaction id for the send. Only 1 transaction is created " | |||||
"regardless of the number of addresses."}, | |||||
{RPCResult::Type::STR_HEX, "hex", | |||||
"If add_to_wallet is false, the hex-encoded raw transaction with " | |||||
"signature(s)"}, | |||||
{RPCResult::Type::STR, "psbt", | |||||
"If more signatures are needed, or if add_to_wallet is false, " | |||||
"the base64-encoded (partially) signed transaction"}}}, | |||||
RPCExamples{ | |||||
"" | |||||
"\nSend with a fee rate of 10 XEC/kB\n" + | |||||
HelpExampleCli( | |||||
"send", | |||||
"'{\"" + EXAMPLE_ADDRESS + | |||||
"\": 100000}' '{\"fee_rate\": 10}'\n" + | |||||
"\nCreate a transaction with a specific input, and return " | |||||
"result without adding to wallet or broadcasting to the " | |||||
"network\n") + | |||||
HelpExampleCli("send", | |||||
"'{\"" + EXAMPLE_ADDRESS + | |||||
"\": 100000}' '{\"add_to_wallet\": " | |||||
"false, \"inputs\": " | |||||
"[{\"txid\":" | |||||
"\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b565" | |||||
"5e72f463568df1aadf0\", \"vout\":1}]}'")}, | |||||
[&](const RPCHelpMan &self, const Config &config, | |||||
const JSONRPCRequest &request) -> UniValue { | |||||
RPCTypeCheck(request.params, | |||||
{// ARR or OBJ, checked later | |||||
UniValueType(), UniValue::VOBJ}, | |||||
true); | |||||
std::shared_ptr<CWallet> const wallet = | |||||
GetWalletForJSONRPCRequest(request); | |||||
if (!wallet) { | |||||
return NullUniValue; | |||||
} | |||||
CWallet *const pwallet = wallet.get(); | |||||
UniValue options = request.params[1]; | |||||
if (options.exists("changeAddress")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address"); | |||||
} | |||||
if (options.exists("changePosition")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"Use change_position"); | |||||
} | |||||
if (options.exists("includeWatching")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"Use include_watching"); | |||||
} | |||||
if (options.exists("lockUnspents")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents"); | |||||
} | |||||
if (options.exists("subtractFeeFromOutputs")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | |||||
"Use subtract_fee_from_outputs"); | |||||
} | |||||
if (options.exists("feeRate")) { | |||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use fee_rate"); | |||||
} | |||||
const bool psbt_opt_in = | |||||
options.exists("psbt") && options["psbt"].get_bool(); | |||||
Amount fee; | |||||
int change_position; | |||||
CMutableTransaction rawTx = ConstructTransaction( | |||||
wallet->GetChainParams(), options["inputs"], request.params[0], | |||||
options["locktime"]); | |||||
CCoinControl coin_control; | |||||
// Automatically select coins, unless at least one is manually | |||||
// selected. Can be overridden by options.add_inputs. | |||||
coin_control.m_add_inputs = rawTx.vin.size() == 0; | |||||
FundTransaction(pwallet, rawTx, fee, change_position, options, | |||||
coin_control); | |||||
bool add_to_wallet = true; | |||||
if (options.exists("add_to_wallet")) { | |||||
add_to_wallet = options["add_to_wallet"].get_bool(); | |||||
} | |||||
// Make a blank psbt | |||||
PartiallySignedTransaction psbtx(rawTx); | |||||
// Fill transaction with out data and sign | |||||
bool complete = true; | |||||
const TransactionError err = pwallet->FillPSBT( | |||||
psbtx, complete, SigHashType().withForkId(), true, false); | |||||
if (err != TransactionError::OK) { | |||||
throw JSONRPCTransactionError(err); | |||||
} | |||||
CMutableTransaction mtx; | |||||
complete = FinalizeAndExtractPSBT(psbtx, mtx); | |||||
UniValue result(UniValue::VOBJ); | |||||
// Serialize the PSBT | |||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); | |||||
ssTx << psbtx; | |||||
const std::string result_str = EncodeBase64(ssTx.str()); | |||||
if (psbt_opt_in || !complete || !add_to_wallet) { | |||||
result.pushKV("psbt", result_str); | |||||
} | |||||
if (complete) { | |||||
std::string err_string; | |||||
std::string hex = EncodeHexTx(CTransaction(mtx)); | |||||
CTransactionRef tx(MakeTransactionRef(std::move(mtx))); | |||||
result.pushKV("txid", tx->GetHash().GetHex()); | |||||
if (add_to_wallet && !psbt_opt_in) { | |||||
pwallet->CommitTransaction(tx, {}, {} /* orderForm */); | |||||
} else { | |||||
result.pushKV("hex", hex); | |||||
} | |||||
} | |||||
result.pushKV("complete", complete); | |||||
return result; | |||||
}}; | |||||
} | |||||
static UniValue sethdseed(const Config &config, const JSONRPCRequest &request) { | static UniValue sethdseed(const Config &config, const JSONRPCRequest &request) { | ||||
RPCHelpMan{ | RPCHelpMan{ | ||||
"sethdseed", | "sethdseed", | ||||
"Set or generate a new HD wallet seed. Non-HD wallets will not be " | "Set or generate a new HD wallet seed. Non-HD wallets will not be " | ||||
"upgraded to being a HD wallet. Wallets that are already\n" | "upgraded to being a HD wallet. Wallets that are already\n" | ||||
"HD will have a new HD seed set so that new keys added to the keypool " | "HD will have a new HD seed set so that new keys added to the keypool " | ||||
"will be derived from this new seed.\n" | "will be derived from this new seed.\n" | ||||
"\nNote that you will need to MAKE A NEW BACKUP of your wallet after " | "\nNote that you will need to MAKE A NEW BACKUP of your wallet after " | ||||
▲ Show 20 Lines • Show All 430 Lines • ▼ Show 20 Lines | static const CRPCCommand commands[] = { | ||||
{ "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, | { "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, | ||||
{ "wallet", "listtransactions", listtransactions, {"label|dummy","count","skip","include_watchonly"} }, | { "wallet", "listtransactions", listtransactions, {"label|dummy","count","skip","include_watchonly"} }, | ||||
{ "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, | { "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, | ||||
{ "wallet", "listwalletdir", listwalletdir, {} }, | { "wallet", "listwalletdir", listwalletdir, {} }, | ||||
{ "wallet", "listwallets", listwallets, {} }, | { "wallet", "listwallets", listwallets, {} }, | ||||
{ "wallet", "loadwallet", loadwallet, {"filename", "load_on_startup"} }, | { "wallet", "loadwallet", loadwallet, {"filename", "load_on_startup"} }, | ||||
{ "wallet", "lockunspent", lockunspent, {"unlock","transactions"} }, | { "wallet", "lockunspent", lockunspent, {"unlock","transactions"} }, | ||||
{ "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} }, | { "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} }, | ||||
{ "wallet", "send", send, {"outputs", "options"} }, | |||||
{ "wallet", "sendmany", sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom"} }, | { "wallet", "sendmany", sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom"} }, | ||||
{ "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","avoid_reuse"} }, | { "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","avoid_reuse"} }, | ||||
{ "wallet", "sethdseed", sethdseed, {"newkeypool","seed"} }, | { "wallet", "sethdseed", sethdseed, {"newkeypool","seed"} }, | ||||
{ "wallet", "setlabel", setlabel, {"address","label"} }, | { "wallet", "setlabel", setlabel, {"address","label"} }, | ||||
{ "wallet", "settxfee", settxfee, {"amount"} }, | { "wallet", "settxfee", settxfee, {"amount"} }, | ||||
{ "wallet", "setwalletflag", setwalletflag, {"flag","value"} }, | { "wallet", "setwalletflag", setwalletflag, {"flag","value"} }, | ||||
{ "wallet", "signmessage", signmessage, {"address","message"} }, | { "wallet", "signmessage", signmessage, {"address","message"} }, | ||||
{ "wallet", "signrawtransactionwithwallet", signrawtransactionwithwallet, {"hextring","prevtxs","sighashtype"} }, | { "wallet", "signrawtransactionwithwallet", signrawtransactionwithwallet, {"hextring","prevtxs","sighashtype"} }, | ||||
Show All 12 Lines |