Merge bitcoin/bitcoin#22100: refactor: Clean up new wallet spend, receive files added #21207

b11a195ef4 refactor: Detach wallet transaction methods (followup for move-only) (Russell Yanofsky)

Pull request description:

  This makes `CWallet` and `CWalletTx` methods in `spend.cpp` and `receive.cpp` files into standalone functions.

  It's a followup to [#21207 MOVEONLY: CWallet transaction code out of wallet.cpp/.h](https://github.com/bitcoin/bitcoin/pull/21207), which moved code from `wallet.cpp` to new `spend.cpp` and `receive.cpp` files.

  There are no changes in behavior. This is just making methods into functions and removing circular dependencies created by #21207. There are no comment or documentation changes, either. Removed comments from `transaction.h` are just migrated to `spend.h`, `receive.h`, and `wallet.h`.

  ---

  This commit was split off from #21206 so there are a few earlier review comments there

ACKs for top commit:
  achow101:
    ACK b11a195ef4
  Sjors:
    utACK b11a195ef4
  meshcollider:
    light ACK b11a195ef4

Tree-SHA512: 75ce818d3f03b728b14b12e2d21bd20b7be73978601989cb37ff98254393300d1bb7823281449cd3d9e40756d67d42bd9a46bbdafd2e8baa95aaf2cb1c84549f
This commit is contained in:
Samuel Dobson committed 2021-09-03 21:21:58 +12:00
commit 629c4ab2e3
20 files changed
+504 -492

No files matched your search

