Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcwallet.cpp
Show First 20 Lines • Show All 348 Lines • ▼ Show 20 Lines | if (pwallet->IsMine(dest)) { | ||||
pwallet->SetAddressBook(dest, label, "receive"); | pwallet->SetAddressBook(dest, label, "receive"); | ||||
} else { | } else { | ||||
pwallet->SetAddressBook(dest, label, "send"); | pwallet->SetAddressBook(dest, label, "send"); | ||||
} | } | ||||
return NullUniValue; | return NullUniValue; | ||||
} | } | ||||
static CTransactionRef SendMoney(CWallet *const pwallet, | void ParseRecipients(const UniValue &address_amounts, | ||||
const CTxDestination &address, Amount nValue, | const UniValue &subtract_fee_outputs, | ||||
bool fSubtractFeeFromAmount, | std::vector<CRecipient> &recipients, | ||||
const CCoinControl &coin_control, | const CChainParams &chainParams) { | ||||
mapValue_t mapValue) { | std::set<CTxDestination> destinations; | ||||
Amount curBalance = | int i = 0; | ||||
pwallet->GetBalance(0, coin_control.m_avoid_address_reuse) | for (const std::string &address : address_amounts.getKeys()) { | ||||
.m_mine_trusted; | CTxDestination dest = DecodeDestination(address, chainParams); | ||||
if (!IsValidDestination(dest)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
std::string("Invalid Bitcoin address: ") + | |||||
address); | |||||
} | |||||
// Check amount | if (destinations.count(dest)) { | ||||
if (nValue <= Amount::zero()) { | throw JSONRPCError( | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); | RPC_INVALID_PARAMETER, | ||||
std::string("Invalid parameter, duplicated address: ") + | |||||
address); | |||||
} | } | ||||
destinations.insert(dest); | |||||
if (nValue > curBalance) { | CScript script_pub_key = GetScriptForDestination(dest); | ||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); | Amount amount = AmountFromValue(address_amounts[i++]); | ||||
bool subtract_fee = false; | |||||
for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) { | |||||
const UniValue &addr = subtract_fee_outputs[idx]; | |||||
if (addr.get_str() == address) { | |||||
subtract_fee = true; | |||||
} | |||||
} | |||||
CRecipient recipient = {script_pub_key, amount, subtract_fee}; | |||||
recipients.push_back(recipient); | |||||
} | |||||
} | } | ||||
// Parse Bitcoin address | UniValue SendMoney(CWallet *const pwallet, const CCoinControl &coin_control, | ||||
CScript scriptPubKey = GetScriptForDestination(address); | std::vector<CRecipient> &recipients, mapValue_t map_value) { | ||||
EnsureWalletIsUnlocked(pwallet); | |||||
// Shuffle recipient list | |||||
std::shuffle(recipients.begin(), recipients.end(), FastRandomContext()); | |||||
// Create and send the transaction | // Send | ||||
Amount nFeeRequired = Amount::zero(); | Amount nFeeRequired = Amount::zero(); | ||||
bilingual_str error; | |||||
std::vector<CRecipient> vecSend; | |||||
int nChangePosRet = -1; | int nChangePosRet = -1; | ||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | bilingual_str error; | ||||
vecSend.push_back(recipient); | |||||
CTransactionRef tx; | CTransactionRef tx; | ||||
if (!pwallet->CreateTransaction(vecSend, tx, nFeeRequired, nChangePosRet, | bool fCreated = pwallet->CreateTransaction( | ||||
error, coin_control)) { | recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, | ||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { | !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); | ||||
error = strprintf(Untranslated("Error: This transaction requires a " | if (!fCreated) { | ||||
"transaction fee of at least %s"), | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original); | ||||
FormatMoney(nFeeRequired)); | |||||
} | |||||
throw JSONRPCError(RPC_WALLET_ERROR, error.original); | |||||
} | } | ||||
pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */); | pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */); | ||||
return tx; | return tx->GetId().GetHex(); | ||||
} | } | ||||
static UniValue sendtoaddress(const Config &config, | static UniValue sendtoaddress(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
RPCHelpMan{ | RPCHelpMan{ | ||||
"sendtoaddress", | "sendtoaddress", | ||||
"Send an amount to a given address.\n" + HELP_REQUIRING_PASSPHRASE, | "Send an amount to a given address.\n" + HELP_REQUIRING_PASSPHRASE, | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | static UniValue sendtoaddress(const Config &config, | ||||
CWallet *const pwallet = wallet.get(); | CWallet *const pwallet = wallet.get(); | ||||
// Make sure the results are valid at least up to the most recent block | // Make sure the results are valid at least up to the most recent block | ||||
// the user could have gotten from another RPC command prior to now | // the user could have gotten from another RPC command prior to now | ||||
pwallet->BlockUntilSyncedToCurrentChain(); | pwallet->BlockUntilSyncedToCurrentChain(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
CTxDestination dest = DecodeDestination(request.params[0].get_str(), | |||||
wallet->GetChainParams()); | |||||
if (!IsValidDestination(dest)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |||||
} | |||||
// Amount | |||||
Amount nAmount = AmountFromValue(request.params[1]); | |||||
if (nAmount <= Amount::zero()) { | |||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |||||
} | |||||
// Wallet comments | // Wallet comments | ||||
mapValue_t mapValue; | mapValue_t mapValue; | ||||
if (!request.params[2].isNull() && !request.params[2].get_str().empty()) { | if (!request.params[2].isNull() && !request.params[2].get_str().empty()) { | ||||
mapValue["comment"] = request.params[2].get_str(); | mapValue["comment"] = request.params[2].get_str(); | ||||
} | } | ||||
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { | if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { | ||||
mapValue["to"] = request.params[3].get_str(); | mapValue["to"] = request.params[3].get_str(); | ||||
} | } | ||||
bool fSubtractFeeFromAmount = false; | bool fSubtractFeeFromAmount = false; | ||||
if (!request.params[4].isNull()) { | if (!request.params[4].isNull()) { | ||||
fSubtractFeeFromAmount = request.params[4].get_bool(); | fSubtractFeeFromAmount = request.params[4].get_bool(); | ||||
} | } | ||||
CCoinControl coin_control; | CCoinControl coin_control; | ||||
coin_control.m_avoid_address_reuse = | coin_control.m_avoid_address_reuse = | ||||
GetAvoidReuseFlag(pwallet, request.params[5]); | GetAvoidReuseFlag(pwallet, request.params[5]); | ||||
// We also enable partial spend avoidance if reuse avoidance is set. | // We also enable partial spend avoidance if reuse avoidance is set. | ||||
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse; | coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse; | ||||
EnsureWalletIsUnlocked(pwallet); | EnsureWalletIsUnlocked(pwallet); | ||||
CTransactionRef tx = | UniValue address_amounts(UniValue::VOBJ); | ||||
SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, | const std::string address = request.params[0].get_str(); | ||||
std::move(mapValue)); | address_amounts.pushKV(address, request.params[1]); | ||||
return tx->GetId().GetHex(); | UniValue subtractFeeFromAmount(UniValue::VARR); | ||||
if (fSubtractFeeFromAmount) { | |||||
subtractFeeFromAmount.push_back(address); | |||||
} | |||||
std::vector<CRecipient> recipients; | |||||
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients, | |||||
wallet->GetChainParams()); | |||||
return SendMoney(pwallet, coin_control, recipients, mapValue); | |||||
} | } | ||||
static UniValue listaddressgroupings(const Config &config, | static UniValue listaddressgroupings(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
RPCHelpMan{ | RPCHelpMan{ | ||||
"listaddressgroupings", | "listaddressgroupings", | ||||
"Lists groups of addresses which have had their common ownership\n" | "Lists groups of addresses which have had their common ownership\n" | ||||
"made public by common use as inputs or as the resulting change\n" | "made public by common use as inputs or as the resulting change\n" | ||||
▲ Show 20 Lines • Show All 480 Lines • ▼ Show 20 Lines | if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { | ||||
mapValue["comment"] = request.params[3].get_str(); | mapValue["comment"] = request.params[3].get_str(); | ||||
} | } | ||||
UniValue subtractFeeFromAmount(UniValue::VARR); | UniValue subtractFeeFromAmount(UniValue::VARR); | ||||
if (!request.params[4].isNull()) { | if (!request.params[4].isNull()) { | ||||
subtractFeeFromAmount = request.params[4].get_array(); | subtractFeeFromAmount = request.params[4].get_array(); | ||||
} | } | ||||
std::set<CTxDestination> destinations; | std::vector<CRecipient> recipients; | ||||
std::vector<CRecipient> vecSend; | ParseRecipients(sendTo, subtractFeeFromAmount, recipients, | ||||
wallet->GetChainParams()); | |||||
std::vector<std::string> keys = sendTo.getKeys(); | |||||
for (const std::string &name_ : keys) { | |||||
CTxDestination dest = | |||||
DecodeDestination(name_, wallet->GetChainParams()); | |||||
if (!IsValidDestination(dest)) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |||||
std::string("Invalid Bitcoin address: ") + | |||||
name_); | |||||
} | |||||
if (destinations.count(dest)) { | |||||
throw JSONRPCError( | |||||
RPC_INVALID_PARAMETER, | |||||
std::string("Invalid parameter, duplicated address: ") + name_); | |||||
} | |||||
destinations.insert(dest); | |||||
CScript scriptPubKey = GetScriptForDestination(dest); | |||||
Amount nAmount = AmountFromValue(sendTo[name_]); | |||||
if (nAmount <= Amount::zero()) { | |||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |||||
} | |||||
bool fSubtractFeeFromAmount = false; | |||||
for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) { | |||||
const UniValue &addr = subtractFeeFromAmount[idx]; | |||||
if (addr.get_str() == name_) { | |||||
fSubtractFeeFromAmount = true; | |||||
} | |||||
} | |||||
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; | |||||
vecSend.push_back(recipient); | |||||
} | |||||
EnsureWalletIsUnlocked(pwallet); | |||||
// Shuffle recipient list | |||||
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext()); | |||||
// Send | CCoinControl coin_control; | ||||
Amount nFeeRequired = Amount::zero(); | return SendMoney(pwallet, coin_control, recipients, std::move(mapValue)); | ||||
int nChangePosRet = -1; | |||||
bilingual_str error; | |||||
CTransactionRef tx; | |||||
CCoinControl coinControl; | |||||
bool fCreated = pwallet->CreateTransaction( | |||||
vecSend, tx, nFeeRequired, nChangePosRet, error, coinControl); | |||||
if (!fCreated) { | |||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original); | |||||
} | |||||
pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */); | |||||
return tx->GetId().GetHex(); | |||||
} | } | ||||
static UniValue addmultisigaddress(const Config &config, | static UniValue addmultisigaddress(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
RPCHelpMan{ | RPCHelpMan{ | ||||
"addmultisigaddress", | "addmultisigaddress", | ||||
"Add an nrequired-to-sign multisignature address to the wallet. " | "Add an nrequired-to-sign multisignature address to the wallet. " | ||||
"Requires a new wallet backup.\n" | "Requires a new wallet backup.\n" | ||||
▲ Show 20 Lines • Show All 3,948 Lines • Show Last 20 Lines |