Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcwallet.cpp
Show First 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | |||||
void EnsureWalletIsUnlocked(CWallet *const pwallet) { | void EnsureWalletIsUnlocked(CWallet *const pwallet) { | ||||
if (pwallet->IsLocked()) { | if (pwallet->IsLocked()) { | ||||
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, | throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, | ||||
"Error: Please enter the wallet passphrase with " | "Error: Please enter the wallet passphrase with " | ||||
"walletpassphrase first."); | "walletpassphrase first."); | ||||
} | } | ||||
} | } | ||||
static void WalletTxToJSON(interfaces::Chain &chain, const CWalletTx &wtx, | static void WalletTxToJSON(interfaces::Chain &chain, | ||||
UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | interfaces::Chain::Lock &locked_chain, | ||||
int confirms = wtx.GetDepthInMainChain(); | const CWalletTx &wtx, UniValue &entry) { | ||||
int confirms = wtx.GetDepthInMainChain(locked_chain); | |||||
entry.pushKV("confirmations", confirms); | entry.pushKV("confirmations", confirms); | ||||
if (wtx.IsCoinBase()) { | if (wtx.IsCoinBase()) { | ||||
entry.pushKV("generated", true); | entry.pushKV("generated", true); | ||||
} | } | ||||
if (confirms > 0) { | if (confirms > 0) { | ||||
entry.pushKV("blockhash", wtx.hashBlock.GetHex()); | entry.pushKV("blockhash", wtx.hashBlock.GetHex()); | ||||
entry.pushKV("blockindex", wtx.nIndex); | entry.pushKV("blockindex", wtx.nIndex); | ||||
entry.pushKV("blocktime", | entry.pushKV("blocktime", | ||||
LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); | LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); | ||||
} else { | } else { | ||||
entry.pushKV("trusted", wtx.IsTrusted()); | entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); | ||||
} | } | ||||
uint256 hash = wtx.GetId(); | uint256 hash = wtx.GetId(); | ||||
entry.pushKV("txid", hash.GetHex()); | entry.pushKV("txid", hash.GetHex()); | ||||
UniValue conflicts(UniValue::VARR); | UniValue conflicts(UniValue::VARR); | ||||
for (const uint256 &conflict : wtx.GetConflicts()) { | for (const uint256 &conflict : wtx.GetConflicts()) { | ||||
conflicts.push_back(conflict.GetHex()); | conflicts.push_back(conflict.GetHex()); | ||||
} | } | ||||
entry.pushKV("walletconflicts", conflicts); | entry.pushKV("walletconflicts", conflicts); | ||||
▲ Show 20 Lines • Show All 413 Lines • ▼ Show 20 Lines | for (const std::pair<const CTxDestination, CAddressBookData> &item : | ||||
if (strName == strAccount) { | if (strName == strAccount) { | ||||
ret.push_back(EncodeDestination(dest, config)); | ret.push_back(EncodeDestination(dest, config)); | ||||
} | } | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
static CTransactionRef SendMoney(CWallet *const pwallet, | static CTransactionRef SendMoney(interfaces::Chain::Lock &locked_chain, | ||||
CWallet *const pwallet, | |||||
const CTxDestination &address, Amount nValue, | const CTxDestination &address, Amount nValue, | ||||
bool fSubtractFeeFromAmount, | bool fSubtractFeeFromAmount, | ||||
mapValue_t mapValue, std::string fromAccount) { | mapValue_t mapValue, std::string fromAccount) { | ||||
Amount curBalance = pwallet->GetBalance(); | Amount curBalance = pwallet->GetBalance(); | ||||
// Check amount | // Check amount | ||||
if (nValue <= Amount::zero()) { | if (nValue <= Amount::zero()) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); | ||||
Show All 18 Lines | static CTransactionRef SendMoney(interfaces::Chain::Lock &locked_chain, | ||||
std::string strError; | std::string strError; | ||||
std::vector<CRecipient> vecSend; | std::vector<CRecipient> vecSend; | ||||
int nChangePosRet = -1; | int nChangePosRet = -1; | ||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | ||||
vecSend.push_back(recipient); | vecSend.push_back(recipient); | ||||
CCoinControl coinControl; | CCoinControl coinControl; | ||||
CTransactionRef tx; | CTransactionRef tx; | ||||
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, | if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, | ||||
nChangePosRet, strError, coinControl)) { | nFeeRequired, nChangePosRet, strError, | ||||
coinControl)) { | |||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { | if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { | ||||
strError = strprintf("Error: This transaction requires a " | strError = strprintf("Error: This transaction requires a " | ||||
"transaction fee of at least %s", | "transaction fee of at least %s", | ||||
FormatMoney(nFeeRequired)); | FormatMoney(nFeeRequired)); | ||||
} | } | ||||
throw JSONRPCError(RPC_WALLET_ERROR, strError); | throw JSONRPCError(RPC_WALLET_ERROR, strError); | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | static UniValue sendtoaddress(const Config &config, | ||||
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(); | ||||
} | } | ||||
EnsureWalletIsUnlocked(pwallet); | EnsureWalletIsUnlocked(pwallet); | ||||
CTransactionRef tx = | CTransactionRef tx = | ||||
SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, | SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, | ||||
std::move(mapValue), {} /* fromAccount */); | std::move(mapValue), {} /* fromAccount */); | ||||
return tx->GetId().GetHex(); | return tx->GetId().GetHex(); | ||||
} | } | ||||
static UniValue listaddressgroupings(const Config &config, | static UniValue listaddressgroupings(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | ||||
CWallet *const pwallet = wallet.get(); | CWallet *const pwallet = wallet.get(); | ||||
Show All 31 Lines | static UniValue listaddressgroupings(const Config &config, | ||||
// 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(); | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
UniValue jsonGroupings(UniValue::VARR); | UniValue jsonGroupings(UniValue::VARR); | ||||
std::map<CTxDestination, Amount> balances = pwallet->GetAddressBalances(); | std::map<CTxDestination, Amount> balances = | ||||
pwallet->GetAddressBalances(*locked_chain); | |||||
for (const std::set<CTxDestination> &grouping : | for (const std::set<CTxDestination> &grouping : | ||||
pwallet->GetAddressGroupings()) { | pwallet->GetAddressGroupings()) { | ||||
UniValue jsonGrouping(UniValue::VARR); | UniValue jsonGrouping(UniValue::VARR); | ||||
for (const CTxDestination &address : grouping) { | for (const CTxDestination &address : grouping) { | ||||
UniValue addressInfo(UniValue::VARR); | UniValue addressInfo(UniValue::VARR); | ||||
addressInfo.push_back(EncodeDestination(address, config)); | addressInfo.push_back(EncodeDestination(address, config)); | ||||
addressInfo.push_back(ValueFromAmount(balances[address])); | addressInfo.push_back(ValueFromAmount(balances[address])); | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | if (request.fHelp || request.params.size() < 1 || | ||||
HelpExampleRpc("getreceivedbyaddress", | HelpExampleRpc("getreceivedbyaddress", | ||||
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); | "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); | ||||
} | } | ||||
// 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(); | ||||
// Temporary, for ContextualCheckTransactionForCurrentBlock below. Removed | |||||
// in upcoming commit. | |||||
LockAnnotation lock(::cs_main); | |||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
// Bitcoin address | // Bitcoin address | ||||
CTxDestination dest = | CTxDestination dest = | ||||
DecodeDestination(request.params[0].get_str(), config.GetChainParams()); | DecodeDestination(request.params[0].get_str(), config.GetChainParams()); | ||||
if (!IsValidDestination(dest)) { | if (!IsValidDestination(dest)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
Show All 19 Lines | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
if (wtx.IsCoinBase() || | if (wtx.IsCoinBase() || | ||||
!ContextualCheckTransactionForCurrentBlock( | !ContextualCheckTransactionForCurrentBlock( | ||||
config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | ||||
continue; | continue; | ||||
} | } | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (const CTxOut &txout : wtx.tx->vout) { | ||||
if (txout.scriptPubKey == scriptPubKey) { | if (txout.scriptPubKey == scriptPubKey) { | ||||
if (wtx.GetDepthInMainChain() >= nMinDepth) { | if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) { | ||||
nAmount += txout.nValue; | nAmount += txout.nValue; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return ValueFromAmount(nAmount); | return ValueFromAmount(nAmount); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (request.fHelp || request.params.size() < 1 || | ||||
"\nAs a json rpc call\n" + | "\nAs a json rpc call\n" + | ||||
HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")); | HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")); | ||||
} | } | ||||
// 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(); | ||||
// Temporary, for ContextualCheckTransactionForCurrentBlock below. Removed | |||||
// in upcoming commit. | |||||
LockAnnotation lock(::cs_main); | |||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
// Minimum confirmations | // Minimum confirmations | ||||
int nMinDepth = 1; | int nMinDepth = 1; | ||||
if (!request.params[1].isNull()) { | if (!request.params[1].isNull()) { | ||||
nMinDepth = request.params[1].get_int(); | nMinDepth = request.params[1].get_int(); | ||||
} | } | ||||
Show All 12 Lines | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | ||||
continue; | continue; | ||||
} | } | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (const CTxOut &txout : wtx.tx->vout) { | ||||
CTxDestination address; | CTxDestination address; | ||||
if (ExtractDestination(txout.scriptPubKey, address) && | if (ExtractDestination(txout.scriptPubKey, address) && | ||||
IsMine(*pwallet, address) && setAddress.count(address)) { | IsMine(*pwallet, address) && setAddress.count(address)) { | ||||
if (wtx.GetDepthInMainChain() >= nMinDepth) { | if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) { | ||||
nAmount += txout.nValue; | nAmount += txout.nValue; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return ValueFromAmount(nAmount); | return ValueFromAmount(nAmount); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | static UniValue sendfrom(const Config &config, const JSONRPCRequest &request) { | ||||
// Check funds | // Check funds | ||||
Amount nBalance = | Amount nBalance = | ||||
pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &label); | pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &label); | ||||
if (nAmount > nBalance) { | if (nAmount > nBalance) { | ||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | ||||
"Account has insufficient funds"); | "Account has insufficient funds"); | ||||
} | } | ||||
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, false, | CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, false, | ||||
std::move(mapValue), std::move(label)); | std::move(mapValue), std::move(label)); | ||||
return tx->GetId().GetHex(); | return tx->GetId().GetHex(); | ||||
} | } | ||||
static UniValue sendmany(const Config &config, const JSONRPCRequest &request) { | static UniValue sendmany(const Config &config, const JSONRPCRequest &request) { | ||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | ||||
CWallet *const pwallet = wallet.get(); | CWallet *const pwallet = wallet.get(); | ||||
▲ Show 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | static UniValue sendmany(const Config &config, const JSONRPCRequest &request) { | ||||
// Send | // Send | ||||
CReserveKey keyChange(pwallet); | CReserveKey keyChange(pwallet); | ||||
Amount nFeeRequired = Amount::zero(); | Amount nFeeRequired = Amount::zero(); | ||||
int nChangePosRet = -1; | int nChangePosRet = -1; | ||||
std::string strFailReason; | std::string strFailReason; | ||||
CTransactionRef tx; | CTransactionRef tx; | ||||
CCoinControl coinControl; | CCoinControl coinControl; | ||||
bool fCreated = | bool fCreated = pwallet->CreateTransaction( | ||||
pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, | *locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, | ||||
nChangePosRet, strFailReason, coinControl); | strFailReason, coinControl); | ||||
if (!fCreated) { | if (!fCreated) { | ||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, | if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, | ||||
std::move(strAccount), keyChange, | std::move(strAccount), keyChange, | ||||
g_connman.get(), state)) { | g_connman.get(), state)) { | ||||
strFailReason = strprintf("Transaction commit failed:: %s", | strFailReason = strprintf("Transaction commit failed:: %s", | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | |||||
struct tallyitem { | struct tallyitem { | ||||
Amount nAmount{Amount::zero()}; | Amount nAmount{Amount::zero()}; | ||||
int nConf{std::numeric_limits<int>::max()}; | int nConf{std::numeric_limits<int>::max()}; | ||||
std::vector<uint256> txids; | std::vector<uint256> txids; | ||||
bool fIsWatchonly{false}; | bool fIsWatchonly{false}; | ||||
tallyitem() {} | tallyitem() {} | ||||
}; | }; | ||||
static UniValue ListReceived(const Config &config, CWallet *const pwallet, | static UniValue | ||||
const UniValue ¶ms, bool by_label) | ListReceived(const Config &config, interfaces::Chain::Lock &locked_chain, | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet) { | CWallet *const pwallet, const UniValue ¶ms, bool by_label) | ||||
EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { | |||||
// Temporary, for ContextualCheckTransactionForCurrentBlock below. Removed | |||||
// in upcoming commit. | |||||
LockAnnotation lock(::cs_main); | |||||
// Minimum confirmations | // Minimum confirmations | ||||
int nMinDepth = 1; | int nMinDepth = 1; | ||||
if (!params[0].isNull()) { | if (!params[0].isNull()) { | ||||
nMinDepth = params[0].get_int(); | nMinDepth = params[0].get_int(); | ||||
} | } | ||||
// Whether to include empty labels | // Whether to include empty labels | ||||
bool fIncludeEmpty = false; | bool fIncludeEmpty = false; | ||||
Show All 26 Lines | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
CValidationState state; | CValidationState state; | ||||
if (wtx.IsCoinBase() || | if (wtx.IsCoinBase() || | ||||
!ContextualCheckTransactionForCurrentBlock( | !ContextualCheckTransactionForCurrentBlock( | ||||
config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | config.GetChainParams().GetConsensus(), *wtx.tx, state)) { | ||||
continue; | continue; | ||||
} | } | ||||
int nDepth = wtx.GetDepthInMainChain(); | int nDepth = wtx.GetDepthInMainChain(locked_chain); | ||||
if (nDepth < nMinDepth) { | if (nDepth < nMinDepth) { | ||||
continue; | continue; | ||||
} | } | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (const CTxOut &txout : wtx.tx->vout) { | ||||
CTxDestination address; | CTxDestination address; | ||||
if (!ExtractDestination(txout.scriptPubKey, address)) { | if (!ExtractDestination(txout.scriptPubKey, address)) { | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | static UniValue listreceivedbyaddress(const Config &config, | ||||
// 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(); | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
return ListReceived(config, pwallet, request.params, false); | return ListReceived(config, *locked_chain, pwallet, request.params, false); | ||||
} | } | ||||
static UniValue listreceivedbylabel(const Config &config, | static UniValue listreceivedbylabel(const Config &config, | ||||
const JSONRPCRequest &request) { | const JSONRPCRequest &request) { | ||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | ||||
CWallet *const pwallet = wallet.get(); | CWallet *const pwallet = wallet.get(); | ||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { | if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | static UniValue listreceivedbylabel(const Config &config, | ||||
// 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(); | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
return ListReceived(config, pwallet, request.params, true); | return ListReceived(config, *locked_chain, pwallet, request.params, true); | ||||
} | } | ||||
static void MaybePushAddress(UniValue &entry, const CTxDestination &dest) { | static void MaybePushAddress(UniValue &entry, const CTxDestination &dest) { | ||||
if (IsValidDestination(dest)) { | if (IsValidDestination(dest)) { | ||||
entry.pushKV("address", EncodeDestination(dest, GetConfig())); | entry.pushKV("address", EncodeDestination(dest, GetConfig())); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* List transactions based on the given criteria. | * List transactions based on the given criteria. | ||||
* | * | ||||
* @param pwallet The wallet. | * @param pwallet The wallet. | ||||
* @param wtx The wallet transaction. | * @param wtx The wallet transaction. | ||||
* @param strAccount The account, if any, or "*" for all. | * @param strAccount The account, if any, or "*" for all. | ||||
* @param nMinDepth The minimum confirmation depth. | * @param nMinDepth The minimum confirmation depth. | ||||
* @param fLong Whether to include the JSON version of the transaction. | * @param fLong Whether to include the JSON version of the transaction. | ||||
* @param ret The UniValue into which the result is stored. | * @param ret The UniValue into which the result is stored. | ||||
* @param filter The "is mine" filter bool. | * @param filter The "is mine" filter bool. | ||||
*/ | */ | ||||
static void ListTransactions(CWallet *const pwallet, const CWalletTx &wtx, | static void ListTransactions(interfaces::Chain::Lock &locked_chain, | ||||
CWallet *const pwallet, const CWalletTx &wtx, | |||||
const std::string &strAccount, int nMinDepth, | const std::string &strAccount, int nMinDepth, | ||||
bool fLong, UniValue &ret, | bool fLong, UniValue &ret, | ||||
const isminefilter &filter) | const isminefilter &filter) { | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | |||||
Amount nFee; | Amount nFee; | ||||
std::string strSentAccount; | std::string strSentAccount; | ||||
std::list<COutputEntry> listReceived; | std::list<COutputEntry> listReceived; | ||||
std::list<COutputEntry> listSent; | std::list<COutputEntry> listSent; | ||||
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); | ||||
bool fAllAccounts = (strAccount == std::string("*")); | bool fAllAccounts = (strAccount == std::string("*")); | ||||
Show All 16 Lines | if ((!listSent.empty() || nFee != Amount::zero()) && | ||||
entry.pushKV("amount", ValueFromAmount(-s.amount)); | entry.pushKV("amount", ValueFromAmount(-s.amount)); | ||||
if (pwallet->mapAddressBook.count(s.destination)) { | if (pwallet->mapAddressBook.count(s.destination)) { | ||||
entry.pushKV("label", | entry.pushKV("label", | ||||
pwallet->mapAddressBook[s.destination].name); | pwallet->mapAddressBook[s.destination].name); | ||||
} | } | ||||
entry.pushKV("vout", s.vout); | entry.pushKV("vout", s.vout); | ||||
entry.pushKV("fee", ValueFromAmount(-1 * nFee)); | entry.pushKV("fee", ValueFromAmount(-1 * nFee)); | ||||
if (fLong) { | if (fLong) { | ||||
WalletTxToJSON(pwallet->chain(), wtx, entry); | WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry); | ||||
} | } | ||||
entry.pushKV("abandoned", wtx.isAbandoned()); | entry.pushKV("abandoned", wtx.isAbandoned()); | ||||
ret.push_back(entry); | ret.push_back(entry); | ||||
} | } | ||||
} | } | ||||
// Received | // Received | ||||
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { | if (listReceived.size() > 0 && | ||||
wtx.GetDepthInMainChain(locked_chain) >= nMinDepth) { | |||||
for (const COutputEntry &r : listReceived) { | for (const COutputEntry &r : listReceived) { | ||||
std::string account; | std::string account; | ||||
if (pwallet->mapAddressBook.count(r.destination)) { | if (pwallet->mapAddressBook.count(r.destination)) { | ||||
account = pwallet->mapAddressBook[r.destination].name; | account = pwallet->mapAddressBook[r.destination].name; | ||||
} | } | ||||
if (fAllAccounts || (account == strAccount)) { | if (fAllAccounts || (account == strAccount)) { | ||||
UniValue entry(UniValue::VOBJ); | UniValue entry(UniValue::VOBJ); | ||||
if (involvesWatchonly || | if (involvesWatchonly || | ||||
(::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { | (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { | ||||
entry.pushKV("involvesWatchonly", true); | entry.pushKV("involvesWatchonly", true); | ||||
} | } | ||||
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { | if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { | ||||
entry.pushKV("account", account); | entry.pushKV("account", account); | ||||
} | } | ||||
MaybePushAddress(entry, r.destination); | MaybePushAddress(entry, r.destination); | ||||
if (wtx.IsCoinBase()) { | if (wtx.IsCoinBase()) { | ||||
if (wtx.GetDepthInMainChain() < 1) { | if (wtx.GetDepthInMainChain(locked_chain) < 1) { | ||||
entry.pushKV("category", "orphan"); | entry.pushKV("category", "orphan"); | ||||
} else if (wtx.IsImmatureCoinBase()) { | } else if (wtx.IsImmatureCoinBase(locked_chain)) { | ||||
entry.pushKV("category", "immature"); | entry.pushKV("category", "immature"); | ||||
} else { | } else { | ||||
entry.pushKV("category", "generate"); | entry.pushKV("category", "generate"); | ||||
} | } | ||||
} else { | } else { | ||||
entry.pushKV("category", "receive"); | entry.pushKV("category", "receive"); | ||||
} | } | ||||
entry.pushKV("amount", ValueFromAmount(r.amount)); | entry.pushKV("amount", ValueFromAmount(r.amount)); | ||||
if (pwallet->mapAddressBook.count(r.destination)) { | if (pwallet->mapAddressBook.count(r.destination)) { | ||||
entry.pushKV("label", account); | entry.pushKV("label", account); | ||||
} | } | ||||
entry.pushKV("vout", r.vout); | entry.pushKV("vout", r.vout); | ||||
if (fLong) { | if (fLong) { | ||||
WalletTxToJSON(pwallet->chain(), wtx, entry); | WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry); | ||||
} | } | ||||
ret.push_back(entry); | ret.push_back(entry); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void AcentryToJSON(const CAccountingEntry &acentry, | static void AcentryToJSON(const CAccountingEntry &acentry, | ||||
▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | UniValue listtransactions(const Config &config, const JSONRPCRequest &request) { | ||||
const CWallet::TxItems &txOrdered = pwallet->wtxOrdered; | const CWallet::TxItems &txOrdered = pwallet->wtxOrdered; | ||||
// iterate backwards until we have nCount items to return: | // iterate backwards until we have nCount items to return: | ||||
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); | for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); | ||||
it != txOrdered.rend(); ++it) { | it != txOrdered.rend(); ++it) { | ||||
CWalletTx *const pwtx = (*it).second.first; | CWalletTx *const pwtx = (*it).second.first; | ||||
if (pwtx != nullptr) { | if (pwtx != nullptr) { | ||||
ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); | ListTransactions(*locked_chain, pwallet, *pwtx, strAccount, 0, true, | ||||
ret, filter); | |||||
} | } | ||||
if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { | if (IsDeprecatedRPCEnabled(gArgs, "accounts")) { | ||||
CAccountingEntry *const pacentry = (*it).second.second; | CAccountingEntry *const pacentry = (*it).second.second; | ||||
if (pacentry != nullptr) { | if (pacentry != nullptr) { | ||||
AcentryToJSON(*pacentry, strAccount, ret); | AcentryToJSON(*pacentry, strAccount, ret); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | static UniValue listaccounts(const Config &config, | ||||
} | } | ||||
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
const CWalletTx &wtx = pairWtx.second; | const CWalletTx &wtx = pairWtx.second; | ||||
Amount nFee; | Amount nFee; | ||||
std::string strSentAccount; | std::string strSentAccount; | ||||
std::list<COutputEntry> listReceived; | std::list<COutputEntry> listReceived; | ||||
std::list<COutputEntry> listSent; | std::list<COutputEntry> listSent; | ||||
int nDepth = wtx.GetDepthInMainChain(); | int nDepth = wtx.GetDepthInMainChain(*locked_chain); | ||||
if (wtx.IsImmatureCoinBase() || nDepth < 0) { | if (wtx.IsImmatureCoinBase(*locked_chain) || nDepth < 0) { | ||||
continue; | continue; | ||||
} | } | ||||
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, | ||||
includeWatchonly); | includeWatchonly); | ||||
mapAccountBalances[strSentAccount] -= nFee; | mapAccountBalances[strSentAccount] -= nFee; | ||||
for (const COutputEntry &s : listSent) { | for (const COutputEntry &s : listSent) { | ||||
mapAccountBalances[strSentAccount] -= s.amount; | mapAccountBalances[strSentAccount] -= s.amount; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | static UniValue listsinceblock(const Config &config, | ||||
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; | int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; | ||||
UniValue transactions(UniValue::VARR); | UniValue transactions(UniValue::VARR); | ||||
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
CWalletTx tx = pairWtx.second; | CWalletTx tx = pairWtx.second; | ||||
if (depth == -1 || tx.GetDepthInMainChain() < depth) { | if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) { | ||||
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter); | ListTransactions(*locked_chain, pwallet, tx, "*", 0, true, | ||||
transactions, filter); | |||||
} | } | ||||
} | } | ||||
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | ||||
// when a reorg'd block is requested, we also list any relevant transactions | // when a reorg'd block is requested, we also list any relevant transactions | ||||
// in the blocks of the chain that was detached | // in the blocks of the chain that was detached | ||||
UniValue removed(UniValue::VARR); | UniValue removed(UniValue::VARR); | ||||
while (include_removed && paltindex && paltindex != pindex) { | while (include_removed && paltindex && paltindex != pindex) { | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, paltindex, params)) { | if (!ReadBlockFromDisk(block, paltindex, params)) { | ||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | throw JSONRPCError(RPC_INTERNAL_ERROR, | ||||
"Can't read block from disk"); | "Can't read block from disk"); | ||||
} | } | ||||
for (const CTransactionRef &tx : block.vtx) { | for (const CTransactionRef &tx : block.vtx) { | ||||
auto it = pwallet->mapWallet.find(tx->GetId()); | auto it = pwallet->mapWallet.find(tx->GetId()); | ||||
if (it != pwallet->mapWallet.end()) { | if (it != pwallet->mapWallet.end()) { | ||||
// We want all transactions regardless of confirmation count to | // We want all transactions regardless of confirmation count to | ||||
// appear here, even negative confirmation ones, hence the big | // appear here, even negative confirmation ones, hence the big | ||||
// negative. | // negative. | ||||
ListTransactions(pwallet, it->second, "*", -100000000, true, | ListTransactions(*locked_chain, pwallet, it->second, "*", | ||||
removed, filter); | -100000000, true, removed, filter); | ||||
} | } | ||||
} | } | ||||
paltindex = paltindex->pprev; | paltindex = paltindex->pprev; | ||||
} | } | ||||
CBlockIndex *pblockLast = | CBlockIndex *pblockLast = | ||||
chainActive[chainActive.Height() + 1 - target_confirms]; | chainActive[chainActive.Height() + 1 - target_confirms]; | ||||
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); | uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | static UniValue gettransaction(const Config &config, | ||||
UniValue entry(UniValue::VOBJ); | UniValue entry(UniValue::VOBJ); | ||||
auto it = pwallet->mapWallet.find(txid); | auto it = pwallet->mapWallet.find(txid); | ||||
if (it == pwallet->mapWallet.end()) { | if (it == pwallet->mapWallet.end()) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid or non-wallet transaction id"); | "Invalid or non-wallet transaction id"); | ||||
} | } | ||||
const CWalletTx &wtx = it->second; | const CWalletTx &wtx = it->second; | ||||
Amount nCredit = wtx.GetCredit(filter); | Amount nCredit = wtx.GetCredit(*locked_chain, filter); | ||||
Amount nDebit = wtx.GetDebit(filter); | Amount nDebit = wtx.GetDebit(filter); | ||||
Amount nNet = nCredit - nDebit; | Amount nNet = nCredit - nDebit; | ||||
Amount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit | Amount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit | ||||
: Amount::zero()); | : Amount::zero()); | ||||
entry.pushKV("amount", ValueFromAmount(nNet - nFee)); | entry.pushKV("amount", ValueFromAmount(nNet - nFee)); | ||||
if (wtx.IsFromMe(filter)) { | if (wtx.IsFromMe(filter)) { | ||||
entry.pushKV("fee", ValueFromAmount(nFee)); | entry.pushKV("fee", ValueFromAmount(nFee)); | ||||
} | } | ||||
WalletTxToJSON(pwallet->chain(), wtx, entry); | WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry); | ||||
UniValue details(UniValue::VARR); | UniValue details(UniValue::VARR); | ||||
ListTransactions(pwallet, wtx, "*", 0, false, details, filter); | ListTransactions(*locked_chain, pwallet, wtx, "*", 0, false, details, | ||||
filter); | |||||
entry.pushKV("details", details); | entry.pushKV("details", details); | ||||
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); | std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); | ||||
entry.pushKV("hex", strHex); | entry.pushKV("hex", strHex); | ||||
return entry; | return entry; | ||||
} | } | ||||
Show All 39 Lines | static UniValue abandontransaction(const Config &config, | ||||
TxId txid; | TxId txid; | ||||
txid.SetHex(request.params[0].get_str()); | txid.SetHex(request.params[0].get_str()); | ||||
if (!pwallet->mapWallet.count(txid)) { | if (!pwallet->mapWallet.count(txid)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Invalid or non-wallet transaction id"); | "Invalid or non-wallet transaction id"); | ||||
} | } | ||||
if (!pwallet->AbandonTransaction(txid)) { | if (!pwallet->AbandonTransaction(*locked_chain, txid)) { | ||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | ||||
"Transaction not eligible for abandonment"); | "Transaction not eligible for abandonment"); | ||||
} | } | ||||
return NullUniValue; | return NullUniValue; | ||||
} | } | ||||
static UniValue backupwallet(const Config &config, | static UniValue backupwallet(const Config &config, | ||||
▲ Show 20 Lines • Show All 491 Lines • ▼ Show 20 Lines | for (size_t idx = 0; idx < output_params.size(); idx++) { | ||||
const COutPoint output(txid, nOutput); | const COutPoint output(txid, nOutput); | ||||
const CWalletTx &trans = it->second; | const CWalletTx &trans = it->second; | ||||
if (output.GetN() >= trans.tx->vout.size()) { | if (output.GetN() >= trans.tx->vout.size()) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Invalid parameter, vout index out of bounds"); | "Invalid parameter, vout index out of bounds"); | ||||
} | } | ||||
if (pwallet->IsSpent(output)) { | if (pwallet->IsSpent(*locked_chain, output)) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Invalid parameter, expected unspent output"); | "Invalid parameter, expected unspent output"); | ||||
} | } | ||||
const bool is_locked = pwallet->IsLockedCoin(output); | const bool is_locked = pwallet->IsLockedCoin(output); | ||||
if (fUnlock && !is_locked) { | if (fUnlock && !is_locked) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Invalid parameter, expected locked output"); | "Invalid parameter, expected locked output"); | ||||
▲ Show 20 Lines • Show All 487 Lines • ▼ Show 20 Lines | static UniValue resendwallettransactions(const Config &config, | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
if (!pwallet->GetBroadcastTransactions()) { | if (!pwallet->GetBroadcastTransactions()) { | ||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction " | throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction " | ||||
"broadcasting is disabled with " | "broadcasting is disabled with " | ||||
"-walletbroadcast"); | "-walletbroadcast"); | ||||
} | } | ||||
std::vector<uint256> txids = | std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore( | ||||
pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get()); | *locked_chain, GetTime(), g_connman.get()); | ||||
UniValue result(UniValue::VARR); | UniValue result(UniValue::VARR); | ||||
for (const uint256 &txid : txids) { | for (const uint256 &txid : txids) { | ||||
result.push_back(txid.ToString()); | result.push_back(txid.ToString()); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 167 Lines • ▼ Show 20 Lines | static UniValue listunspent(const Config &config, | ||||
// 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(); | ||||
UniValue results(UniValue::VARR); | UniValue results(UniValue::VARR); | ||||
std::vector<COutput> vecOutputs; | std::vector<COutput> vecOutputs; | ||||
{ | { | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, | pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, | ||||
nMinimumAmount, nMaximumAmount, | nullptr, nMinimumAmount, nMaximumAmount, | ||||
nMinimumSumAmount, nMaximumCount, nMinDepth, | nMinimumSumAmount, nMaximumCount, nMinDepth, | ||||
nMaxDepth); | nMaxDepth); | ||||
} | } | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
for (const COutput &out : vecOutputs) { | for (const COutput &out : vecOutputs) { | ||||
CTxDestination address; | CTxDestination address; | ||||
▲ Show 20 Lines • Show All 1,344 Lines • Show Last 20 Lines |