+6 -5
View File
@@ -6,6 +6,7 @@
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <node/context.h> #include <node/context.h>
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <set> #include <set>
@@ -17,7 +18,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(1); tx.vout.resize(1);
tx.vout[0].nValue = nValue; tx.vout[0].nValue = nValue;
wtxs.push_back(std::make_unique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)))); wtxs.push_back(std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx))));
} }
// Simple benchmark for wallet coin selection. Note that it maybe be necessary // Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -45,7 +46,7 @@ static void CoinSelection(benchmark::Bench& bench)
// Create coins // Create coins
std::vector<COutput> coins; std::vector<COutput> coins;
for (const auto& wtx : wtxs) { for (const auto& wtx : wtxs) {
coins.emplace_back(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */); coins.emplace_back(wallet, *wtx, 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
} }
const CoinEligibilityFilter filter_standard(1, 6, 0); const CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -56,7 +57,7 @@ static void CoinSelection(benchmark::Bench& bench)
bench.run([&] { bench.run([&] {
std::set<CInputCoin> setCoinsRet; std::set<CInputCoin> setCoinsRet;
CAmount nValueRet; CAmount nValueRet;
bool success = wallet.AttemptSelection(1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params); bool success = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
assert(success); assert(success);
assert(nValueRet == 1003 * COIN); assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2); assert(setCoinsRet.size() == 2);
@@ -75,9 +76,9 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx; CMutableTransaction tx;
tx.vout.resize(nInput + 1); tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue; tx.vout[nInput].nValue = nValue;
std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); std::unique_ptr<CWalletTx> wtx = std::make_unique<CWalletTx>(MakeTransactionRef(std::move(tx)));
set.emplace_back(); set.emplace_back();
set.back().Insert(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false); set.back().Insert(COutput(testWallet, *wtx, nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0, false);
wtxn.emplace_back(std::move(wtx)); wtxn.emplace_back(std::move(wtx));
} }
// Copied from src/wallet/test/coinselector_tests.cpp // Copied from src/wallet/test/coinselector_tests.cpp
+3 -2
View File
@@ -9,6 +9,7 @@
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <test/util/wallet.h> #include <test/util/wallet.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <wallet/receive.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <optional> #include <optional>
@@ -35,11 +36,11 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
} }
SyncWithValidationInterfaceQueue(); SyncWithValidationInterfaceQueue();
auto bal = wallet.GetBalance(); // Cache auto bal = GetBalance(wallet); // Cache
bench.run([&] { bench.run([&] {
if (set_dirty) wallet.MarkDirty(); if (set_dirty) wallet.MarkDirty();
bal = wallet.GetBalance(); bal = GetBalance(wallet);
if (add_mine) assert(bal.m_mine_trusted > 0); if (add_mine) assert(bal.m_mine_trusted > 0);
if (add_watchonly) assert(bal.m_watchonly_trusted > 0); if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
}); });
+8 -6
View File
@@ -12,6 +12,8 @@
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/fees.h> #include <wallet/fees.h>
#include <wallet/receive.h>
#include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
//! Check whether transaction has descendant in wallet or mempool, or has been //! Check whether transaction has descendant in wallet or mempool, or has been
@@ -30,7 +32,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
} }
} }
if (wtx.GetDepthInMainChain() != 0) { if (wallet.GetTxDepthInMainChain(wtx) != 0) {
errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction")); errors.push_back(Untranslated("Transaction has been mined, or is conflicted with a mined transaction"));
return feebumper::Result::WALLET_ERROR; return feebumper::Result::WALLET_ERROR;
} }
@@ -48,7 +50,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
// check that original tx consists entirely of our inputs // check that original tx consists entirely of our inputs
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
if (!wallet.IsAllFromMe(*wtx.tx, filter)) { if (!AllInputsMine(wallet, *wtx.tx, filter)) {
errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet")); errors.push_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
return feebumper::Result::WALLET_ERROR; return feebumper::Result::WALLET_ERROR;
} }
@@ -81,7 +83,7 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
// Given old total fee and transaction size, calculate the old feeRate // Given old total fee and transaction size, calculate the old feeRate
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
CAmount old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut(); CAmount old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); const int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
CFeeRate nOldFeeRate(old_fee, txSize); CFeeRate nOldFeeRate(old_fee, txSize);
// Min total fee is old fee + relay fee // Min total fee is old fee + relay fee
@@ -174,7 +176,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
// Fill in recipients(and preserve a single change key if there is one) // Fill in recipients(and preserve a single change key if there is one)
std::vector<CRecipient> recipients; std::vector<CRecipient> recipients;
for (const auto& output : wtx.tx->vout) { for (const auto& output : wtx.tx->vout) {
if (!wallet.IsChange(output)) { if (!OutputIsChange(wallet, output)) {
CRecipient recipient = {output.scriptPubKey, output.nValue, false}; CRecipient recipient = {output.scriptPubKey, output.nValue, false};
recipients.push_back(recipient); recipients.push_back(recipient);
} else { } else {
@@ -185,7 +187,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
} }
isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
old_fee = wtx.GetDebit(filter) - wtx.tx->GetValueOut(); old_fee = CachedTxGetDebit(wallet, wtx, filter) - wtx.tx->GetValueOut();
if (coin_control.m_feerate) { if (coin_control.m_feerate) {
// The user provided a feeRate argument. // The user provided a feeRate argument.
@@ -220,7 +222,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
int change_pos_in_out = -1; // No requested location for change int change_pos_in_out = -1; // No requested location for change
bilingual_str fail_reason; bilingual_str fail_reason;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
if (!wallet.CreateTransaction(recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) { if (!CreateTransaction(wallet, recipients, tx_new, fee_ret, change_pos_in_out, fail_reason, new_coin_control, fee_calc_out, false)) {
errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason); errors.push_back(Untranslated("Unable to create transaction.") + Untranslated(" ") + fail_reason);
return Result::WALLET_ERROR; return Result::WALLET_ERROR;
} }
+18 -16
View File
@@ -23,7 +23,9 @@
#include <wallet/fees.h> #include <wallet/fees.h>
#include <wallet/ismine.h> #include <wallet/ismine.h>
#include <wallet/load.h> #include <wallet/load.h>
#include <wallet/receive.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
#include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <memory> #include <memory>
@@ -55,7 +57,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
result.tx = wtx.tx; result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size()); result.txin_is_mine.reserve(wtx.tx->vin.size());
for (const auto& txin : wtx.tx->vin) { for (const auto& txin : wtx.tx->vin) {
result.txin_is_mine.emplace_back(wallet.IsMine(txin)); result.txin_is_mine.emplace_back(InputIsMine(wallet, txin));
} }
result.txout_is_mine.reserve(wtx.tx->vout.size()); result.txout_is_mine.reserve(wtx.tx->vout.size());
result.txout_address.reserve(wtx.tx->vout.size()); result.txout_address.reserve(wtx.tx->vout.size());
@@ -67,9 +69,9 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
wallet.IsMine(result.txout_address.back()) : wallet.IsMine(result.txout_address.back()) :
ISMINE_NO); ISMINE_NO);
} }
result.credit = wtx.GetCredit(ISMINE_ALL); result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL); result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL);
result.change = wtx.GetChange(); result.change = CachedTxGetChange(wallet, wtx);
result.time = wtx.GetTxTime(); result.time = wtx.GetTxTime();
result.value_map = wtx.mapValue; result.value_map = wtx.mapValue;
result.is_coinbase = wtx.IsCoinBase(); result.is_coinbase = wtx.IsCoinBase();
@@ -81,15 +83,15 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
{ {
WalletTxStatus result; WalletTxStatus result;
result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max(); result.block_height = wtx.m_confirm.block_height > 0 ? wtx.m_confirm.block_height : std::numeric_limits<int>::max();
result.blocks_to_maturity = wtx.GetBlocksToMaturity(); result.blocks_to_maturity = wallet.GetTxBlocksToMaturity(wtx);
result.depth_in_main_chain = wtx.GetDepthInMainChain(); result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx);
result.time_received = wtx.nTimeReceived; result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime; result.lock_time = wtx.tx->nLockTime;
result.is_final = wallet.chain().checkFinalTx(*wtx.tx); result.is_final = wallet.chain().checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(); result.is_trusted = CachedTxIsTrusted(wallet, wtx);
result.is_abandoned = wtx.isAbandoned(); result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase(); result.is_coinbase = wtx.IsCoinBase();
result.is_in_main_chain = wtx.IsInMainChain(); result.is_in_main_chain = wallet.IsTxInMainChain(wtx);
return result; return result;
} }
@@ -242,7 +244,7 @@ public:
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
CTransactionRef tx; CTransactionRef tx;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos, if (!CreateTransaction(*m_wallet, recipients, tx, fee, change_pos,
fail_reason, coin_control, fee_calc_out, sign)) { fail_reason, coin_control, fee_calc_out, sign)) {
return {}; return {};
} }
@@ -358,7 +360,7 @@ public:
} }
WalletBalances getBalances() override WalletBalances getBalances() override
{ {
const auto bal = m_wallet->GetBalance(); const auto bal = GetBalance(*m_wallet);
WalletBalances result; WalletBalances result;
result.balance = bal.m_mine_trusted; result.balance = bal.m_mine_trusted;
result.unconfirmed_balance = bal.m_mine_untrusted_pending; result.unconfirmed_balance = bal.m_mine_untrusted_pending;
@@ -381,15 +383,15 @@ public:
balances = getBalances(); balances = getBalances();
return true; return true;
} }
CAmount getBalance() override { return m_wallet->GetBalance().m_mine_trusted; } CAmount getBalance() override { return GetBalance(*m_wallet).m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override CAmount getAvailableBalance(const CCoinControl& coin_control) override
{ {
return m_wallet->GetAvailableBalance(&coin_control); return GetAvailableBalance(*m_wallet, &coin_control);
} }
isminetype txinIsMine(const CTxIn& txin) override isminetype txinIsMine(const CTxIn& txin) override
{ {
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(txin); return InputIsMine(*m_wallet, txin);
} }
isminetype txoutIsMine(const CTxOut& txout) override isminetype txoutIsMine(const CTxOut& txout) override
{ {
@@ -404,13 +406,13 @@ public:
CAmount getCredit(const CTxOut& txout, isminefilter filter) override CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{ {
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
return m_wallet->GetCredit(txout, filter); return OutputGetCredit(*m_wallet, txout, filter);
} }
CoinsList listCoins() override CoinsList listCoins() override
{ {
LOCK(m_wallet->cs_wallet); LOCK(m_wallet->cs_wallet);
CoinsList result; CoinsList result;
for (const auto& entry : m_wallet->ListCoins()) { for (const auto& entry : ListCoins(*m_wallet)) {
auto& group = result[entry.first]; auto& group = result[entry.first];
for (const auto& coin : entry.second) { for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i), group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
@@ -428,7 +430,7 @@ public:
result.emplace_back(); result.emplace_back();
auto it = m_wallet->mapWallet.find(output.hash); auto it = m_wallet->mapWallet.find(output.hash);
if (it != m_wallet->mapWallet.end()) { if (it != m_wallet->mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(); int depth = m_wallet->GetTxDepthInMainChain(it->second);
if (depth >= 0) { if (depth >= 0) {
result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth); result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth);
} }
+1
View File
@@ -13,6 +13,7 @@
#include <util/system.h> #include <util/system.h>
#include <util/translation.h> #include <util/translation.h>
#include <wallet/context.h> #include <wallet/context.h>
#include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletdb.h> #include <wallet/walletdb.h>
+116 -113
View File
@@ -7,27 +7,27 @@
#include <wallet/transaction.h> #include <wallet/transaction.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
isminetype CWallet::IsMine(const CTxIn &txin) const isminetype InputIsMine(const CWallet& wallet, const CTxIn &txin)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); std::map<uint256, CWalletTx>::const_iterator mi = wallet.mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != wallet.mapWallet.end())
{ {
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.tx->vout.size()) if (txin.prevout.n < prev.tx->vout.size())
return IsMine(prev.tx->vout[txin.prevout.n]); return wallet.IsMine(prev.tx->vout[txin.prevout.n]);
} }
return ISMINE_NO; return ISMINE_NO;
} }
bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
{ {
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
for (const CTxIn& txin : tx.vin) for (const CTxIn& txin : tx.vin)
{ {
auto mi = mapWallet.find(txin.prevout.hash); auto mi = wallet.mapWallet.find(txin.prevout.hash);
if (mi == mapWallet.end()) if (mi == wallet.mapWallet.end())
return false; // any unknown inputs can't be from us return false; // any unknown inputs can't be from us
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
@@ -35,33 +35,33 @@ bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) co
if (txin.prevout.n >= prev.tx->vout.size()) if (txin.prevout.n >= prev.tx->vout.size())
return false; // invalid input! return false; // invalid input!
if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) if (!(wallet.IsMine(prev.tx->vout[txin.prevout.n]) & filter))
return false; return false;
} }
return true; return true;
} }
CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter)
{ {
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0); return ((wallet.IsMine(txout) & filter) ? txout.nValue : 0);
} }
CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
{ {
CAmount nCredit = 0; CAmount nCredit = 0;
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
{ {
nCredit += GetCredit(txout, filter); nCredit += OutputGetCredit(wallet, txout, filter);
if (!MoneyRange(nCredit)) if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
} }
return nCredit; return nCredit;
} }
bool CWallet::IsChange(const CScript& script) const bool ScriptIsChange(const CWallet& wallet, const CScript& script)
{ {
// TODO: fix handling of 'change' outputs. The assumption is that any // TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book // payment to a script that is ours, but is not in the address book
@@ -70,179 +70,177 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are // a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember // 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change). // which output, if any, was change).
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
if (IsMine(script)) if (wallet.IsMine(script))
{ {
CTxDestination address; CTxDestination address;
if (!ExtractDestination(script, address)) if (!ExtractDestination(script, address))
return true; return true;
if (!FindAddressBookEntry(address)) { if (!wallet.FindAddressBookEntry(address)) {
return true; return true;
} }
} }
return false; return false;
} }
bool CWallet::IsChange(const CTxOut& txout) const bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
{ {
return IsChange(txout.scriptPubKey); return ScriptIsChange(wallet, txout.scriptPubKey);
} }
CAmount CWallet::GetChange(const CTxOut& txout) const CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0); return (OutputIsChange(wallet, txout) ? txout.nValue : 0);
} }
CAmount CWallet::GetChange(const CTransaction& tx) const CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
{ {
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
CAmount nChange = 0; CAmount nChange = 0;
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
{ {
nChange += GetChange(txout); nChange += OutputGetChange(wallet, txout);
if (!MoneyRange(nChange)) if (!MoneyRange(nChange))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
} }
return nChange; return nChange;
} }
CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter, bool recalculate = false)
{ {
auto& amount = m_amounts[type]; auto& amount = wtx.m_amounts[type];
if (recalculate || !amount.m_cached[filter]) { if (recalculate || !amount.m_cached[filter]) {
amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter)); amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter));
m_is_cache_empty = false; wtx.m_is_cache_empty = false;
} }
return amount.m_value[filter]; return amount.m_value[filter];
} }
CAmount CWalletTx::GetCredit(const isminefilter& filter) const CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{ {
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase()) if (wallet.IsTxImmatureCoinBase(wtx))
return 0; return 0;
CAmount credit = 0; CAmount credit = 0;
if (filter & ISMINE_SPENDABLE) { if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change // GetBalance can assume transactions in mapWallet won't change
credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE); credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_SPENDABLE);
} }
if (filter & ISMINE_WATCH_ONLY) { if (filter & ISMINE_WATCH_ONLY) {
credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY); credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_WATCH_ONLY);
} }
return credit; return credit;
} }
CAmount CWalletTx::GetDebit(const isminefilter& filter) const CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{ {
if (tx->vin.empty()) if (wtx.tx->vin.empty())
return 0; return 0;
CAmount debit = 0; CAmount debit = 0;
if (filter & ISMINE_SPENDABLE) { if (filter & ISMINE_SPENDABLE) {
debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE); debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_SPENDABLE);
} }
if (filter & ISMINE_WATCH_ONLY) { if (filter & ISMINE_WATCH_ONLY) {
debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY); debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_WATCH_ONLY);
} }
return debit; return debit;
} }
CAmount CWalletTx::GetChange() const CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
{ {
if (fChangeCached) if (wtx.fChangeCached)
return nChangeCached; return wtx.nChangeCached;
nChangeCached = pwallet->GetChange(*tx); wtx.nChangeCached = TxGetChange(wallet, *wtx.tx);
fChangeCached = true; wtx.fChangeCached = true;
return nChangeCached; return wtx.nChangeCached;
} }
CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache)
{ {
if (IsImmatureCoinBase() && IsInMainChain()) { if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache); return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
} }
return 0; return 0;
} }
CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache)
{ {
if (IsImmatureCoinBase() && IsInMainChain()) { if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache); return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
} }
return 0; return 0;
} }
CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache, const isminefilter& filter)
{ {
if (pwallet == nullptr)
return 0;
// Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future). // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
// Must wait until coinbase is safely deep enough in the chain before valuing it // Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase()) if (wallet.IsTxImmatureCoinBase(wtx))
return 0; return 0;
if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { if (fUseCache && allow_cache && wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_cached[filter]) {
return m_amounts[AVAILABLE_CREDIT].m_value[filter]; return wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_value[filter];
} }
bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
CAmount nCredit = 0; CAmount nCredit = 0;
uint256 hashTx = GetHash(); uint256 hashTx = wtx.GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++) for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{ {
if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) { if (!wallet.IsSpent(hashTx, i) && (allow_used_addresses || !wallet.IsSpentKey(hashTx, i))) {
const CTxOut &txout = tx->vout[i]; const CTxOut &txout = wtx.tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter); nCredit += OutputGetCredit(wallet, txout, filter);
if (!MoneyRange(nCredit)) if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + " : value out of range"); throw std::runtime_error(std::string(__func__) + " : value out of range");
} }
} }
if (allow_cache) { if (allow_cache) {
m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit); wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].Set(filter, nCredit);
m_is_cache_empty = false; wtx.m_is_cache_empty = false;
} }
return nCredit; return nCredit;
} }
void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter)
{ {
nFee = 0; nFee = 0;
listReceived.clear(); listReceived.clear();
listSent.clear(); listSent.clear();
// Compute fee: // Compute fee:
CAmount nDebit = GetDebit(filter); CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction if (nDebit > 0) // debit>0 means we signed/sent this transaction
{ {
CAmount nValueOut = tx->GetValueOut(); CAmount nValueOut = wtx.tx->GetValueOut();
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
LOCK(pwallet->cs_wallet); LOCK(wallet.cs_wallet);
// Sent/received. // Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i) for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
{ {
const CTxOut& txout = tx->vout[i]; const CTxOut& txout = wtx.tx->vout[i];
isminetype fIsMine = pwallet->IsMine(txout); isminetype fIsMine = wallet.IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true: // Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent) // 1) they debit from us (sent)
// 2) the output is to us (received) // 2) the output is to us (received)
if (nDebit > 0) if (nDebit > 0)
{ {
// Don't report 'change' txouts // Don't report 'change' txouts
if (pwallet->IsChange(txout)) if (OutputIsChange(wallet, txout))
continue; continue;
} }
else if (!(fIsMine & filter)) else if (!(fIsMine & filter))
@@ -253,8 +251,8 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
{ {
pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString()); wtx.GetHash().ToString());
address = CNoDestination(); address = CNoDestination();
} }
@@ -271,16 +269,21 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
} }
bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
{ {
AssertLockHeld(cs_wallet); return (CachedTxGetDebit(wallet, wtx, filter) > 0);
}
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
{
AssertLockHeld(wallet.cs_wallet);
// Quick answer in most cases // Quick answer in most cases
if (!chain().checkFinalTx(*wtx.tx)) return false; if (!wallet.chain().checkFinalTx(*wtx.tx)) return false;
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth >= 1) return true; if (nDepth >= 1) return true;
if (nDepth < 0) return false; if (nDepth < 0) return false;
// using wtx's cached debit // using wtx's cached debit
if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false; if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx, ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool. // Don't trust unconfirmed transactions from us unless they are in the mempool.
if (!wtx.InMempool()) return false; if (!wtx.InMempool()) return false;
@@ -289,41 +292,41 @@ bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents
for (const CTxIn& txin : wtx.tx->vin) for (const CTxIn& txin : wtx.tx->vin)
{ {
// Transactions not sent by us: not trusted // Transactions not sent by us: not trusted
const CWalletTx* parent = GetWalletTx(txin.prevout.hash); const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);
if (parent == nullptr) return false; if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
// Check that this specific input being spent is trusted // Check that this specific input being spent is trusted
if (IsMine(parentOut) != ISMINE_SPENDABLE) return false; if (wallet.IsMine(parentOut) != ISMINE_SPENDABLE) return false;
// If we've already trusted this parent, continue // If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue; if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted // Recurse to check that the parent is also trusted
if (!IsTrusted(*parent, trusted_parents)) return false; if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash()); trusted_parents.insert(parent->GetHash());
} }
return true; return true;
} }
bool CWalletTx::IsTrusted() const bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
{ {
std::set<uint256> trusted_parents; std::set<uint256> trusted_parents;
LOCK(pwallet->cs_wallet); LOCK(wallet.cs_wallet);
return pwallet->IsTrusted(*this, trusted_parents); return CachedTxIsTrusted(wallet, wtx, trusted_parents);
} }
CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
{ {
Balance ret; Balance ret;
isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
{ {
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
std::set<uint256> trusted_parents; std::set<uint256> trusted_parents;
for (const auto& entry : mapWallet) for (const auto& entry : wallet.mapWallet)
{ {
const CWalletTx& wtx = entry.second; const CWalletTx& wtx = entry.second;
const bool is_trusted{IsTrusted(wtx, trusted_parents)}; const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
const int tx_depth{wtx.GetDepthInMainChain()}; const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; const CAmount tx_credit_mine{CachedTxGetAvailableCredit(wallet, wtx, /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; const CAmount tx_credit_watchonly{CachedTxGetAvailableCredit(wallet, wtx, /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
if (is_trusted && tx_depth >= min_depth) { if (is_trusted && tx_depth >= min_depth) {
ret.m_mine_trusted += tx_credit_mine; ret.m_mine_trusted += tx_credit_mine;
ret.m_watchonly_trusted += tx_credit_watchonly; ret.m_watchonly_trusted += tx_credit_watchonly;
@@ -332,43 +335,43 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
ret.m_mine_untrusted_pending += tx_credit_mine; ret.m_mine_untrusted_pending += tx_credit_mine;
ret.m_watchonly_untrusted_pending += tx_credit_watchonly; ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
} }
ret.m_mine_immature += wtx.GetImmatureCredit(); ret.m_mine_immature += CachedTxGetImmatureCredit(wallet, wtx);
ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); ret.m_watchonly_immature += CachedTxGetImmatureWatchOnlyCredit(wallet, wtx);
} }
} }
return ret; return ret;
} }
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
{ {
std::map<CTxDestination, CAmount> balances; std::map<CTxDestination, CAmount> balances;
{ {
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
std::set<uint256> trusted_parents; std::set<uint256> trusted_parents;
for (const auto& walletEntry : mapWallet) for (const auto& walletEntry : wallet.mapWallet)
{ {
const CWalletTx& wtx = walletEntry.second; const CWalletTx& wtx = walletEntry.second;
if (!IsTrusted(wtx, trusted_parents)) if (!CachedTxIsTrusted(wallet, wtx, trusted_parents))
continue; continue;
if (wtx.IsImmatureCoinBase()) if (wallet.IsTxImmatureCoinBase(wtx))
continue; continue;
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1)) if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1))
continue; continue;
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{ {
CTxDestination addr; CTxDestination addr;
if (!IsMine(wtx.tx->vout[i])) if (!wallet.IsMine(wtx.tx->vout[i]))
continue; continue;
if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
continue; continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue; CAmount n = wallet.IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
balances[addr] += n; balances[addr] += n;
} }
} }
@@ -377,13 +380,13 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
return balances; return balances;
} }
std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
std::set< std::set<CTxDestination> > groupings; std::set< std::set<CTxDestination> > groupings;
std::set<CTxDestination> grouping; std::set<CTxDestination> grouping;
for (const auto& walletEntry : mapWallet) for (const auto& walletEntry : wallet.mapWallet)
{ {
const CWalletTx& wtx = walletEntry.second; const CWalletTx& wtx = walletEntry.second;
@@ -394,9 +397,9 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
for (const CTxIn& txin : wtx.tx->vin) for (const CTxIn& txin : wtx.tx->vin)
{ {
CTxDestination address; CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */ if(!InputIsMine(wallet, txin)) /* If this input isn't mine, ignore it */
continue; continue;
if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address)) if(!ExtractDestination(wallet.mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
continue; continue;
grouping.insert(address); grouping.insert(address);
any_mine = true; any_mine = true;
@@ -406,7 +409,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
if (any_mine) if (any_mine)
{ {
for (const CTxOut& txout : wtx.tx->vout) for (const CTxOut& txout : wtx.tx->vout)
if (IsChange(txout)) if (OutputIsChange(wallet, txout))
{ {
CTxDestination txoutAddr; CTxDestination txoutAddr;
if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
@@ -423,7 +426,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const
// group lone addrs by themselves // group lone addrs by themselves
for (const auto& txout : wtx.tx->vout) for (const auto& txout : wtx.tx->vout)
if (IsMine(txout)) if (wallet.IsMine(txout))
{ {
CTxDestination address; CTxDestination address;
if(!ExtractDestination(txout.scriptPubKey, address)) if(!ExtractDestination(txout.scriptPubKey, address))
+44
View File
@@ -10,11 +10,55 @@
#include <wallet/transaction.h> #include <wallet/transaction.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
/** Returns whether all of the inputs match the filter */
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter);
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter);
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter);
bool ScriptIsChange(const CWallet& wallet, const CScript& script) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
bool OutputIsChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx);
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
//! filter decides which addresses will count towards the debit
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx);
CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true);
CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache = true);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet.
CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) NO_THREAD_SAFETY_ANALYSIS;
struct COutputEntry struct COutputEntry
{ {
CTxDestination destination; CTxDestination destination;
CAmount amount; CAmount amount;
int vout; int vout;
}; };
void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent,
CAmount& nFee, const isminefilter& filter);
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx);
struct Balance {
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
CAmount m_watchonly_trusted{0};
CAmount m_watchonly_untrusted_pending{0};
CAmount m_watchonly_immature{0};
};
Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = true);
std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet);
std::set<std::set<CTxDestination>> GetAddressGroupings(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
#endif // BITCOIN_WALLET_RECEIVE_H #endif // BITCOIN_WALLET_RECEIVE_H
+33 -30
View File
@@ -31,7 +31,9 @@
#include <wallet/context.h> #include <wallet/context.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/load.h> #include <wallet/load.h>
#include <wallet/receive.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
#include <wallet/spend.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <wallet/walletdb.h> #include <wallet/walletdb.h>
#include <wallet/walletutil.h> #include <wallet/walletutil.h>
@@ -147,9 +149,10 @@ LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_cr
return *spk_man; return *spk_man;
} }
static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniValue& entry) static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry)
{ {
int confirms = wtx.GetDepthInMainChain(); interfaces::Chain& chain = wallet.chain();
int confirms = wallet.GetTxDepthInMainChain(wtx);
entry.pushKV("confirmations", confirms); entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
entry.pushKV("generated", true); entry.pushKV("generated", true);
@@ -162,12 +165,12 @@ static void WalletTxToJSON(interfaces::Chain& chain, const CWalletTx& wtx, UniVa
CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time))); CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time)));
entry.pushKV("blocktime", block_time); entry.pushKV("blocktime", block_time);
} else { } else {
entry.pushKV("trusted", wtx.IsTrusted()); entry.pushKV("trusted", CachedTxIsTrusted(wallet, wtx));
} }
uint256 hash = wtx.GetHash(); uint256 hash = wtx.GetHash();
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 : wallet.GetTxConflicts(wtx))
conflicts.push_back(conflict.GetHex()); conflicts.push_back(conflict.GetHex());
entry.pushKV("walletconflicts", conflicts); entry.pushKV("walletconflicts", conflicts);
entry.pushKV("time", wtx.GetTxTime()); entry.pushKV("time", wtx.GetTxTime());
@@ -423,7 +426,7 @@ UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vecto
bilingual_str error; bilingual_str error;
CTransactionRef tx; CTransactionRef tx;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
const bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true); const bool fCreated = CreateTransaction(wallet, recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, true);
if (!fCreated) { if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original); throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
} }
@@ -576,8 +579,8 @@ static RPCHelpMan listaddressgroupings()
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR); UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(); std::map<CTxDestination, CAmount> balances = GetAddressBalances(*pwallet);
for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) { for (const std::set<CTxDestination>& grouping : GetAddressGroupings(*pwallet)) {
UniValue jsonGrouping(UniValue::VARR); UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping) for (const CTxDestination& address : grouping)
{ {
@@ -686,7 +689,7 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
CAmount amount = 0; CAmount amount = 0;
for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) { for (const std::pair<const uint256, CWalletTx>& wtx_pair : wallet.mapWallet) {
const CWalletTx& wtx = wtx_pair.second; const CWalletTx& wtx = wtx_pair.second;
if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wtx.GetDepthInMainChain() < min_depth) { if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx) || wallet.GetTxDepthInMainChain(wtx) < min_depth) {
continue; continue;
} }
@@ -826,7 +829,7 @@ static RPCHelpMan getbalance()
bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]); bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse); const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0)); return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
}, },
@@ -851,7 +854,7 @@ static RPCHelpMan getunconfirmedbalance()
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending); return ValueFromAmount(GetBalance(*pwallet).m_mine_untrusted_pending);
}, },
}; };
} }
@@ -1085,7 +1088,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool
continue; continue;
} }
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth < nMinDepth) if (nDepth < nMinDepth)
continue; continue;
@@ -1310,9 +1313,9 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
std::list<COutputEntry> listReceived; std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent; std::list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine); CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine);
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); bool involvesWatchonly = CachedTxIsFromMe(wallet, wtx, ISMINE_WATCH_ONLY);
// Sent // Sent
if (!filter_label) if (!filter_label)
@@ -1333,14 +1336,14 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
entry.pushKV("vout", s.vout); entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee)); entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong) if (fLong)
WalletTxToJSON(wallet.chain(), wtx, entry); WalletTxToJSON(wallet, 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 && wallet.GetTxDepthInMainChain(wtx) >= nMinDepth) {
for (const COutputEntry& r : listReceived) for (const COutputEntry& r : listReceived)
{ {
std::string label; std::string label;
@@ -1358,9 +1361,9 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
MaybePushAddress(entry, r.destination); MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase()) if (wtx.IsCoinBase())
{ {
if (wtx.GetDepthInMainChain() < 1) if (wallet.GetTxDepthInMainChain(wtx) < 1)
entry.pushKV("category", "orphan"); entry.pushKV("category", "orphan");
else if (wtx.IsImmatureCoinBase()) else if (wallet.IsTxImmatureCoinBase(wtx))
entry.pushKV("category", "immature"); entry.pushKV("category", "immature");
else else
entry.pushKV("category", "generate"); entry.pushKV("category", "generate");
@@ -1375,7 +1378,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
} }
entry.pushKV("vout", r.vout); entry.pushKV("vout", r.vout);
if (fLong) if (fLong)
WalletTxToJSON(wallet.chain(), wtx, entry); WalletTxToJSON(wallet, wtx, entry);
ret.push_back(entry); ret.push_back(entry);
} }
} }
@@ -1615,7 +1618,7 @@ static RPCHelpMan listsinceblock()
for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) { for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
const CWalletTx& tx = pairWtx.second; const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) { if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */); ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
} }
} }
@@ -1736,16 +1739,16 @@ static RPCHelpMan gettransaction()
} }
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
CAmount nCredit = wtx.GetCredit(filter); CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, filter);
CAmount nDebit = wtx.GetDebit(filter); CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, filter);
CAmount nNet = nCredit - nDebit; CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); CAmount nFee = (CachedTxIsFromMe(*pwallet, wtx, filter) ? wtx.tx->GetValueOut() - nDebit : 0);
entry.pushKV("amount", ValueFromAmount(nNet - nFee)); entry.pushKV("amount", ValueFromAmount(nNet - nFee));
if (wtx.IsFromMe(filter)) if (CachedTxIsFromMe(*pwallet, wtx, filter))
entry.pushKV("fee", ValueFromAmount(nFee)); entry.pushKV("fee", ValueFromAmount(nFee));
WalletTxToJSON(pwallet->chain(), wtx, entry); WalletTxToJSON(*pwallet, wtx, entry);
UniValue details(UniValue::VARR); UniValue details(UniValue::VARR);
ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */); ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
@@ -2384,7 +2387,7 @@ static RPCHelpMan getbalances()
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
const auto bal = wallet.GetBalance(); const auto bal = GetBalance(wallet);
UniValue balances{UniValue::VOBJ}; UniValue balances{UniValue::VOBJ};
{ {
UniValue balances_mine{UniValue::VOBJ}; UniValue balances_mine{UniValue::VOBJ};
@@ -2394,7 +2397,7 @@ static RPCHelpMan getbalances()
if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
// If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get // If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
// the total balance, and then subtract bal to get the reused address balance. // the total balance, and then subtract bal to get the reused address balance.
const auto full_bal = wallet.GetBalance(0, false); const auto full_bal = GetBalance(wallet, 0, false);
balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending)); balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
} }
balances.pushKV("mine", balances_mine); balances.pushKV("mine", balances_mine);
@@ -2462,7 +2465,7 @@ static RPCHelpMan getwalletinfo()
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys(); size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
const auto bal = pwallet->GetBalance(); const auto bal = GetBalance(*pwallet);
int64_t kp_oldest = pwallet->GetOldestKeyPoolTime(); int64_t kp_oldest = pwallet->GetOldestKeyPoolTime();
obj.pushKV("walletname", pwallet->GetName()); obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion()); obj.pushKV("walletversion", pwallet->GetVersion());
@@ -3058,7 +3061,7 @@ static RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth; cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe; cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
pwallet->AvailableCoins(vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount); AvailableCoins(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
} }
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@@ -3274,7 +3277,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
bilingual_str error; bilingual_str error;
if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) { if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original); throw JSONRPCError(RPC_WALLET_ERROR, error.original);
} }
} }
@@ -3959,7 +3962,7 @@ RPCHelpMan getaddressinfo()
UniValue detail = DescribeWalletAddress(*pwallet, dest); UniValue detail = DescribeWalletAddress(*pwallet, dest);
ret.pushKVs(detail); ret.pushKVs(detail);
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); ret.pushKV("ischange", ScriptIsChange(*pwallet, scriptPubKey));
ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey); ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
if (spk_man) { if (spk_man) {
+99 -92
View File
@@ -21,6 +21,11 @@ using interfaces::FoundBlock;
static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100}; static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100};
int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig)
{
return CalculateMaximumSignedInputSize(wtx.tx->vout[out], &wallet, use_max_sig);
}
std::string COutput::ToString() const std::string COutput::ToString() const
{ {
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
@@ -64,33 +69,33 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig); return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig);
} }
void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
vCoins.clear(); vCoins.clear();
CAmount nTotal = 0; CAmount nTotal = 0;
// Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
// a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); bool allow_used_addresses = !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse);
const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH}; const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH}; const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true}; const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true};
std::set<uint256> trusted_parents; std::set<uint256> trusted_parents;
for (const auto& entry : mapWallet) for (const auto& entry : wallet.mapWallet)
{ {
const uint256& wtxid = entry.first; const uint256& wtxid = entry.first;
const CWalletTx& wtx = entry.second; const CWalletTx& wtx = entry.second;
if (!chain().checkFinalTx(*wtx.tx)) { if (!wallet.chain().checkFinalTx(*wtx.tx)) {
continue; continue;
} }
if (wtx.IsImmatureCoinBase()) if (wallet.IsTxImmatureCoinBase(wtx))
continue; continue;
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth < 0) if (nDepth < 0)
continue; continue;
@@ -99,7 +104,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
if (nDepth == 0 && !wtx.InMempool()) if (nDepth == 0 && !wtx.InMempool())
continue; continue;
bool safeTx = IsTrusted(wtx, trusted_parents); bool safeTx = CachedTxIsTrusted(wallet, wtx, trusted_parents);
// We should not consider coins from transactions that are replacing // We should not consider coins from transactions that are replacing
// other transactions. // other transactions.
@@ -152,28 +157,28 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
continue; continue;
if (IsLockedCoin(entry.first, i)) if (wallet.IsLockedCoin(entry.first, i))
continue; continue;
if (IsSpent(wtxid, i)) if (wallet.IsSpent(wtxid, i))
continue; continue;
isminetype mine = IsMine(wtx.tx->vout[i]); isminetype mine = wallet.IsMine(wtx.tx->vout[i]);
if (mine == ISMINE_NO) { if (mine == ISMINE_NO) {
continue; continue;
} }
if (!allow_used_addresses && IsSpentKey(wtxid, i)) { if (!allow_used_addresses && wallet.IsSpentKey(wtxid, i)) {
continue; continue;
} }
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey); std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); vCoins.push_back(COutput(wallet, wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
// Checks the sum amount of all UTXO's. // Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) { if (nMinimumSumAmount != MAX_MONEY) {
@@ -192,13 +197,13 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* c
} }
} }
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
{ {
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
CAmount balance = 0; CAmount balance = 0;
std::vector<COutput> vCoins; std::vector<COutput> vCoins;
AvailableCoins(vCoins, coinControl); AvailableCoins(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) { for (const COutput& out : vCoins) {
if (out.fSpendable) { if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue; balance += out.tx->tx->vout[out.i].nValue;
@@ -207,16 +212,16 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance; return balance;
} }
const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransaction& tx, int output)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
const CTransaction* ptx = &tx; const CTransaction* ptx = &tx;
int n = output; int n = output;
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) { while (OutputIsChange(wallet, ptx->vout[n]) && ptx->vin.size() > 0) {
const COutPoint& prevout = ptx->vin[0].prevout; const COutPoint& prevout = ptx->vin[0].prevout;
auto it = mapWallet.find(prevout.hash); auto it = wallet.mapWallet.find(prevout.hash);
if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n || if (it == wallet.mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
!IsMine(it->second.tx->vout[prevout.n])) { !wallet.IsMine(it->second.tx->vout[prevout.n])) {
break; break;
} }
ptx = it->second.tx.get(); ptx = it->second.tx.get();
@@ -225,39 +230,39 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out
return ptx->vout[n]; return ptx->vout[n];
} }
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result; std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins; std::vector<COutput> availableCoins;
AvailableCoins(availableCoins); AvailableCoins(wallet, availableCoins);
for (const COutput& coin : availableCoins) { for (const COutput& coin : availableCoins) {
CTxDestination address; CTxDestination address;
if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) && if ((coin.fSpendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { ExtractDestination(FindNonChangeParentOutput(wallet, *coin.tx->tx, coin.i).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin)); result[address].emplace_back(std::move(coin));
} }
} }
std::vector<COutPoint> lockedCoins; std::vector<COutPoint> lockedCoins;
ListLockedCoins(lockedCoins); wallet.ListLockedCoins(lockedCoins);
// Include watch-only for LegacyScriptPubKeyMan wallets without private keys // Include watch-only for LegacyScriptPubKeyMan wallets without private keys
const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); const bool include_watch_only = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
for (const COutPoint& output : lockedCoins) { for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash); auto it = wallet.mapWallet.find(output.hash);
if (it != mapWallet.end()) { if (it != wallet.mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(); int depth = wallet.GetTxDepthInMainChain(it->second);
if (depth >= 0 && output.n < it->second.tx->vout.size() && if (depth >= 0 && output.n < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.n]) == is_mine_filter wallet.IsMine(it->second.tx->vout[output.n]) == is_mine_filter
) { ) {
CTxDestination address; CTxDestination address;
if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) { if (ExtractDestination(FindNonChangeParentOutput(wallet, *it->second.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back( result[address].emplace_back(
&it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */); wallet, it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
} }
} }
} }
@@ -266,7 +271,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
return result; return result;
} }
std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only)
{ {
std::vector<OutputGroup> groups_out; std::vector<OutputGroup> groups_out;
@@ -277,12 +282,12 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
if (!output.fSpendable) continue; if (!output.fSpendable) continue;
size_t ancestors, descendants; size_t ancestors, descendants;
chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
CInputCoin input_coin = output.GetInputCoin(); CInputCoin input_coin = output.GetInputCoin();
// Make an OutputGroup containing just this output // Make an OutputGroup containing just this output
OutputGroup group{coin_sel_params}; OutputGroup group{coin_sel_params};
group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); group.Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
// Check the OutputGroup's eligibility. Only add the eligible ones. // Check the OutputGroup's eligibility. Only add the eligible ones.
if (positive_only && group.GetSelectionAmount() <= 0) continue; if (positive_only && group.GetSelectionAmount() <= 0) continue;
@@ -303,7 +308,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
if (!output.fSpendable) continue; if (!output.fSpendable) continue;
size_t ancestors, descendants; size_t ancestors, descendants;
chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
CInputCoin input_coin = output.GetInputCoin(); CInputCoin input_coin = output.GetInputCoin();
CScript spk = input_coin.txout.scriptPubKey; CScript spk = input_coin.txout.scriptPubKey;
@@ -327,7 +332,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
} }
// Add the input_coin to group // Add the input_coin to group
group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); group->Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
} }
// Now we go through the entire map and pull out the OutputGroups // Now we go through the entire map and pull out the OutputGroups
@@ -352,8 +357,8 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
return groups_out; return groups_out;
} }
bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params)
{ {
setCoinsRet.clear(); setCoinsRet.clear();
nValueRet = 0; nValueRet = 0;
@@ -363,7 +368,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
std::vector<std::tuple<CAmount, std::set<CInputCoin>, CAmount>> results; std::vector<std::tuple<CAmount, std::set<CInputCoin>, CAmount>> results;
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output. // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */); std::vector<OutputGroup> positive_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, true /* positive_only */);
std::set<CInputCoin> bnb_coins; std::set<CInputCoin> bnb_coins;
CAmount bnb_value; CAmount bnb_value;
if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, bnb_coins, bnb_value)) { if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, bnb_coins, bnb_value)) {
@@ -372,7 +377,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
} }
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here. // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */); std::vector<OutputGroup> all_groups = GroupOutputs(wallet, coins, coin_selection_params, eligibility_filter, false /* positive_only */);
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output. // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output. // So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
std::set<CInputCoin> knapsack_coins; std::set<CInputCoin> knapsack_coins;
@@ -397,7 +402,7 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
return true; return true;
} }
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params)
{ {
std::vector<COutput> vCoins(vAvailableCoins); std::vector<COutput> vCoins(vAvailableCoins);
CAmount value_to_select = nTargetValue; CAmount value_to_select = nTargetValue;
@@ -423,8 +428,8 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
coin_control.ListSelected(vPresetInputs); coin_control.ListSelected(vPresetInputs);
for (const COutPoint& outpoint : vPresetInputs) for (const COutPoint& outpoint : vPresetInputs)
{ {
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
if (it != mapWallet.end()) if (it != wallet.mapWallet.end())
{ {
const CWalletTx& wtx = it->second; const CWalletTx& wtx = it->second;
// Clearly invalid input, fail // Clearly invalid input, fail
@@ -432,7 +437,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return false; return false;
} }
// Just to calculate the marginal byte size // Just to calculate the marginal byte size
CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false)); CInputCoin coin(wtx.tx, outpoint.n, GetTxSpendSize(wallet, wtx, outpoint.n, false));
nValueFromPresetInputs += coin.txout.nValue; nValueFromPresetInputs += coin.txout.nValue;
if (coin.m_input_bytes <= 0) { if (coin.m_input_bytes <= 0) {
return false; // Not solvable, can't estimate size for fee return false; // Not solvable, can't estimate size for fee
@@ -460,7 +465,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
unsigned int limit_ancestor_count = 0; unsigned int limit_ancestor_count = 0;
unsigned int limit_descendant_count = 0; unsigned int limit_descendant_count = 0;
chain().getPackageLimits(limit_ancestor_count, limit_descendant_count); wallet.chain().getPackageLimits(limit_ancestor_count, limit_descendant_count);
const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count); const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count);
const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
@@ -483,32 +488,32 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six // If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
// confirmations on outputs received from other wallets and only spend confirmed change. // confirmations on outputs received from other wallets and only spend confirmed change.
if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
if (AttemptSelection(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
// Fall back to using zero confirmation change (but with as few ancestors in the mempool as // Fall back to using zero confirmation change (but with as few ancestors in the mempool as
// possible) if we cannot fund the transaction otherwise. // possible) if we cannot fund the transaction otherwise.
if (m_spend_zero_conf_change) { if (wallet.m_spend_zero_conf_change) {
if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true;
if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) { vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true; return true;
} }
if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) { vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true; return true;
} }
// If partial groups are allowed, relax the requirement of spending OutputGroups (groups // If partial groups are allowed, relax the requirement of spending OutputGroups (groups
// of UTXOs sent to the same address, which are obviously controlled by a single wallet) // of UTXOs sent to the same address, which are obviously controlled by a single wallet)
// in their entirety. // in their entirety.
if (AttemptSelection(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), if (AttemptSelection(wallet, value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) { vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true; return true;
} }
// Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs
// received from other wallets. // received from other wallets.
if (coin_control.m_include_unsafe_inputs if (coin_control.m_include_unsafe_inputs
&& AttemptSelection(value_to_select, && AttemptSelection(wallet, value_to_select,
CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) { vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true; return true;
@@ -516,7 +521,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// Try with unlimited ancestors/descendants. The transaction will still need to meet // Try with unlimited ancestors/descendants. The transaction will still need to meet
// mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but
// OutputGroups use heuristics that may overestimate ancestor/descendant counts. // OutputGroups use heuristics that may overestimate ancestor/descendant counts.
if (!fRejectLongChains && AttemptSelection(value_to_select, if (!fRejectLongChains && AttemptSelection(wallet, value_to_select,
CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */),
vCoins, setCoinsRet, nValueRet, coin_selection_params)) { vCoins, setCoinsRet, nValueRet, coin_selection_params)) {
return true; return true;
@@ -595,7 +600,8 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
return locktime; return locktime;
} }
bool CWallet::CreateTransactionInternal( static bool CreateTransactionInternal(
CWallet& wallet,
const std::vector<CRecipient>& vecSend, const std::vector<CRecipient>& vecSend,
CTransactionRef& tx, CTransactionRef& tx,
CAmount& nFeeRet, CAmount& nFeeRet,
@@ -603,22 +609,22 @@ bool CWallet::CreateTransactionInternal(
bilingual_str& error, bilingual_str& error,
const CCoinControl& coin_control, const CCoinControl& coin_control,
FeeCalculation& fee_calc_out, FeeCalculation& fee_calc_out,
bool sign) bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
AssertLockHeld(cs_wallet); AssertLockHeld(wallet.cs_wallet);
CMutableTransaction txNew; // The resulting transaction that we make CMutableTransaction txNew; // The resulting transaction that we make
txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight()); txNew.nLockTime = GetLocktimeForNewTransaction(wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
// Set the long term feerate estimate to the wallet's consolidate feerate // Set the long term feerate estimate to the wallet's consolidate feerate
coin_selection_params.m_long_term_feerate = m_consolidate_feerate; coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate;
CAmount recipients_sum = 0; CAmount recipients_sum = 0;
const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); const OutputType change_type = wallet.TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : wallet.m_default_change_type, vecSend);
ReserveDestination reservedest(this, change_type); ReserveDestination reservedest(&wallet, change_type);
unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from unsigned int outputs_to_subtract_fee_from = 0; // The number of outputs which we are subtracting the fee from
for (const auto& recipient : vecSend) { for (const auto& recipient : vecSend) {
recipients_sum += recipient.nAmount; recipients_sum += recipient.nAmount;
@@ -662,7 +668,7 @@ bool CWallet::CreateTransactionInternal(
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
// Get size of spending the change output // Get size of spending the change output
int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, &wallet);
// If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
// as lower-bound to allow BnB to do it's thing // as lower-bound to allow BnB to do it's thing
if (change_spend_size == -1) { if (change_spend_size == -1) {
@@ -672,18 +678,18 @@ bool CWallet::CreateTransactionInternal(
} }
// Set discard feerate // Set discard feerate
coin_selection_params.m_discard_feerate = GetDiscardRate(*this); coin_selection_params.m_discard_feerate = GetDiscardRate(wallet);
// Get the fee rate to use effective values in coin selection // Get the fee rate to use effective values in coin selection
FeeCalculation feeCalc; FeeCalculation feeCalc;
coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc); coin_selection_params.m_effective_feerate = GetMinimumFeeRate(wallet, coin_control, &feeCalc);
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
// provided one // provided one
if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) { if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)); error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
return false; return false;
} }
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
// eventually allow a fallback fee // eventually allow a fallback fee
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
return false; return false;
@@ -710,7 +716,7 @@ bool CWallet::CreateTransactionInternal(
coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
} }
if (IsDust(txout, chain().relayDustFee())) if (IsDust(txout, wallet.chain().relayDustFee()))
{ {
error = _("Transaction amount too small"); error = _("Transaction amount too small");
return false; return false;
@@ -724,12 +730,12 @@ bool CWallet::CreateTransactionInternal(
// Get available coins // Get available coins
std::vector<COutput> vAvailableCoins; std::vector<COutput> vAvailableCoins;
AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); AvailableCoins(wallet, vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use // Choose coins to use
CAmount inputs_sum = 0; CAmount inputs_sum = 0;
std::set<CInputCoin> setCoins; std::set<CInputCoin> setCoins;
if (!SelectCoins(vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params)) if (!SelectCoins(wallet, vAvailableCoins, /* nTargetValue */ selection_target, setCoins, inputs_sum, coin_control, coin_selection_params))
{ {
error = _("Insufficient funds"); error = _("Insufficient funds");
return false; return false;
@@ -767,13 +773,13 @@ bool CWallet::CreateTransactionInternal(
// to avoid conflicting with other possible uses of nSequence, // to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior // and in the spirit of "smallest possible change from prior
// behavior." // behavior."
const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1);
for (const auto& coin : selected_coins) { for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
} }
// Calculate the transaction fee // Calculate the transaction fee
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
int nBytes = tx_sizes.vsize; int nBytes = tx_sizes.vsize;
if (nBytes < 0) { if (nBytes < 0) {
error = _("Signing transaction failed"); error = _("Signing transaction failed");
@@ -798,7 +804,7 @@ bool CWallet::CreateTransactionInternal(
txNew.vout.erase(change_position); txNew.vout.erase(change_position);
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, coin_control.fAllowWatchOnly);
nBytes = tx_sizes.vsize; nBytes = tx_sizes.vsize;
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes); fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
} }
@@ -835,7 +841,7 @@ bool CWallet::CreateTransactionInternal(
} }
// Error if this output is reduced to be below dust // Error if this output is reduced to be below dust
if (IsDust(txout, chain().relayDustFee())) { if (IsDust(txout, wallet.chain().relayDustFee())) {
if (txout.nValue < 0) { if (txout.nValue < 0) {
error = _("The transaction amount is too small to pay the fee"); error = _("The transaction amount is too small to pay the fee");
} else { } else {
@@ -854,7 +860,7 @@ bool CWallet::CreateTransactionInternal(
return false; return false;
} }
if (sign && !SignTransaction(txNew)) { if (sign && !wallet.SignTransaction(txNew)) {
error = _("Signing transaction failed"); error = _("Signing transaction failed");
return false; return false;
} }
@@ -870,14 +876,14 @@ bool CWallet::CreateTransactionInternal(
return false; return false;
} }
if (nFeeRet > m_default_max_tx_fee) { if (nFeeRet > wallet.m_default_max_tx_fee) {
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false; return false;
} }
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits // Lastly, ensure this tx will pass the mempool's chain limits
if (!chain().checkChainLimits(tx)) { if (!wallet.chain().checkChainLimits(tx)) {
error = _("Transaction has too long of a mempool chain"); error = _("Transaction has too long of a mempool chain");
return false; return false;
} }
@@ -888,7 +894,7 @@ bool CWallet::CreateTransactionInternal(
reservedest.KeepDestination(); reservedest.KeepDestination();
fee_calc_out = feeCalc; fee_calc_out = feeCalc;
WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
feeCalc.est.pass.start, feeCalc.est.pass.end, feeCalc.est.pass.start, feeCalc.est.pass.end,
(feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0,
@@ -899,7 +905,8 @@ bool CWallet::CreateTransactionInternal(
return true; return true;
} }
bool CWallet::CreateTransaction( bool CreateTransaction(
CWallet& wallet,
const std::vector<CRecipient>& vecSend, const std::vector<CRecipient>& vecSend,
CTransactionRef& tx, CTransactionRef& tx,
CAmount& nFeeRet, CAmount& nFeeRet,
@@ -919,23 +926,23 @@ bool CWallet::CreateTransaction(
return false; return false;
} }
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
int nChangePosIn = nChangePosInOut; int nChangePosIn = nChangePosInOut;
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr) Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign); bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign);
// try with avoidpartialspends unless it's enabled already // try with avoidpartialspends unless it's enabled already
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) { if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
CCoinControl tmp_cc = coin_control; CCoinControl tmp_cc = coin_control;
tmp_cc.m_avoid_partial_spends = true; tmp_cc.m_avoid_partial_spends = true;
CAmount nFeeRet2; CAmount nFeeRet2;
CTransactionRef tx2; CTransactionRef tx2;
int nChangePosInOut2 = nChangePosIn; int nChangePosInOut2 = nChangePosIn;
bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) { if (CreateTransactionInternal(wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
// if fee of this alternative one is within the range of the max fee, we use this one // if fee of this alternative one is within the range of the max fee, we use this one
const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee; const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped"); wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
if (use_aps) { if (use_aps) {
tx = tx2; tx = tx2;
nFeeRet = nFeeRet2; nFeeRet = nFeeRet2;
@@ -946,7 +953,7 @@ bool CWallet::CreateTransaction(
return res; return res;
} }
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl) bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
{ {
std::vector<CRecipient> vecSend; std::vector<CRecipient> vecSend;
@@ -965,11 +972,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
// Acquire the locks to prevent races to the new locked unspents between the // Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true). // CreateTransaction call and LockCoin calls (when lockUnspents is true).
LOCK(cs_wallet); LOCK(wallet.cs_wallet);
CTransactionRef tx_new; CTransactionRef tx_new;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) { if (!CreateTransaction(wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) {
return false; return false;
} }
@@ -990,7 +997,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
} }
if (lockUnspents) { if (lockUnspents) {
LockCoin(txin.prevout); wallet.LockCoin(txin.prevout);
} }
} }
+79 -4
View File
@@ -9,6 +9,9 @@
#include <wallet/transaction.h> #include <wallet/transaction.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
/** Get the marginal bytes if spending the specified output from this transaction */
int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig = false);
class COutput class COutput
{ {
public: public:
@@ -43,13 +46,13 @@ public:
*/ */
bool fSafe; bool fSafe;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false) COutput(const CWallet& wallet, const CWalletTx& wtx, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
{ {
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in; tx = &wtx; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
// If known and signable by the given wallet, compute nInputBytes // If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1 // Failure will keep this value -1
if (fSpendable && tx) { if (fSpendable) {
nInputBytes = tx->GetSpendSize(i, use_max_sig); nInputBytes = GetTxSpendSize(wallet, wtx, i, use_max_sig);
} }
} }
@@ -61,4 +64,76 @@ public:
} }
}; };
//Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
struct TxSize {
int64_t vsize{-1};
int64_t weight{-1};
};
/** Calculate the size of the transaction assuming all signatures are max size
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
* be AllInputsMine). */
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
/**
* populate vCoins with vector of available COutputs.
*/
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);
/**
* Find non-change parent output.
*/
const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransaction& tx, int output) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only);
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled
* param@[in] coins Set of UTXOs to consider. These will be categorized into
* OutputGroups and filtered using eligibility_filter before
* selecting coins.
* param@[out] setCoinsRet Populated with the coins selected if successful.
* param@[out] nValueRet Used to return the total value of selected coins.
*/
bool AttemptSelection(const CWallet& wallet, const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params);
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coin_control are selected; never select unconfirmed coins if they are not ours
* param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
* coin_control and Coin Selection if successful.
* param@[out] nValueRet Total value of selected coins including pre-selected ones
* from coin_control and Coin Selection if successful.
*/
bool SelectCoins(const CWallet& wallet, const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
/**
* Create a new transaction paying the recipients with a set of coins
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
/**
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
#endif // BITCOIN_WALLET_SPEND_H #endif // BITCOIN_WALLET_SPEND_H
+5 -4
View File
@@ -10,6 +10,7 @@
#include <util/translation.h> #include <util/translation.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/spend.h>
#include <wallet/test/wallet_test_fixture.h> #include <wallet/test/wallet_test_fixture.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
@@ -87,7 +88,7 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1); wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
wtx->m_is_cache_empty = false; wtx->m_is_cache_empty = false;
} }
COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); COutput output(wallet, *wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output); vCoins.push_back(output);
} }
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false) static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
@@ -144,7 +145,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinEligibilityFilter& filter) inline std::vector<OutputGroup>& KnapsackGroupOutputs(const CoinEligibilityFilter& filter)
{ {
static std::vector<OutputGroup> static_groups; static std::vector<OutputGroup> static_groups;
static_groups = testWallet.GroupOutputs(vCoins, coin_selection_params, filter, /* positive_only */false); static_groups = GroupOutputs(testWallet, vCoins, coin_selection_params, filter, /* positive_only */false);
return static_groups; return static_groups;
} }
@@ -316,7 +317,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_control.fAllowOtherInputs = true; coin_control.fAllowOtherInputs = true;
coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i)); coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0); coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb)); BOOST_CHECK(SelectCoins(*wallet, vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb));
} }
} }
@@ -657,7 +658,7 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test)
CoinSet out_set; CoinSet out_set;
CAmount out_value = 0; CAmount out_value = 0;
CCoinControl cc; CCoinControl cc;
BOOST_CHECK(testWallet.SelectCoins(vCoins, target, out_set, out_value, cc, cs_params)); BOOST_CHECK(SelectCoins(testWallet, vCoins, target, out_set, out_value, cc, cs_params));
BOOST_CHECK_GE(out_value, target); BOOST_CHECK_GE(out_value, target);
} }
} }
+2 -2
View File
@@ -22,12 +22,12 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION); CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx1; CTransactionRef prev_tx1;
s_prev_tx1 >> prev_tx1; s_prev_tx1 >> prev_tx1;
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx1)); m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(prev_tx1));
CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION); CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx2; CTransactionRef prev_tx2;
s_prev_tx2 >> prev_tx2; s_prev_tx2 >> prev_tx2;
m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx2)); m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(prev_tx2));
// Add scripts // Add scripts
CScript rs1; CScript rs1;
+2 -1
View File
@@ -5,6 +5,7 @@
#include <policy/fees.h> #include <policy/fees.h>
#include <validation.h> #include <validation.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/spend.h>
#include <wallet/test/util.h> #include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h> #include <wallet/test/wallet_test_fixture.h>
@@ -32,7 +33,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
coin_control.m_feerate.emplace(10000); coin_control.m_feerate.emplace(10000);
coin_control.fOverrideFeeRate = true; coin_control.fOverrideFeeRate = true;
FeeCalculation fee_calc; FeeCalculation fee_calc;
BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, change_pos, error, coin_control, fee_calc)); BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, change_pos, error, coin_control, fee_calc));
BOOST_CHECK_EQUAL(tx->vout.size(), 1); BOOST_CHECK_EQUAL(tx->vout.size(), 1);
BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee); BOOST_CHECK_EQUAL(tx->vout[0].nValue, recipient.nAmount + leftover_input_amount - fee);
BOOST_CHECK_GT(fee, 0); BOOST_CHECK_GT(fee, 0);
+17 -15
View File
@@ -21,6 +21,8 @@
#include <validation.h> #include <validation.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/context.h> #include <wallet/context.h>
#include <wallet/receive.h>
#include <wallet/spend.h>
#include <wallet/test/util.h> #include <wallet/test/util.h>
#include <wallet/test/wallet_test_fixture.h> #include <wallet/test/wallet_test_fixture.h>
@@ -103,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull()); BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK(result.last_scanned_block.IsNull()); BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height); BOOST_CHECK(!result.last_scanned_height);
BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0); BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
} }
// Verify ScanForWalletTransactions picks up transactions in both the old // Verify ScanForWalletTransactions picks up transactions in both the old
@@ -122,7 +124,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull()); BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash()); BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight); BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 100 * COIN); BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 100 * COIN);
} }
// Prune the older block file. // Prune the older block file.
@@ -148,7 +150,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash()); BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash()); BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight); BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 50 * COIN); BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 50 * COIN);
} }
// Prune the remaining block file. // Prune the remaining block file.
@@ -173,7 +175,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash()); BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.last_scanned_block.IsNull()); BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height); BOOST_CHECK(!result.last_scanned_height);
BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0); BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
} }
} }
@@ -319,7 +321,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{ {
CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase()); CWallet wallet(m_node.chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan(); auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back()); CWalletTx wtx(m_coinbase_txns.back());
LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore); LOCK2(wallet.cs_wallet, spk_man->cs_KeyStore);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash()); wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
@@ -329,13 +331,13 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
// Call GetImmatureCredit() once before adding the key to the wallet to // Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0. // cache the current immature credit amount, which is 0.
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0); BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 0);
// Invalidate the cached value, add the key, and make sure a new immature // Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated. // credit amount is calculated.
wtx.MarkDirty(); wtx.MarkDirty();
BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey())); BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN); BOOST_CHECK_EQUAL(CachedTxGetImmatureCredit(wallet, wtx), 50*COIN);
} }
static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime) static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@@ -506,7 +508,7 @@ public:
CCoinControl dummy; CCoinControl dummy;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
{ {
BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, fee, changePos, error, dummy, fee_calc_out)); BOOST_CHECK(CreateTransaction(*wallet, {recipient}, tx, fee, changePos, error, dummy, fee_calc_out));
} }
wallet->CommitTransaction(tx, {}, {}); wallet->CommitTransaction(tx, {}, {});
CMutableTransaction blocktx; CMutableTransaction blocktx;
@@ -528,7 +530,7 @@ public:
std::unique_ptr<CWallet> wallet; std::unique_ptr<CWallet> wallet;
}; };
BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
{ {
std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString(); std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString();
@@ -537,14 +539,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
std::map<CTxDestination, std::vector<COutput>> list; std::map<CTxDestination, std::vector<COutput>> list;
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
list = wallet->ListCoins(); list = ListCoins(*wallet);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
// Check initial balance from one mature coinbase transaction. // Check initial balance from one mature coinbase transaction.
BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance()); BOOST_CHECK_EQUAL(50 * COIN, GetAvailableBalance(*wallet));
// Add a transaction creating a change address, and confirm ListCoins still // Add a transaction creating a change address, and confirm ListCoins still
// returns the coin associated with the change address underneath the // returns the coin associated with the change address underneath the
@@ -553,7 +555,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */}); AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
list = wallet->ListCoins(); list = ListCoins(*wallet);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
@@ -563,7 +565,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
std::vector<COutput> available; std::vector<COutput> available;
wallet->AvailableCoins(available); AvailableCoins(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U); BOOST_CHECK_EQUAL(available.size(), 2U);
} }
for (const auto& group : list) { for (const auto& group : list) {
@@ -575,14 +577,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
std::vector<COutput> available; std::vector<COutput> available;
wallet->AvailableCoins(available); AvailableCoins(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U); BOOST_CHECK_EQUAL(available.size(), 0U);
} }
// Confirm ListCoins still returns same result as before, despite coins // Confirm ListCoins still returns same result as before, despite coins
// being locked. // being locked.
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
list = wallet->ListCoins(); list = ListCoins(*wallet);
} }
BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
+3 -70
View File
@@ -17,12 +17,8 @@
#include <list> #include <list>
#include <vector> #include <vector>
struct COutputEntry;
typedef std::map<std::string, std::string> mapValue_t; typedef std::map<std::string, std::string> mapValue_t;
//Get the marginal bytes of spending the specified output
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
{ {
@@ -34,6 +30,7 @@ static inline void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue)
nOrderPos = atoi64(mapValue["n"]); nOrderPos = atoi64(mapValue["n"]);
} }
static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
{ {
if (nOrderPos == -1) if (nOrderPos == -1)
@@ -68,8 +65,6 @@ public:
class CWalletTx class CWalletTx
{ {
private: private:
const CWallet* const pwallet;
/** Constant used in hashBlock to indicate tx has been abandoned, only used at /** Constant used in hashBlock to indicate tx has been abandoned, only used at
* serialization/deserialization to avoid ambiguity with conflicted. * serialization/deserialization to avoid ambiguity with conflicted.
*/ */
@@ -126,7 +121,6 @@ public:
// memory only // memory only
enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS }; enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]; mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
/** /**
* This flag is true if all m_amounts caches are empty. This is particularly * This flag is true if all m_amounts caches are empty. This is particularly
@@ -139,9 +133,8 @@ public:
mutable bool fInMempool; mutable bool fInMempool;
mutable CAmount nChangeCached; mutable CAmount nChangeCached;
CWalletTx(const CWallet* wallet, CTransactionRef arg) CWalletTx(CTransactionRef arg)
: pwallet(wallet), : tx(std::move(arg))
tx(std::move(arg))
{ {
Init(); Init();
} }
@@ -264,72 +257,13 @@ public:
m_is_cache_empty = true; m_is_cache_empty = true;
} }
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache = true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet.
CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
CAmount GetChange() const;
/** Get the marginal bytes if spending the specified output from this transaction */
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
{
return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
}
void GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const;
bool IsFromMe(const isminefilter& filter) const
{
return (GetDebit(filter) > 0);
}
/** True if only scriptSigs are different */ /** True if only scriptSigs are different */
bool IsEquivalentTo(const CWalletTx& tx) const; bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const; bool InMempool() const;
bool IsTrusted() const;
int64_t GetTxTime() const; int64_t GetTxTime() const;
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
// "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
// resolve the issue of member access into incomplete type CWallet. Note
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
// "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
// resolve the issue of member access into incomplete type CWallet. Note
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
int GetBlocksToMaturity() const;
bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
void setAbandoned() void setAbandoned()
{ {
@@ -346,7 +280,6 @@ public:
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); } const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsCoinBase() const { return tx->IsCoinBase(); }
bool IsImmatureCoinBase() const;
// Disable copying of CWalletTx objects to prevent bugs where instances get // Disable copying of CWalletTx objects to prevent bugs where instances get
// copied in and out of the mapWallet map, and fields are updated in the // copied in and out of the mapWallet map, and fields are updated in the
+32 -34
View File
@@ -581,7 +581,7 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
const uint256& wtxid = it->second; const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) { if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain(); int depth = GetTxDepthInMainChain(mit->second);
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent return true; // Spent
} }
@@ -900,7 +900,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
} }
// Inserts only if not already there, returns tx inserted or tx found // Inserts only if not already there, returns tx inserted or tx found
auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, tx)); auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx));
CWalletTx& wtx = (*ret.first).second; CWalletTx& wtx = (*ret.first).second;
bool fInsertedNew = ret.second; bool fInsertedNew = ret.second;
bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew); bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);
@@ -984,7 +984,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx)
{ {
const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, nullptr)); const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(nullptr));
CWalletTx& wtx = ins.first->second; CWalletTx& wtx = ins.first->second;
if (!fill_wtx(wtx, ins.second)) { if (!fill_wtx(wtx, ins.second)) {
return false; return false;
@@ -1074,7 +1074,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx); const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool(); return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool();
} }
void CWallet::MarkInputsDirty(const CTransactionRef& tx) void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@@ -1100,7 +1100,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(hashTx); auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
const CWalletTx& origtx = it->second; const CWalletTx& origtx = it->second;
if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) { if (GetTxDepthInMainChain(origtx) != 0 || origtx.InMempool()) {
return false; return false;
} }
@@ -1113,7 +1113,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(); int currentconfirm = GetTxDepthInMainChain(wtx);
// If the orig tx was not in block, none of its spends can be // If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0); assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
@@ -1168,7 +1168,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
auto it = mapWallet.find(now); auto it = mapWallet.find(now);
assert(it != mapWallet.end()); assert(it != mapWallet.end());
CWalletTx& wtx = it->second; CWalletTx& wtx = it->second;
int currentconfirm = wtx.GetDepthInMainChain(); int currentconfirm = GetTxDepthInMainChain(wtx);
if (conflictconfirms < currentconfirm) { if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update. // Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block. // Mark transaction as conflicted with this block.
@@ -1698,7 +1698,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid); assert(wtx.GetHash() == wtxid);
int nDepth = wtx.GetDepthInMainChain(); int nDepth = GetTxDepthInMainChain(wtx);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1709,24 +1709,24 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) { for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second); CWalletTx& wtx = *(item.second);
std::string unused_err_string; std::string unused_err_string;
wtx.SubmitMemoryPoolAndRelay(unused_err_string, false); SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, false);
} }
} }
bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay) bool CWallet::SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const
{ {
// Can't relay if wallet is not broadcasting // Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false; if (!GetBroadcastTransactions()) return false;
// Don't relay abandoned transactions // Don't relay abandoned transactions
if (isAbandoned()) return false; if (wtx.isAbandoned()) return false;
// Don't try to submit coinbase transactions. These would fail anyway but would // Don't try to submit coinbase transactions. These would fail anyway but would
// cause log spam. // cause log spam.
if (IsCoinBase()) return false; if (wtx.IsCoinBase()) return false;
// Don't try to submit conflicted or confirmed transactions. // Don't try to submit conflicted or confirmed transactions.
if (GetDepthInMainChain() != 0) return false; if (GetTxDepthInMainChain(wtx) != 0) return false;
// Submit transaction to mempool for relay // Submit transaction to mempool for relay
pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString()); WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
// We must set fInMempool here - while it will be re-set to true by the // We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a // entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors // user could call sendmoney in a loop and hit spurious out of funds errors
@@ -1736,18 +1736,17 @@ bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
// Irrespective of the failure reason, un-marking fInMempool // Irrespective of the failure reason, un-marking fInMempool
// out-of-order is incorrect - it should be unmarked when // out-of-order is incorrect - it should be unmarked when
// TransactionRemovedFromMempool fires. // TransactionRemovedFromMempool fires.
bool ret = pwallet->chain().broadcastTransaction(tx, pwallet->m_default_max_tx_fee, relay, err_string); bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
fInMempool |= ret; wtx.fInMempool |= ret;
return ret; return ret;
} }
std::set<uint256> CWalletTx::GetConflicts() const std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
{ {
std::set<uint256> result; std::set<uint256> result;
if (pwallet != nullptr)
{ {
uint256 myHash = GetHash(); uint256 myHash = wtx.GetHash();
result = pwallet->GetConflicts(myHash); result = GetConflicts(myHash);
result.erase(myHash); result.erase(myHash);
} }
return result; return result;
@@ -1785,11 +1784,11 @@ void CWallet::ResendWalletTransactions()
for (std::pair<const uint256, CWalletTx>& item : mapWallet) { for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
// Attempt to rebroadcast all txes more than 5 minutes older than // Attempt to rebroadcast all txes more than 5 minutes older than
// the last block. SubmitMemoryPoolAndRelay() will not rebroadcast // the last block. SubmitTxMemoryPoolAndRelay() will not rebroadcast
// any confirmed or conflicting txs. // any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue; if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
std::string unused_err_string; std::string unused_err_string;
if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count; if (SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, true)) ++submitted_tx_count;
} }
} // cs_wallet } // cs_wallet
@@ -1977,7 +1976,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
} }
std::string err_string; std::string err_string;
if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) { if (!SubmitTxMemoryPoolAndRelay(wtx, err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string); WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} }
@@ -2911,28 +2910,27 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
m_pre_split = false; m_pre_split = false;
} }
int CWalletTx::GetDepthInMainChain() const int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
{ {
assert(pwallet != nullptr); AssertLockHeld(cs_wallet);
AssertLockHeld(pwallet->cs_wallet); if (wtx.isUnconfirmed() || wtx.isAbandoned()) return 0;
if (isUnconfirmed() || isAbandoned()) return 0;
return (pwallet->GetLastBlockHeight() - m_confirm.block_height + 1) * (isConflicted() ? -1 : 1); return (GetLastBlockHeight() - wtx.m_confirm.block_height + 1) * (wtx.isConflicted() ? -1 : 1);
} }
int CWalletTx::GetBlocksToMaturity() const int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
{ {
if (!IsCoinBase()) if (!wtx.IsCoinBase())
return 0; return 0;
int chain_depth = GetDepthInMainChain(); int chain_depth = GetTxDepthInMainChain(wtx);
assert(chain_depth >= 0); // coinbase tx should not be conflicted assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth); return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
} }
bool CWalletTx::IsImmatureCoinBase() const bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
{ {
// note GetBlocksToMaturity is 0 for non-coinbase tx // note GetBlocksToMaturity is 0 for non-coinbase tx
return GetBlocksToMaturity() > 0; return GetTxBlocksToMaturity(wtx) > 0;
} }
bool CWallet::IsCrypted() const bool CWallet::IsCrypted() const
+34 -92
View File
@@ -21,9 +21,7 @@
#include <validationinterface.h> #include <validationinterface.h>
#include <wallet/coinselection.h> #include <wallet/coinselection.h>
#include <wallet/crypter.h> #include <wallet/crypter.h>
#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h> #include <wallet/scriptpubkeyman.h>
#include <wallet/spend.h>
#include <wallet/transaction.h> #include <wallet/transaction.h>
#include <wallet/walletdb.h> #include <wallet/walletdb.h>
#include <wallet/walletutil.h> #include <wallet/walletutil.h>
@@ -331,8 +329,6 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure // ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers; std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Catch wallet up to current chain, scanning new blocks, updating the best * Catch wallet up to current chain, scanning new blocks, updating the best
* block locator and m_last_block_processed, and registering for * block locator and m_last_block_processed, and registering for
@@ -353,17 +349,6 @@ public:
return *m_database; return *m_database;
} }
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coin_control are selected; never select unconfirmed coins if they are not ours
* param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
* coin_control and Coin Selection if successful.
* param@[out] nValueRet Total value of selected coins including pre-selected ones
* from coin_control and Coin Selection if successful.
*/
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Get a name for this wallet for logging/debugging purposes. /** Get a name for this wallet for logging/debugging purposes.
*/ */
const std::string& GetName() const { return m_name; } const std::string& GetName() const { return m_name; }
@@ -419,48 +404,47 @@ public:
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; } interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
// "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
// resolve the issue of member access into incomplete type CWallet. Note
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
std::set<uint256> GetTxConflicts(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
// "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
// resolve the issue of member access into incomplete type CWallet. Note
// that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
// in place.
int GetTxDepthInMainChain(const CWalletTx& wtx) const NO_THREAD_SAFETY_ANALYSIS;
bool IsTxInMainChain(const CWalletTx& wtx) const { return GetTxDepthInMainChain(wtx) > 0; }
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
int GetTxBlocksToMaturity(const CWalletTx& wtx) const;
bool IsTxImmatureCoinBase(const CWalletTx& wtx) const;
//! check whether we support the named feature //! check whether we support the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); } bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); }
/**
* populate vCoins with vector of available COutputs.
*/
void AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Find non-change parent output.
*/
const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled
* param@[in] coins Set of UTXOs to consider. These will be categorized into
* OutputGroups and filtered using eligibility_filter before
* selecting coins.
* param@[out] setCoinsRet Populated with the coins selected if successful.
* param@[out] nValueRet Used to return the total value of selected coins.
*/
bool AttemptSelection(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Whether this or any known UTXO with the same single key has been spent. // Whether this or any known UTXO with the same single key has been spent.
bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const;
/** Display address on an external signer. Returns false if external signer support is not compiled */ /** Display address on an external signer. Returns false if external signer support is not compiled */
bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -546,24 +530,9 @@ public:
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override; void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions(); void ResendWalletTransactions();
struct Balance {
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
CAmount m_watchonly_trusted{0};
CAmount m_watchonly_untrusted_pending{0};
CAmount m_watchonly_immature{0};
};
Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const; OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
/**
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
/** Fetch the inputs and sign with SIGHASH_ALL. */ /** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Sign the tx given the input coins and sighash. */ /** Sign the tx given the input coins and sighash. */
@@ -590,12 +559,6 @@ public:
bool bip32derivs = true, bool bip32derivs = true,
size_t* n_signed = nullptr) const; size_t* n_signed = nullptr) const;
/**
* Create a new transaction paying the recipients with a set of coins
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign = true);
/** /**
* Submit the transaction to the node's mempool and then relay to peers. * Submit the transaction to the node's mempool and then relay to peers.
* Should be called after CreateTransaction unless you want to abort * Should be called after CreateTransaction unless you want to abort
@@ -607,6 +570,9 @@ public:
*/ */
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm); void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
/** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */
bool SubmitTxMemoryPoolAndRelay(const CWalletTx& wtx, std::string& err_string, bool relay) const;
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{ {
std::vector<CTxOut> v_txouts(txouts.size()); std::vector<CTxOut> v_txouts(txouts.size());
@@ -664,9 +630,6 @@ public:
int64_t GetOldestKeyPoolTime() const; int64_t GetOldestKeyPoolTime() const;
std::set<std::set<CTxDestination>> GetAddressGroupings() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::map<CTxDestination, CAmount> GetAddressBalances() const;
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
/** /**
@@ -680,25 +643,16 @@ public:
isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Returns amount of debit if the input matches the * Returns amount of debit if the input matches the
* filter, otherwise returns 0 * filter, otherwise returns 0
*/ */
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */ /** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const; bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
/** Returns whether all of the inputs match the filter */
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void chainStateFlushed(const CBlockLocator& loc) override; void chainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(); DBErrors LoadWallet();
@@ -965,18 +919,6 @@ public:
} }
}; };
struct TxSize {
int64_t vsize{-1};
int64_t weight{-1};
};
/** Calculate the size of the transaction assuming all signatures are max size
* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
* be IsAllFromMe). */
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
//! Add wallet name to persistent configuration so it will be loaded on startup. //! Add wallet name to persistent configuration so it will be loaded on startup.
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name); bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+1 -1
View File
@@ -954,7 +954,7 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWal
uint256 hash; uint256 hash;
ssKey >> hash; ssKey >> hash;
vTxHash.push_back(hash); vTxHash.push_back(hash);
vWtx.emplace_back(nullptr /* wallet */, nullptr /* tx */); vWtx.emplace_back(nullptr /* tx */);
ssValue >> vWtx.back(); ssValue >> vWtx.back();
} }
} }
+1 -1
View File
@@ -600,7 +600,7 @@ class WalletTest(BitcoinTestFramework):
total_txs = len(self.nodes[0].listtransactions("*", 99999)) total_txs = len(self.nodes[0].listtransactions("*", 99999))
# Try with walletrejectlongchains # Try with walletrejectlongchains
# Double chain limit but require combining inputs, so we pass SelectCoinsMinConf # Double chain limit but require combining inputs, so we pass AttemptSelection
self.stop_node(0) self.stop_node(0)
extra_args = ["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)] extra_args = ["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]
self.start_node(0, extra_args=extra_args) self.start_node(0, extra_args=extra_args)
-4
View File
@@ -24,10 +24,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"wallet/fees -> wallet/wallet -> wallet/fees" "wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet" "wallet/wallet -> wallet/walletdb -> wallet/wallet"
"node/coinstats -> validation -> node/coinstats" "node/coinstats -> validation -> node/coinstats"
# Temporary circular dependencies that allow wallet.h/wallet.cpp to be
# split up in a MOVEONLY commit. These are removed in #21206.
"wallet/receive -> wallet/wallet -> wallet/receive"
"wallet/spend -> wallet/wallet -> wallet/spend"
) )
EXIT_CODE=0 EXIT_CODE=0