aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/feebumper.cpp50
-rw-r--r--src/wallet/feebumper.h3
-rw-r--r--src/wallet/rpcwallet.cpp48
-rw-r--r--src/wallet/wallet.cpp52
-rw-r--r--src/wallet/wallet.h2
5 files changed, 117 insertions, 38 deletions
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index b3cb6a718c..c10a9eccd9 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -41,6 +41,31 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal
return GetVirtualTransactionSize(txNew);
}
+bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx) {
+ if (pWallet->HasWalletSpend(wtx.GetHash())) {
+ vErrors.push_back("Transaction has descendants in the wallet");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return false;
+ }
+
+ {
+ LOCK(mempool.cs);
+ auto it_mp = mempool.mapTx.find(wtx.GetHash());
+ if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
+ vErrors.push_back("Transaction has descendants in the mempool");
+ currentResult = BumpFeeResult::INVALID_PARAMETER;
+ return false;
+ }
+ }
+
+ if (wtx.GetDepthInMainChain() != 0) {
+ vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
+ currentResult = BumpFeeResult::WALLET_ERROR;
+ return false;
+ }
+ return true;
+}
+
CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool specifiedConfirmTarget, CAmount totalFee, bool newTxReplaceable)
:
txid(std::move(txidIn)),
@@ -58,25 +83,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
auto it = pWallet->mapWallet.find(txid);
const CWalletTx& wtx = it->second;
- if (pWallet->HasWalletSpend(txid)) {
- vErrors.push_back("Transaction has descendants in the wallet");
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return;
- }
-
- {
- LOCK(mempool.cs);
- auto it_mp = mempool.mapTx.find(txid);
- if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
- vErrors.push_back("Transaction has descendants in the mempool");
- currentResult = BumpFeeResult::INVALID_PARAMETER;
- return;
- }
- }
-
- if (wtx.GetDepthInMainChain() != 0) {
- vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
- currentResult = BumpFeeResult::WALLET_ERROR;
+ if (!preconditionChecks(pWallet, wtx)) {
return;
}
@@ -248,6 +255,11 @@ bool CFeeBumper::commit(CWallet *pWallet)
}
CWalletTx& oldWtx = pWallet->mapWallet[txid];
+ // make sure the transaction still has no descendants and hasen't been mined in the meantime
+ if (!preconditionChecks(pWallet, oldWtx)) {
+ return false;
+ }
+
CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx)));
// commit/broadcast the tx
CReserveKey reservekey(pWallet);
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 681f55e4e5..f40d05da28 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -8,6 +8,7 @@
#include <primitives/transaction.h>
class CWallet;
+class CWalletTx;
class uint256;
enum class BumpFeeResult
@@ -44,6 +45,8 @@ public:
bool commit(CWallet *pWalletNonConst);
private:
+ bool preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx);
+
const uint256 txid;
uint256 bumpedTxid;
CMutableTransaction mtx;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index fa67964ce8..ae4f4f37cb 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2470,22 +2470,29 @@ UniValue listunspent(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 4)
+ if (request.fHelp || request.params.size() > 5)
throw std::runtime_error(
- "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n"
+ "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n"
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
- "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n"
+ "3. \"addresses\" (string) A json array of bitcoin addresses to filter\n"
" [\n"
- " \"address\" (string) bitcoin address\n"
+ " \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
"4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
" See description of \"safe\" attribute below.\n"
+ "5. query_options (json, optional) JSON with query options\n"
+ " {\n"
+ " \"minimumAmount\" (numeric or string, default=0) Minimum value of each UTXO in " + CURRENCY_UNIT + "\n"
+ " \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
+ " \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
+ " \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
+ " }\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
@@ -2510,6 +2517,8 @@ UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "")
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
+ + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
);
int nMinDepth = 1;
@@ -2545,15 +2554,34 @@ UniValue listunspent(const JSONRPCRequest& request)
include_unsafe = request.params[3].get_bool();
}
+ CAmount nMinimumAmount = 0;
+ CAmount nMaximumAmount = MAX_MONEY;
+ CAmount nMinimumSumAmount = MAX_MONEY;
+ uint64_t nMaximumCount = 0;
+
+ if (request.params.size() > 4) {
+ const UniValue& options = request.params[4].get_obj();
+
+ if (options.exists("minimumAmount"))
+ nMinimumAmount = AmountFromValue(options["minimumAmount"]);
+
+ if (options.exists("maximumAmount"))
+ nMaximumAmount = AmountFromValue(options["maximumAmount"]);
+
+ if (options.exists("minimumSumAmount"))
+ nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
+
+ if (options.exists("maximumCount"))
+ nMaximumCount = options["maximumCount"].get_int64();
+ }
+
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
assert(pwallet != NULL);
LOCK2(cs_main, pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, !include_unsafe, NULL, true);
- BOOST_FOREACH(const COutput& out, vecOutputs) {
- if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
- continue;
+ pwallet->AvailableCoins(vecOutputs, !include_unsafe, NULL, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ BOOST_FOREACH(const COutput& out, vecOutputs) {
CTxDestination address;
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
@@ -2769,7 +2797,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
"By default, the new fee will be calculated automatically using estimatefee.\n"
"The user can specify a confirmation target for estimatefee.\n"
- "Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n"
+ "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
"At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
"returned by getnetworkinfo) to enter the node's mempool.\n"
"\nArguments:\n"
@@ -2936,7 +2964,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
- { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} },
+ { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index ea329d6ebe..9eba8ad9fe 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1977,12 +1977,15 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons
return balance;
}
-void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
+void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const
{
vCoins.clear();
{
LOCK2(cs_main, cs_wallet);
+
+ CAmount nTotal = 0;
+
for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const uint256& wtxid = it->first;
@@ -2040,15 +2043,46 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
continue;
}
+ if (nDepth < nMinDepth || nDepth > nMaxDepth)
+ continue;
+
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
+ if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
+ continue;
+
+ if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i)))
+ continue;
+
+ if (IsLockedCoin((*it).first, i))
+ continue;
+
+ if (IsSpent(wtxid, i))
+ continue;
+
isminetype mine = IsMine(pcoin->tx->vout[i]);
- if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
- !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) &&
- (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i))))
- vCoins.push_back(COutput(pcoin, i, nDepth,
- ((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
- (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO),
- (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, safeTx));
+
+ if (mine == ISMINE_NO) {
+ continue;
+ }
+
+ bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO);
+ bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO;
+
+ vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx));
+
+ // Checks the sum amount of all UTXO's.
+ if (nMinimumSumAmount != MAX_MONEY) {
+ nTotal += pcoin->tx->vout[i].nValue;
+
+ if (nTotal >= nMinimumSumAmount) {
+ return;
+ }
+ }
+
+ // Checks the maximum number of UTXO's.
+ if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
+ return;
+ }
}
}
}
@@ -2277,6 +2311,8 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
bool CWallet::SignTransaction(CMutableTransaction &tx)
{
+ AssertLockHeld(cs_wallet); // mapWallet
+
// sign the new tx
CTransaction txNewConst(tx);
int nIn = 0;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 179ea1b7ad..69f51b3f64 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -818,7 +818,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;
+ void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const;
/**
* Shuffle and select coins until nTargetValue is reached while avoiding