|
|
@ -356,7 +356,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) |
|
|
|
return ret; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) |
|
|
|
static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, CCoinControl *coin_control = nullptr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
CAmount curBalance = pwallet->GetBalance(); |
|
|
|
CAmount curBalance = pwallet->GetBalance(); |
|
|
|
|
|
|
|
|
|
|
@ -382,7 +382,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA |
|
|
|
int nChangePosRet = -1; |
|
|
|
int nChangePosRet = -1; |
|
|
|
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; |
|
|
|
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; |
|
|
|
vecSend.push_back(recipient); |
|
|
|
vecSend.push_back(recipient); |
|
|
|
if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { |
|
|
|
if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { |
|
|
|
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) |
|
|
|
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) |
|
|
|
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); |
|
|
|
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); |
|
|
|
throw JSONRPCError(RPC_WALLET_ERROR, strError); |
|
|
|
throw JSONRPCError(RPC_WALLET_ERROR, strError); |
|
|
@ -401,9 +401,9 @@ UniValue sendtoaddress(const JSONRPCRequest& request) |
|
|
|
return NullUniValue; |
|
|
|
return NullUniValue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) |
|
|
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) |
|
|
|
throw std::runtime_error( |
|
|
|
throw std::runtime_error( |
|
|
|
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n" |
|
|
|
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n" |
|
|
|
"\nSend an amount to a given address.\n" |
|
|
|
"\nSend an amount to a given address.\n" |
|
|
|
+ HelpRequiringPassphrase(pwallet) + |
|
|
|
+ HelpRequiringPassphrase(pwallet) + |
|
|
|
"\nArguments:\n" |
|
|
|
"\nArguments:\n" |
|
|
@ -416,6 +416,12 @@ UniValue sendtoaddress(const JSONRPCRequest& request) |
|
|
|
" transaction, just kept in your wallet.\n" |
|
|
|
" transaction, just kept in your wallet.\n" |
|
|
|
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" |
|
|
|
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" |
|
|
|
" The recipient will receive less bitcoins than you enter in the amount field.\n" |
|
|
|
" The recipient will receive less bitcoins than you enter in the amount field.\n" |
|
|
|
|
|
|
|
"6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" |
|
|
|
|
|
|
|
"7. conf_target (numeric, optional) Confirmation target (in blocks)\n" |
|
|
|
|
|
|
|
"8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" |
|
|
|
|
|
|
|
" \"UNSET\"\n" |
|
|
|
|
|
|
|
" \"ECONOMICAL\"\n" |
|
|
|
|
|
|
|
" \"CONSERVATIVE\"\n" |
|
|
|
"\nResult:\n" |
|
|
|
"\nResult:\n" |
|
|
|
"\"txid\" (string) The transaction id.\n" |
|
|
|
"\"txid\" (string) The transaction id.\n" |
|
|
|
"\nExamples:\n" |
|
|
|
"\nExamples:\n" |
|
|
@ -444,12 +450,29 @@ UniValue sendtoaddress(const JSONRPCRequest& request) |
|
|
|
wtx.mapValue["to"] = request.params[3].get_str(); |
|
|
|
wtx.mapValue["to"] = request.params[3].get_str(); |
|
|
|
|
|
|
|
|
|
|
|
bool fSubtractFeeFromAmount = false; |
|
|
|
bool fSubtractFeeFromAmount = false; |
|
|
|
if (request.params.size() > 4) |
|
|
|
if (request.params.size() > 4 && !request.params[4].isNull()) { |
|
|
|
fSubtractFeeFromAmount = request.params[4].get_bool(); |
|
|
|
fSubtractFeeFromAmount = request.params[4].get_bool(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CCoinControl coin_control; |
|
|
|
|
|
|
|
if (request.params.size() > 5 && !request.params[5].isNull()) { |
|
|
|
|
|
|
|
coin_control.signalRbf = request.params[5].get_bool(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (request.params.size() > 6 && !request.params[6].isNull()) { |
|
|
|
|
|
|
|
coin_control.nConfirmTarget = request.params[6].get_int(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (request.params.size() > 7 && !request.params[7].isNull()) { |
|
|
|
|
|
|
|
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { |
|
|
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EnsureWalletIsUnlocked(pwallet); |
|
|
|
EnsureWalletIsUnlocked(pwallet); |
|
|
|
|
|
|
|
|
|
|
|
SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx); |
|
|
|
SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, &coin_control); |
|
|
|
|
|
|
|
|
|
|
|
return wtx.GetHash().GetHex(); |
|
|
|
return wtx.GetHash().GetHex(); |
|
|
|
} |
|
|
|
} |
|
|
@ -888,9 +911,9 @@ UniValue sendmany(const JSONRPCRequest& request) |
|
|
|
return NullUniValue; |
|
|
|
return NullUniValue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) |
|
|
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) |
|
|
|
throw std::runtime_error( |
|
|
|
throw std::runtime_error( |
|
|
|
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" |
|
|
|
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" |
|
|
|
"\nSend multiple times. Amounts are double-precision floating point numbers." |
|
|
|
"\nSend multiple times. Amounts are double-precision floating point numbers." |
|
|
|
+ HelpRequiringPassphrase(pwallet) + "\n" |
|
|
|
+ HelpRequiringPassphrase(pwallet) + "\n" |
|
|
|
"\nArguments:\n" |
|
|
|
"\nArguments:\n" |
|
|
@ -910,7 +933,13 @@ UniValue sendmany(const JSONRPCRequest& request) |
|
|
|
" \"address\" (string) Subtract fee from this address\n" |
|
|
|
" \"address\" (string) Subtract fee from this address\n" |
|
|
|
" ,...\n" |
|
|
|
" ,...\n" |
|
|
|
" ]\n" |
|
|
|
" ]\n" |
|
|
|
"\nResult:\n" |
|
|
|
"6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" |
|
|
|
|
|
|
|
"7. conf_target (numeric, optional) Confirmation target (in blocks)\n" |
|
|
|
|
|
|
|
"8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" |
|
|
|
|
|
|
|
" \"UNSET\"\n" |
|
|
|
|
|
|
|
" \"ECONOMICAL\"\n" |
|
|
|
|
|
|
|
" \"CONSERVATIVE\"\n" |
|
|
|
|
|
|
|
"\nResult:\n" |
|
|
|
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" |
|
|
|
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" |
|
|
|
" the number of addresses.\n" |
|
|
|
" the number of addresses.\n" |
|
|
|
"\nExamples:\n" |
|
|
|
"\nExamples:\n" |
|
|
@ -942,9 +971,24 @@ UniValue sendmany(const JSONRPCRequest& request) |
|
|
|
wtx.mapValue["comment"] = request.params[3].get_str(); |
|
|
|
wtx.mapValue["comment"] = request.params[3].get_str(); |
|
|
|
|
|
|
|
|
|
|
|
UniValue subtractFeeFromAmount(UniValue::VARR); |
|
|
|
UniValue subtractFeeFromAmount(UniValue::VARR); |
|
|
|
if (request.params.size() > 4) |
|
|
|
if (request.params.size() > 4 && !request.params[4].isNull()) |
|
|
|
subtractFeeFromAmount = request.params[4].get_array(); |
|
|
|
subtractFeeFromAmount = request.params[4].get_array(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CCoinControl coin_control; |
|
|
|
|
|
|
|
if (request.params.size() > 5 && !request.params[5].isNull()) { |
|
|
|
|
|
|
|
coin_control.signalRbf = request.params[5].get_bool(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (request.params.size() > 6 && !request.params[6].isNull()) { |
|
|
|
|
|
|
|
coin_control.nConfirmTarget = request.params[6].get_int(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (request.params.size() > 7 && !request.params[7].isNull()) { |
|
|
|
|
|
|
|
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) { |
|
|
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
std::set<CBitcoinAddress> setAddress; |
|
|
|
std::set<CBitcoinAddress> setAddress; |
|
|
|
std::vector<CRecipient> vecSend; |
|
|
|
std::vector<CRecipient> vecSend; |
|
|
|
|
|
|
|
|
|
|
@ -989,7 +1033,7 @@ UniValue sendmany(const JSONRPCRequest& request) |
|
|
|
CAmount nFeeRequired = 0; |
|
|
|
CAmount nFeeRequired = 0; |
|
|
|
int nChangePosRet = -1; |
|
|
|
int nChangePosRet = -1; |
|
|
|
std::string strFailReason; |
|
|
|
std::string strFailReason; |
|
|
|
bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); |
|
|
|
bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, &coin_control); |
|
|
|
if (!fCreated) |
|
|
|
if (!fCreated) |
|
|
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); |
|
|
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); |
|
|
|
CValidationState state; |
|
|
|
CValidationState state; |
|
|
@ -2658,6 +2702,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) |
|
|
|
" [vout_index,...]\n" |
|
|
|
" [vout_index,...]\n" |
|
|
|
" \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n" |
|
|
|
" \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n" |
|
|
|
" Allows this transaction to be replaced by a transaction with higher fees\n" |
|
|
|
" Allows this transaction to be replaced by a transaction with higher fees\n" |
|
|
|
|
|
|
|
" \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n" |
|
|
|
|
|
|
|
" \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" |
|
|
|
|
|
|
|
" \"UNSET\"\n" |
|
|
|
|
|
|
|
" \"ECONOMICAL\"\n" |
|
|
|
|
|
|
|
" \"CONSERVATIVE\"\n" |
|
|
|
" }\n" |
|
|
|
" }\n" |
|
|
|
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" |
|
|
|
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" |
|
|
|
"\nResult:\n" |
|
|
|
"\nResult:\n" |
|
|
@ -2710,6 +2759,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) |
|
|
|
{"feeRate", UniValueType()}, // will be checked below
|
|
|
|
{"feeRate", UniValueType()}, // will be checked below
|
|
|
|
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, |
|
|
|
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, |
|
|
|
{"replaceable", UniValueType(UniValue::VBOOL)}, |
|
|
|
{"replaceable", UniValueType(UniValue::VBOOL)}, |
|
|
|
|
|
|
|
{"conf_target", UniValueType(UniValue::VNUM)}, |
|
|
|
|
|
|
|
{"estimate_mode", UniValueType(UniValue::VSTR)}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
true, true); |
|
|
|
true, true); |
|
|
|
|
|
|
|
|
|
|
@ -2746,6 +2797,14 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) |
|
|
|
if (options.exists("replaceable")) { |
|
|
|
if (options.exists("replaceable")) { |
|
|
|
coinControl.signalRbf = options["replaceable"].get_bool(); |
|
|
|
coinControl.signalRbf = options["replaceable"].get_bool(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (options.exists("conf_target")) { |
|
|
|
|
|
|
|
coinControl.nConfirmTarget = options["conf_target"].get_int(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (options.exists("estimate_mode")) { |
|
|
|
|
|
|
|
if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) { |
|
|
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -2823,6 +2882,10 @@ UniValue bumpfee(const JSONRPCRequest& request) |
|
|
|
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n" |
|
|
|
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n" |
|
|
|
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n" |
|
|
|
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n" |
|
|
|
" are replaceable).\n" |
|
|
|
" are replaceable).\n" |
|
|
|
|
|
|
|
" \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" |
|
|
|
|
|
|
|
" \"UNSET\"\n" |
|
|
|
|
|
|
|
" \"ECONOMICAL\"\n" |
|
|
|
|
|
|
|
" \"CONSERVATIVE\"\n" |
|
|
|
" }\n" |
|
|
|
" }\n" |
|
|
|
"\nResult:\n" |
|
|
|
"\nResult:\n" |
|
|
|
"{\n" |
|
|
|
"{\n" |
|
|
@ -2845,6 +2908,7 @@ UniValue bumpfee(const JSONRPCRequest& request) |
|
|
|
int newConfirmTarget = nTxConfirmTarget; |
|
|
|
int newConfirmTarget = nTxConfirmTarget; |
|
|
|
CAmount totalFee = 0; |
|
|
|
CAmount totalFee = 0; |
|
|
|
bool replaceable = true; |
|
|
|
bool replaceable = true; |
|
|
|
|
|
|
|
FeeEstimateMode fee_mode = FeeEstimateMode::UNSET; |
|
|
|
if (request.params.size() > 1) { |
|
|
|
if (request.params.size() > 1) { |
|
|
|
UniValue options = request.params[1]; |
|
|
|
UniValue options = request.params[1]; |
|
|
|
RPCTypeCheckObj(options, |
|
|
|
RPCTypeCheckObj(options, |
|
|
@ -2852,6 +2916,7 @@ UniValue bumpfee(const JSONRPCRequest& request) |
|
|
|
{"confTarget", UniValueType(UniValue::VNUM)}, |
|
|
|
{"confTarget", UniValueType(UniValue::VNUM)}, |
|
|
|
{"totalFee", UniValueType(UniValue::VNUM)}, |
|
|
|
{"totalFee", UniValueType(UniValue::VNUM)}, |
|
|
|
{"replaceable", UniValueType(UniValue::VBOOL)}, |
|
|
|
{"replaceable", UniValueType(UniValue::VBOOL)}, |
|
|
|
|
|
|
|
{"estimate_mode", UniValueType(UniValue::VSTR)}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
true, true); |
|
|
|
true, true); |
|
|
|
|
|
|
|
|
|
|
@ -2876,12 +2941,17 @@ UniValue bumpfee(const JSONRPCRequest& request) |
|
|
|
if (options.exists("replaceable")) { |
|
|
|
if (options.exists("replaceable")) { |
|
|
|
replaceable = options["replaceable"].get_bool(); |
|
|
|
replaceable = options["replaceable"].get_bool(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (options.exists("estimate_mode")) { |
|
|
|
|
|
|
|
if (!FeeModeFromString(options["estimate_mode"].get_str(), fee_mode)) { |
|
|
|
|
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
LOCK2(cs_main, pwallet->cs_wallet); |
|
|
|
LOCK2(cs_main, pwallet->cs_wallet); |
|
|
|
EnsureWalletIsUnlocked(pwallet); |
|
|
|
EnsureWalletIsUnlocked(pwallet); |
|
|
|
|
|
|
|
|
|
|
|
CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable); |
|
|
|
CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable, fee_mode); |
|
|
|
BumpFeeResult res = feeBump.getResult(); |
|
|
|
BumpFeeResult res = feeBump.getResult(); |
|
|
|
if (res != BumpFeeResult::OK) |
|
|
|
if (res != BumpFeeResult::OK) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -3023,8 +3093,8 @@ static const CRPCCommand commands[] = |
|
|
|
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, |
|
|
|
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, |
|
|
|
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, |
|
|
|
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, |
|
|
|
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, |
|
|
|
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, |
|
|
|
{ "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} }, |
|
|
|
{ "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, |
|
|
|
{ "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} }, |
|
|
|
{ "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, |
|
|
|
{ "wallet", "setaccount", &setaccount, true, {"address","account"} }, |
|
|
|
{ "wallet", "setaccount", &setaccount, true, {"address","account"} }, |
|
|
|
{ "wallet", "settxfee", &settxfee, true, {"amount"} }, |
|
|
|
{ "wallet", "settxfee", &settxfee, true, {"amount"} }, |
|
|
|
{ "wallet", "signmessage", &signmessage, true, {"address","message"} }, |
|
|
|
{ "wallet", "signmessage", &signmessage, true, {"address","message"} }, |
|
|
|