mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-17 19:09:02 +00:00
Merge bitcoin/bitcoin#32523: wallet: Remove isminetypes
be776a1443fdf1a72e0d363c1566d71cb0cda8b5 wallet: Remove isminetype (Ava Chow)
009a69a616cf30217f6583e45cc049895b9e10d2 wallet: Remove ISMINE_USED (Ava Chow)
6a7aa015747e2634fe5a4b2f7fa0d104eb75c796 wallet: Remove COutput::spendable and AvailableCoinsListUnspent (Ava Chow)
620abe985e5150c3151192d08746b7845a69dbbf interfaces, gui: Remove is_mine output parameter from getAddress (Ava Chow)
Pull request description:
The remaining isminetypes are `ISMINE_SPENDABLE` and `ISMINE_USED`.
`ISMINE_USED` is only used as a filter for caching balances and is never actually returned from `IsMine`. Since we do still want this behavior, This PR changes the caching to utilize bools and explicit members variables to account for the avoid_reuse case. This allows us to remove `ISMINE_USED`.
`ISMINE_SPENDABLE` and `ISMINE_NO` are the only things that are returned by `IsMine`. This is a bool, so it can be replaced as such.
After removing `ISMINE_USED` and `ISMINE_SPENDABLE`, we are able to remove isminetypes altogether.
ACKs for top commit:
murchandamus:
ACK be776a1443fdf1a72e0d363c1566d71cb0cda8b5
fjahr:
reACK be776a1443fdf1a72e0d363c1566d71cb0cda8b5
davidgumberg:
crACK be776a1443
enirox001:
re-ACK be776a1
jlest01:
reACK be776a1443
Tree-SHA512: 689759f6a6ba20a1ae988b0c3abacb15424844f29a1ec2fcb2d1ca9d87b44ae68313e8f61d6fd310281b681144f0ade67e90fcfab807e982b52ed99441d9c987
This commit is contained in:
commit
8333aa5302
@ -74,7 +74,7 @@ static void CoinSelection(benchmark::Bench& bench)
|
||||
wallet::CoinsResult available_coins;
|
||||
for (const auto& wtx : wtxs) {
|
||||
const auto txout = wtx->tx->vout.at(0);
|
||||
available_coins.coins[OutputType::BECH32].emplace_back(COutPoint(wtx->GetHash(), 0), txout, /*depth=*/6 * 24, CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr), /*spendable=*/true, /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
|
||||
available_coins.coins[OutputType::BECH32].emplace_back(COutPoint(wtx->GetHash(), 0), txout, /*depth=*/6 * 24, CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr), /*solvable=*/true, /*safe=*/true, wtx->GetTxTime(), /*from_me=*/true, /*fees=*/ 0);
|
||||
}
|
||||
|
||||
const CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||
@ -105,7 +105,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
|
||||
CMutableTransaction tx;
|
||||
tx.vout.resize(nInput + 1);
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0);
|
||||
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/0, /*input_bytes=*/-1, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, /*fees=*/0);
|
||||
set.emplace_back();
|
||||
set.back().Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*descendants=*/ 0);
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#include <wallet/context.h>
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/test/util.h>
|
||||
#include <wallet/types.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
@ -58,8 +57,8 @@ static void WalletIsMine(benchmark::Bench& bench, int num_combo = 0)
|
||||
|
||||
bench.run([&] {
|
||||
LOCK(wallet->cs_wallet);
|
||||
isminetype mine = wallet->IsMine(script);
|
||||
assert(mine == ISMINE_NO);
|
||||
bool mine = wallet->IsMine(script);
|
||||
assert(!mine);
|
||||
});
|
||||
|
||||
TestUnloadWallet(std::move(wallet));
|
||||
|
||||
@ -43,10 +43,8 @@ namespace wallet {
|
||||
class CCoinControl;
|
||||
class CWallet;
|
||||
enum class AddressPurpose;
|
||||
enum isminetype : unsigned int;
|
||||
struct CRecipient;
|
||||
struct WalletContext;
|
||||
using isminefilter = std::underlying_type_t<isminetype>;
|
||||
} // namespace wallet
|
||||
|
||||
namespace interfaces {
|
||||
@ -117,7 +115,6 @@ public:
|
||||
//! Look up address in wallet, return whether exists.
|
||||
virtual bool getAddress(const CTxDestination& dest,
|
||||
std::string* name,
|
||||
wallet::isminetype* is_mine,
|
||||
wallet::AddressPurpose* purpose) = 0;
|
||||
|
||||
//! Get wallet address list.
|
||||
@ -225,16 +222,16 @@ public:
|
||||
virtual CAmount getAvailableBalance(const wallet::CCoinControl& coin_control) = 0;
|
||||
|
||||
//! Return whether transaction input belongs to wallet.
|
||||
virtual wallet::isminetype txinIsMine(const CTxIn& txin) = 0;
|
||||
virtual bool txinIsMine(const CTxIn& txin) = 0;
|
||||
|
||||
//! Return whether transaction output belongs to wallet.
|
||||
virtual wallet::isminetype txoutIsMine(const CTxOut& txout) = 0;
|
||||
virtual bool txoutIsMine(const CTxOut& txout) = 0;
|
||||
|
||||
//! Return debit amount if transaction input belongs to wallet.
|
||||
virtual CAmount getDebit(const CTxIn& txin, wallet::isminefilter filter) = 0;
|
||||
virtual CAmount getDebit(const CTxIn& txin) = 0;
|
||||
|
||||
//! Return credit amount if transaction input belongs to wallet.
|
||||
virtual CAmount getCredit(const CTxOut& txout, wallet::isminefilter filter) = 0;
|
||||
virtual CAmount getCredit(const CTxOut& txout) = 0;
|
||||
|
||||
//! Return AvailableCoins + LockedCoins grouped by wallet address.
|
||||
//! (put change in one group with wallet address)
|
||||
@ -356,11 +353,11 @@ public:
|
||||
struct WalletAddress
|
||||
{
|
||||
CTxDestination dest;
|
||||
wallet::isminetype is_mine;
|
||||
bool is_mine;
|
||||
wallet::AddressPurpose purpose;
|
||||
std::string name;
|
||||
|
||||
WalletAddress(CTxDestination dest, wallet::isminetype is_mine, wallet::AddressPurpose purpose, std::string name)
|
||||
WalletAddress(CTxDestination dest, bool is_mine, wallet::AddressPurpose purpose, std::string name)
|
||||
: dest(std::move(dest)), is_mine(is_mine), purpose(std::move(purpose)), name(std::move(name))
|
||||
{
|
||||
}
|
||||
@ -384,11 +381,11 @@ struct WalletBalances
|
||||
struct WalletTx
|
||||
{
|
||||
CTransactionRef tx;
|
||||
std::vector<wallet::isminetype> txin_is_mine;
|
||||
std::vector<wallet::isminetype> txout_is_mine;
|
||||
std::vector<bool> txin_is_mine;
|
||||
std::vector<bool> txout_is_mine;
|
||||
std::vector<bool> txout_is_change;
|
||||
std::vector<CTxDestination> txout_address;
|
||||
std::vector<wallet::isminetype> txout_address_is_mine;
|
||||
std::vector<bool> txout_address_is_mine;
|
||||
CAmount credit;
|
||||
CAmount debit;
|
||||
CAmount change;
|
||||
|
||||
@ -270,7 +270,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
// Check for duplicate addresses to prevent accidental deletion of addresses, if you try
|
||||
// to paste an existing address over another address (with a different label)
|
||||
if (walletModel->wallet().getAddress(
|
||||
newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
|
||||
newAddress, /*name=*/nullptr, /*purpose=*/ nullptr))
|
||||
{
|
||||
editStatus = DUPLICATE_ADDRESS;
|
||||
return false;
|
||||
@ -356,7 +356,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
|
||||
// Check for duplicate addresses
|
||||
{
|
||||
if (walletModel->wallet().getAddress(
|
||||
DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
|
||||
DecodeDestination(strAddress), /*name=*/nullptr, /*purpose=*/nullptr))
|
||||
{
|
||||
editStatus = DUPLICATE_ADDRESS;
|
||||
return QString();
|
||||
@ -429,7 +429,7 @@ bool AddressTableModel::getAddressData(const QString &address,
|
||||
std::string* name,
|
||||
wallet::AddressPurpose* purpose) const {
|
||||
CTxDestination destination = DecodeDestination(address.toStdString());
|
||||
return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose);
|
||||
return walletModel->wallet().getAddress(destination, name, purpose);
|
||||
}
|
||||
|
||||
int AddressTableModel::lookupAddress(const QString &address) const
|
||||
|
||||
@ -17,17 +17,12 @@
|
||||
#include <logging.h>
|
||||
#include <policy/policy.h>
|
||||
#include <validation.h>
|
||||
#include <wallet/types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <QLatin1String>
|
||||
|
||||
using wallet::ISMINE_ALL;
|
||||
using wallet::ISMINE_SPENDABLE;
|
||||
using wallet::isminetype;
|
||||
|
||||
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTxStatus& status, bool inMempool)
|
||||
{
|
||||
int depth = status.depth_in_main_chain;
|
||||
@ -143,8 +138,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
CTxDestination address = DecodeDestination(rec->address);
|
||||
if (IsValidDestination(address)) {
|
||||
std::string name;
|
||||
isminetype ismine;
|
||||
if (wallet.getAddress(address, &name, &ismine, /* purpose= */ nullptr))
|
||||
if (wallet.getAddress(address, &name, /*purpose=*/nullptr))
|
||||
{
|
||||
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
|
||||
strHTML += "<b>" + tr("To") + ":</b> ";
|
||||
@ -171,7 +165,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
CTxDestination dest = DecodeDestination(strAddress);
|
||||
std::string name;
|
||||
if (wallet.getAddress(
|
||||
dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
|
||||
dest, &name, /*purpose=*/nullptr) && !name.empty())
|
||||
strHTML += GUIUtil::HtmlEscape(name) + " ";
|
||||
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
|
||||
}
|
||||
@ -186,7 +180,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
//
|
||||
CAmount nUnmatured = 0;
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
nUnmatured += wallet.getCredit(txout, ISMINE_ALL);
|
||||
nUnmatured += wallet.getCredit(txout);
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> ";
|
||||
if (status.is_in_main_chain)
|
||||
strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")";
|
||||
@ -203,19 +197,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
}
|
||||
else
|
||||
{
|
||||
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
||||
for (const isminetype mine : wtx.txin_is_mine)
|
||||
{
|
||||
if(fAllFromMe > mine) fAllFromMe = mine;
|
||||
}
|
||||
bool all_from_me = std::all_of(wtx.txin_is_mine.begin(), wtx.txin_is_mine.end(), [](bool mine) { return mine; });
|
||||
bool all_to_me = std::all_of(wtx.txout_is_mine.begin(), wtx.txout_is_mine.end(), [](bool mine) { return mine; });
|
||||
|
||||
isminetype fAllToMe = ISMINE_SPENDABLE;
|
||||
for (const isminetype mine : wtx.txout_is_mine)
|
||||
{
|
||||
if(fAllToMe > mine) fAllToMe = mine;
|
||||
}
|
||||
|
||||
if (fAllFromMe)
|
||||
if (all_from_me)
|
||||
{
|
||||
// Debit
|
||||
//
|
||||
@ -223,8 +208,8 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
{
|
||||
// Ignore change
|
||||
isminetype toSelf = *(mine++);
|
||||
if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
|
||||
bool toSelf = *(mine++);
|
||||
if (toSelf && all_from_me)
|
||||
continue;
|
||||
|
||||
if (!wtx.value_map.count("to") || wtx.value_map["to"].empty())
|
||||
@ -236,10 +221,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
strHTML += "<b>" + tr("To") + ":</b> ";
|
||||
std::string name;
|
||||
if (wallet.getAddress(
|
||||
address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
|
||||
address, &name, /*purpose=*/nullptr) && !name.empty())
|
||||
strHTML += GUIUtil::HtmlEscape(name) + " ";
|
||||
strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
|
||||
if(toSelf == ISMINE_SPENDABLE)
|
||||
if(toSelf)
|
||||
strHTML += " (" + tr("own address") + ")";
|
||||
strHTML += "<br>";
|
||||
}
|
||||
@ -250,7 +235,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
|
||||
}
|
||||
|
||||
if (fAllToMe)
|
||||
if (all_to_me)
|
||||
{
|
||||
// Payment to self
|
||||
CAmount nChange = wtx.change;
|
||||
@ -271,13 +256,13 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
auto mine = wtx.txin_is_mine.begin();
|
||||
for (const CTxIn& txin : wtx.tx->vin) {
|
||||
if (*(mine++)) {
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin)) + "<br>";
|
||||
}
|
||||
}
|
||||
mine = wtx.txout_is_mine.begin();
|
||||
for (const CTxOut& txout : wtx.tx->vout) {
|
||||
if (*(mine++)) {
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout)) + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,10 +319,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
|
||||
for (const CTxIn& txin : wtx.tx->vin)
|
||||
if(wallet.txinIsMine(txin))
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin)) + "<br>";
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
if(wallet.txoutIsMine(txout))
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout)) + "<br>";
|
||||
|
||||
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
|
||||
strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
|
||||
@ -357,12 +342,12 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
||||
if (ExtractDestination(vout.scriptPubKey, address))
|
||||
{
|
||||
std::string name;
|
||||
if (wallet.getAddress(address, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr) && !name.empty())
|
||||
if (wallet.getAddress(address, &name, /*purpose=*/nullptr) && !name.empty())
|
||||
strHTML += GUIUtil::HtmlEscape(name) + " ";
|
||||
strHTML += QString::fromStdString(EncodeDestination(address));
|
||||
}
|
||||
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
|
||||
strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
|
||||
strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) ? tr("true") : tr("false")) + "</li>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,16 +7,11 @@
|
||||
#include <chain.h>
|
||||
#include <interfaces/wallet.h>
|
||||
#include <key_io.h>
|
||||
#include <wallet/types.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
using wallet::ISMINE_NO;
|
||||
using wallet::ISMINE_SPENDABLE;
|
||||
using wallet::isminetype;
|
||||
|
||||
/* Return positive answer if transaction should be shown in list.
|
||||
*/
|
||||
bool TransactionRecord::showTransaction()
|
||||
@ -39,26 +34,26 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||
Txid hash = wtx.tx->GetHash();
|
||||
std::map<std::string, std::string> mapValue = wtx.value_map;
|
||||
|
||||
isminetype fAllFromMe = ISMINE_SPENDABLE;
|
||||
bool all_from_me = true;
|
||||
bool any_from_me = false;
|
||||
if (wtx.is_coinbase) {
|
||||
fAllFromMe = ISMINE_NO;
|
||||
all_from_me = false;
|
||||
} else {
|
||||
for (const isminetype mine : wtx.txin_is_mine)
|
||||
for (const bool mine : wtx.txin_is_mine)
|
||||
{
|
||||
if(fAllFromMe > mine) fAllFromMe = mine;
|
||||
all_from_me = all_from_me && mine;
|
||||
if (mine) any_from_me = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fAllFromMe || !any_from_me) {
|
||||
if (all_from_me || !any_from_me) {
|
||||
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
|
||||
|
||||
for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||
{
|
||||
const CTxOut& txout = wtx.tx->vout[i];
|
||||
|
||||
if (fAllFromMe) {
|
||||
if (all_from_me) {
|
||||
// Change is only really possible if we're the sender
|
||||
// Otherwise, someone just sent bitcoins to a change address, which should be shown
|
||||
if (wtx.txout_is_change[i]) {
|
||||
@ -97,7 +92,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
||||
parts.append(sub);
|
||||
}
|
||||
|
||||
isminetype mine = wtx.txout_is_mine[i];
|
||||
bool mine = wtx.txout_is_mine[i];
|
||||
if(mine)
|
||||
{
|
||||
//
|
||||
|
||||
@ -268,7 +268,7 @@ void WalletModel::sendCoins(WalletModelTransaction& transaction)
|
||||
// Check if we have a new address or an updated label
|
||||
std::string name;
|
||||
if (!m_wallet->getAddress(
|
||||
dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr))
|
||||
dest, &name, /*purpose=*/nullptr))
|
||||
{
|
||||
m_wallet->setAddressBook(dest, strLabel, wallet::AddressPurpose::SEND);
|
||||
}
|
||||
|
||||
@ -50,9 +50,6 @@ public:
|
||||
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
||||
int input_bytes;
|
||||
|
||||
/** Whether we have the private keys to spend this output */
|
||||
bool spendable;
|
||||
|
||||
/** Whether we know how to spend this output, ignoring the lack of keys */
|
||||
bool solvable;
|
||||
|
||||
@ -75,12 +72,11 @@ public:
|
||||
/** The fee necessary to bump this UTXO's ancestor transactions to the target feerate */
|
||||
CAmount ancestor_bump_fees{0};
|
||||
|
||||
COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
|
||||
COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool solvable, bool safe, int64_t time, bool from_me, const std::optional<CFeeRate> feerate = std::nullopt)
|
||||
: outpoint{outpoint},
|
||||
txout{txout},
|
||||
depth{depth},
|
||||
input_bytes{input_bytes},
|
||||
spendable{spendable},
|
||||
solvable{solvable},
|
||||
safe{safe},
|
||||
time{time},
|
||||
@ -93,8 +89,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool spendable, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
|
||||
: COutput(outpoint, txout, depth, input_bytes, spendable, solvable, safe, time, from_me)
|
||||
COutput(const COutPoint& outpoint, const CTxOut& txout, int depth, int input_bytes, bool solvable, bool safe, int64_t time, bool from_me, const CAmount fees)
|
||||
: COutput(outpoint, txout, depth, input_bytes, solvable, safe, time, from_me)
|
||||
{
|
||||
// if input_bytes is unknown, then fees should be 0, if input_bytes is known, then the fees should be a positive integer or 0 (input_bytes known and fees = 0 only happens in the tests)
|
||||
assert((input_bytes < 0 && fees == 0) || (input_bytes > 0 && fees >= 0));
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include <common/system.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <node/types.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <util/moneystr.h>
|
||||
@ -48,8 +47,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet
|
||||
if (require_mine) {
|
||||
// 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)
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
if (!AllInputsMine(wallet, *wtx.tx, filter)) {
|
||||
if (!AllInputsMine(wallet, *wtx.tx)) {
|
||||
errors.emplace_back(Untranslated("Transaction contains inputs that don't belong to this wallet"));
|
||||
return feebumper::Result::WALLET_ERROR;
|
||||
}
|
||||
|
||||
@ -23,7 +23,6 @@
|
||||
#include <wallet/context.h>
|
||||
#include <wallet/feebumper.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/types.h>
|
||||
#include <wallet/load.h>
|
||||
#include <wallet/receive.h>
|
||||
#include <wallet/rpc/wallet.h>
|
||||
@ -74,10 +73,10 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
|
||||
result.txout_address.emplace_back();
|
||||
result.txout_address_is_mine.emplace_back(ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ?
|
||||
wallet.IsMine(result.txout_address.back()) :
|
||||
ISMINE_NO);
|
||||
false);
|
||||
}
|
||||
result.credit = CachedTxGetCredit(wallet, wtx, ISMINE_ALL);
|
||||
result.debit = CachedTxGetDebit(wallet, wtx, ISMINE_ALL);
|
||||
result.credit = CachedTxGetCredit(wallet, wtx, /*avoid_reuse=*/true);
|
||||
result.debit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/true);
|
||||
result.change = CachedTxGetChange(wallet, wtx);
|
||||
result.time = wtx.GetTxTime();
|
||||
result.value_map = wtx.mapValue;
|
||||
@ -173,7 +172,7 @@ public:
|
||||
bool isSpendable(const CTxDestination& dest) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
|
||||
return m_wallet->IsMine(dest);
|
||||
}
|
||||
bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional<AddressPurpose>& purpose) override
|
||||
{
|
||||
@ -185,7 +184,6 @@ public:
|
||||
}
|
||||
bool getAddress(const CTxDestination& dest,
|
||||
std::string* name,
|
||||
isminetype* is_mine,
|
||||
AddressPurpose* purpose) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
@ -194,16 +192,9 @@ public:
|
||||
if (name) {
|
||||
*name = entry->GetLabel();
|
||||
}
|
||||
std::optional<isminetype> dest_is_mine;
|
||||
if (is_mine || purpose) {
|
||||
dest_is_mine = m_wallet->IsMine(dest);
|
||||
}
|
||||
if (is_mine) {
|
||||
*is_mine = *dest_is_mine;
|
||||
}
|
||||
if (purpose) {
|
||||
// In very old wallets, address purpose may not be recorded so we derive it from IsMine
|
||||
*purpose = entry->purpose.value_or(*dest_is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND);
|
||||
*purpose = entry->purpose.value_or(m_wallet->IsMine(dest) ? AddressPurpose::RECEIVE : AddressPurpose::SEND);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -213,7 +204,7 @@ public:
|
||||
std::vector<WalletAddress> result;
|
||||
m_wallet->ForEachAddrBookEntry([&](const CTxDestination& dest, const std::string& label, bool is_change, const std::optional<AddressPurpose>& purpose) EXCLUSIVE_LOCKS_REQUIRED(m_wallet->cs_wallet) {
|
||||
if (is_change) return;
|
||||
isminetype is_mine = m_wallet->IsMine(dest);
|
||||
bool is_mine = m_wallet->IsMine(dest);
|
||||
// In very old wallets, address purpose may not be recorded so we derive it from IsMine
|
||||
result.emplace_back(dest, is_mine, purpose.value_or(is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND), label);
|
||||
});
|
||||
@ -431,25 +422,25 @@ public:
|
||||
|
||||
return total_amount;
|
||||
}
|
||||
isminetype txinIsMine(const CTxIn& txin) override
|
||||
bool txinIsMine(const CTxIn& txin) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return InputIsMine(*m_wallet, txin);
|
||||
}
|
||||
isminetype txoutIsMine(const CTxOut& txout) override
|
||||
bool txoutIsMine(const CTxOut& txout) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->IsMine(txout);
|
||||
}
|
||||
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
|
||||
CAmount getDebit(const CTxIn& txin) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return m_wallet->GetDebit(txin, filter);
|
||||
return m_wallet->GetDebit(txin);
|
||||
}
|
||||
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
|
||||
CAmount getCredit(const CTxOut& txout) override
|
||||
{
|
||||
LOCK(m_wallet->cs_wallet);
|
||||
return OutputGetCredit(*m_wallet, txout, filter);
|
||||
return OutputGetCredit(*m_wallet, txout);
|
||||
}
|
||||
CoinsList listCoins() override
|
||||
{
|
||||
|
||||
@ -10,39 +10,39 @@
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
namespace wallet {
|
||||
isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin)
|
||||
bool InputIsMine(const CWallet& wallet, const CTxIn& txin)
|
||||
{
|
||||
AssertLockHeld(wallet.cs_wallet);
|
||||
const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);
|
||||
if (prev && txin.prevout.n < prev->tx->vout.size()) {
|
||||
return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
|
||||
}
|
||||
return ISMINE_NO;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
|
||||
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx)
|
||||
{
|
||||
LOCK(wallet.cs_wallet);
|
||||
for (const CTxIn& txin : tx.vin) {
|
||||
if (!(InputIsMine(wallet, txin) & filter)) return false;
|
||||
if (!InputIsMine(wallet, txin)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter)
|
||||
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout)
|
||||
{
|
||||
if (!MoneyRange(txout.nValue))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
LOCK(wallet.cs_wallet);
|
||||
return ((wallet.IsMine(txout) & filter) ? txout.nValue : 0);
|
||||
return (wallet.IsMine(txout) ? txout.nValue : 0);
|
||||
}
|
||||
|
||||
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
|
||||
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx)
|
||||
{
|
||||
CAmount nCredit = 0;
|
||||
for (const CTxOut& txout : tx.vout)
|
||||
{
|
||||
nCredit += OutputGetCredit(wallet, txout, filter);
|
||||
nCredit += OutputGetCredit(wallet, txout);
|
||||
if (!MoneyRange(nCredit))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
}
|
||||
@ -97,17 +97,17 @@ CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
|
||||
return nChange;
|
||||
}
|
||||
|
||||
static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter)
|
||||
static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, bool avoid_reuse)
|
||||
{
|
||||
auto& amount = wtx.m_amounts[type];
|
||||
if (!amount.m_cached[filter]) {
|
||||
amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter));
|
||||
if (!amount.IsCached(avoid_reuse)) {
|
||||
amount.Set(avoid_reuse, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx) : TxGetCredit(wallet, *wtx.tx));
|
||||
wtx.m_is_cache_empty = false;
|
||||
}
|
||||
return amount.m_value[filter];
|
||||
return amount.Get(avoid_reuse);
|
||||
}
|
||||
|
||||
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
|
||||
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
|
||||
{
|
||||
AssertLockHeld(wallet.cs_wallet);
|
||||
|
||||
@ -115,26 +115,16 @@ CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const ism
|
||||
if (wallet.IsTxImmatureCoinBase(wtx))
|
||||
return 0;
|
||||
|
||||
CAmount credit = 0;
|
||||
const isminefilter get_amount_filter{filter & ISMINE_ALL};
|
||||
if (get_amount_filter) {
|
||||
// GetBalance can assume transactions in mapWallet won't change
|
||||
credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, get_amount_filter);
|
||||
}
|
||||
return credit;
|
||||
// GetBalance can assume transactions in mapWallet won't change
|
||||
return GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, avoid_reuse);
|
||||
}
|
||||
|
||||
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
|
||||
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
|
||||
{
|
||||
if (wtx.tx->vin.empty())
|
||||
return 0;
|
||||
|
||||
CAmount debit = 0;
|
||||
const isminefilter get_amount_filter{filter & ISMINE_ALL};
|
||||
if (get_amount_filter) {
|
||||
debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, get_amount_filter);
|
||||
}
|
||||
return debit;
|
||||
return GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, avoid_reuse);
|
||||
}
|
||||
|
||||
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
|
||||
@ -148,7 +138,7 @@ CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
|
||||
|
||||
void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
std::list<COutputEntry>& listReceived,
|
||||
std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter,
|
||||
std::list<COutputEntry>& listSent, CAmount& nFee,
|
||||
bool include_change)
|
||||
{
|
||||
nFee = 0;
|
||||
@ -156,7 +146,7 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
listSent.clear();
|
||||
|
||||
// Compute fee:
|
||||
CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter);
|
||||
CAmount nDebit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false);
|
||||
if (nDebit > 0) // debit>0 means we signed/sent this transaction
|
||||
{
|
||||
CAmount nValueOut = wtx.tx->GetValueOut();
|
||||
@ -168,7 +158,7 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
|
||||
{
|
||||
const CTxOut& txout = wtx.tx->vout[i];
|
||||
isminetype fIsMine = wallet.IsMine(txout);
|
||||
bool ismine = wallet.IsMine(txout);
|
||||
// Only need to handle txouts if AT LEAST one of these is true:
|
||||
// 1) they debit from us (sent)
|
||||
// 2) the output is to us (received)
|
||||
@ -177,7 +167,7 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
if (!include_change && OutputIsChange(wallet, txout))
|
||||
continue;
|
||||
}
|
||||
else if (!(fIsMine & filter))
|
||||
else if (!ismine)
|
||||
continue;
|
||||
|
||||
// In either case, we need to get the destination address
|
||||
@ -197,15 +187,15 @@ void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
listSent.push_back(output);
|
||||
|
||||
// If we are receiving the output, add it as a "received" entry
|
||||
if (fIsMine & filter)
|
||||
if (ismine)
|
||||
listReceived.push_back(output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
|
||||
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx)
|
||||
{
|
||||
return (CachedTxGetDebit(wallet, wtx, filter) > 0);
|
||||
return (CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false) > 0);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(misc-no-recursion)
|
||||
@ -219,7 +209,7 @@ bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txi
|
||||
if (wtx.isConfirmed()) return true;
|
||||
if (wtx.isBlockConflicted()) return false;
|
||||
// using wtx's cached debit
|
||||
if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx, ISMINE_ALL)) return false;
|
||||
if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx)) return false;
|
||||
|
||||
// Don't trust unconfirmed transactions from us unless they are in the mempool.
|
||||
if (!wtx.InMempool()) return false;
|
||||
@ -232,7 +222,7 @@ bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txi
|
||||
if (parent == nullptr) return false;
|
||||
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
|
||||
// Check that this specific input being spent is trusted
|
||||
if (wallet.IsMine(parentOut) != ISMINE_SPENDABLE) return false;
|
||||
if (!wallet.IsMine(parentOut)) return false;
|
||||
// If we've already trusted this parent, continue
|
||||
if (trusted_parents.count(parent->GetHash())) continue;
|
||||
// Recurse to check that the parent is also trusted
|
||||
@ -264,13 +254,7 @@ Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
|
||||
|
||||
if (!wallet.IsSpent(outpoint) && (allow_used_addresses || !wallet.IsSpentKey(txo.GetTxOut().scriptPubKey))) {
|
||||
// Get the amounts for mine
|
||||
CAmount credit_mine = 0;
|
||||
if (txo.GetIsMine() == ISMINE_SPENDABLE) {
|
||||
credit_mine = txo.GetTxOut().nValue;
|
||||
} else {
|
||||
// We shouldn't see any other isminetypes
|
||||
Assume(false);
|
||||
}
|
||||
CAmount credit_mine = txo.GetTxOut().nValue;
|
||||
|
||||
// Set the amounts in the return object
|
||||
if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
|
||||
@ -300,7 +284,7 @@ std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
|
||||
if (wallet.IsTxImmatureCoinBase(wtx)) continue;
|
||||
|
||||
int nDepth = wallet.GetTxDepthInMainChain(wtx);
|
||||
if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1)) continue;
|
||||
if (nDepth < (CachedTxIsFromMe(wallet, wtx) ? 0 : 1)) continue;
|
||||
|
||||
CTxDestination addr;
|
||||
Assume(wallet.IsMine(txo.GetTxOut()));
|
||||
|
||||
@ -8,27 +8,25 @@
|
||||
#include <consensus/amount.h>
|
||||
#include <primitives/transaction_identifier.h>
|
||||
#include <wallet/transaction.h>
|
||||
#include <wallet/types.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
namespace wallet {
|
||||
isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
bool 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);
|
||||
/** Returns whether all of the inputs belong to the wallet*/
|
||||
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx);
|
||||
|
||||
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter);
|
||||
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter);
|
||||
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout);
|
||||
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx);
|
||||
|
||||
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)
|
||||
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
//! filter decides which addresses will count towards the debit
|
||||
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
|
||||
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse);
|
||||
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx);
|
||||
struct COutputEntry
|
||||
{
|
||||
@ -39,9 +37,9 @@ struct COutputEntry
|
||||
void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
|
||||
std::list<COutputEntry>& listReceived,
|
||||
std::list<COutputEntry>& listSent,
|
||||
CAmount& nFee, const isminefilter& filter,
|
||||
CAmount& nFee,
|
||||
bool include_change);
|
||||
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter);
|
||||
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx);
|
||||
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txid>& trusted_parents) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx);
|
||||
|
||||
|
||||
@ -448,8 +448,8 @@ RPCHelpMan getaddressinfo()
|
||||
|
||||
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
|
||||
|
||||
isminetype mine = pwallet->IsMine(dest);
|
||||
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
|
||||
bool mine = pwallet->IsMine(dest);
|
||||
ret.pushKV("ismine", mine);
|
||||
|
||||
if (provider) {
|
||||
auto inferred = InferDescriptor(scriptPubKey, *provider);
|
||||
|
||||
@ -498,7 +498,7 @@ RPCHelpMan listunspent()
|
||||
{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
|
||||
{RPCResult::Type::STR_HEX, "redeemScript", /*optional=*/true, "The redeem script if the output script is P2SH"},
|
||||
{RPCResult::Type::STR, "witnessScript", /*optional=*/true, "witness script if the output script is P2WSH or P2SH-P2WSH"},
|
||||
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
|
||||
{RPCResult::Type::BOOL, "spendable", "(DEPRECATED) Always true"},
|
||||
{RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
|
||||
{RPCResult::Type::BOOL, "reused", /*optional=*/true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
|
||||
{RPCResult::Type::STR, "desc", /*optional=*/true, "(only when solvable) A descriptor for spending this output"},
|
||||
@ -598,8 +598,9 @@ RPCHelpMan listunspent()
|
||||
cctl.m_min_depth = nMinDepth;
|
||||
cctl.m_max_depth = nMaxDepth;
|
||||
cctl.m_include_unsafe_inputs = include_unsafe;
|
||||
filter_coins.check_version_trucness = false;
|
||||
LOCK(pwallet->cs_wallet);
|
||||
vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, filter_coins).All();
|
||||
vecOutputs = AvailableCoins(*pwallet, &cctl, /*feerate=*/std::nullopt, filter_coins).All();
|
||||
}
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
@ -672,7 +673,7 @@ RPCHelpMan listunspent()
|
||||
entry.pushKV("ancestorfees", uint64_t(ancestor_fees));
|
||||
}
|
||||
}
|
||||
entry.pushKV("spendable", out.spendable);
|
||||
entry.pushKV("spendable", true); // Any coins we list are always spendable
|
||||
entry.pushKV("solvable", out.solvable);
|
||||
if (out.solvable) {
|
||||
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
|
||||
|
||||
@ -1505,7 +1505,7 @@ RPCHelpMan sendall()
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
|
||||
}
|
||||
const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
|
||||
if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & ISMINE_SPENDABLE)) {
|
||||
if (!tx || input.prevout.n >= tx->tx->vout.size() || !pwallet->IsMine(tx->tx->vout[input.prevout.n])) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
|
||||
}
|
||||
if (pwallet->GetTxDepthInMainChain(*tx) == 0) {
|
||||
|
||||
@ -83,8 +83,6 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
||||
if (!params[1].isNull())
|
||||
fIncludeEmpty = params[1].get_bool();
|
||||
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
std::optional<CTxDestination> filtered_address{std::nullopt};
|
||||
if (!by_label && !params[3].isNull() && !params[3].get_str().empty()) {
|
||||
if (!IsValidDestinationString(params[3].get_str())) {
|
||||
@ -116,8 +114,7 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
||||
continue;
|
||||
}
|
||||
|
||||
isminefilter mine = wallet.IsMine(address);
|
||||
if (!(mine & filter))
|
||||
if (!wallet.IsMine(address))
|
||||
continue;
|
||||
|
||||
tallyitem& item = mapTally[address];
|
||||
@ -302,12 +299,11 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
|
||||
* @param nMinDepth The minimum confirmation depth.
|
||||
* @param fLong Whether to include the JSON version of the transaction.
|
||||
* @param ret The vector into which the result is stored.
|
||||
* @param filter_ismine The "is mine" filter flags.
|
||||
* @param filter_label Optional label string to filter incoming transactions.
|
||||
*/
|
||||
template <class Vec>
|
||||
static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong,
|
||||
Vec& ret, const isminefilter& filter_ismine, const std::optional<std::string>& filter_label,
|
||||
Vec& ret, const std::optional<std::string>& filter_label,
|
||||
bool include_change = false)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
|
||||
{
|
||||
@ -315,7 +311,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
||||
std::list<COutputEntry> listReceived;
|
||||
std::list<COutputEntry> listSent;
|
||||
|
||||
CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine, include_change);
|
||||
CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, include_change);
|
||||
|
||||
// Sent
|
||||
if (!filter_label.has_value())
|
||||
@ -484,7 +480,6 @@ RPCHelpMan listtransactions()
|
||||
int nFrom = 0;
|
||||
if (!request.params[2].isNull())
|
||||
nFrom = request.params[2].getInt<int>();
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
if (nCount < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
|
||||
@ -501,7 +496,7 @@ RPCHelpMan listtransactions()
|
||||
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||
{
|
||||
CWalletTx *const pwtx = (*it).second;
|
||||
ListTransactions(*pwallet, *pwtx, 0, true, ret, filter, filter_label);
|
||||
ListTransactions(*pwallet, *pwtx, 0, true, ret, filter_label);
|
||||
if ((int)ret.size() >= (nCount+nFrom)) break;
|
||||
}
|
||||
}
|
||||
@ -589,7 +584,6 @@ RPCHelpMan listsinceblock()
|
||||
std::optional<int> height; // Height of the specified block or the common ancestor, if the block provided was in a deactivated chain.
|
||||
std::optional<int> altheight; // Height of the specified block, even if it's in a deactivated chain.
|
||||
int target_confirms = 1;
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
uint256 blockId;
|
||||
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
|
||||
@ -623,7 +617,7 @@ RPCHelpMan listsinceblock()
|
||||
for (const auto& [_, tx] : wallet.mapWallet) {
|
||||
|
||||
if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
|
||||
ListTransactions(wallet, tx, 0, true, transactions, filter, filter_label, include_change);
|
||||
ListTransactions(wallet, tx, 0, true, transactions, filter_label, include_change);
|
||||
}
|
||||
}
|
||||
|
||||
@ -640,7 +634,7 @@ RPCHelpMan listsinceblock()
|
||||
if (it != wallet.mapWallet.end()) {
|
||||
// We want all transactions regardless of confirmation count to appear here,
|
||||
// even negative confirmation ones, hence the big negative.
|
||||
ListTransactions(wallet, it->second, -100000000, true, removed, filter, filter_label, include_change);
|
||||
ListTransactions(wallet, it->second, -100000000, true, removed, filter_label, include_change);
|
||||
}
|
||||
}
|
||||
blockId = block.hashPrevBlock;
|
||||
@ -730,8 +724,6 @@ RPCHelpMan gettransaction()
|
||||
|
||||
Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
|
||||
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
@ -741,19 +733,19 @@ RPCHelpMan gettransaction()
|
||||
}
|
||||
const CWalletTx& wtx = it->second;
|
||||
|
||||
CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, filter);
|
||||
CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, filter);
|
||||
CAmount nCredit = CachedTxGetCredit(*pwallet, wtx, /*avoid_reuse=*/false);
|
||||
CAmount nDebit = CachedTxGetDebit(*pwallet, wtx, /*avoid_reuse=*/false);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
CAmount nFee = (CachedTxIsFromMe(*pwallet, wtx, filter) ? wtx.tx->GetValueOut() - nDebit : 0);
|
||||
CAmount nFee = (CachedTxIsFromMe(*pwallet, wtx) ? wtx.tx->GetValueOut() - nDebit : 0);
|
||||
|
||||
entry.pushKV("amount", ValueFromAmount(nNet - nFee));
|
||||
if (CachedTxIsFromMe(*pwallet, wtx, filter))
|
||||
if (CachedTxIsFromMe(*pwallet, wtx))
|
||||
entry.pushKV("fee", ValueFromAmount(nFee));
|
||||
|
||||
WalletTxToJSON(*pwallet, wtx, entry);
|
||||
|
||||
UniValue details(UniValue::VARR);
|
||||
ListTransactions(*pwallet, wtx, 0, false, details, filter, /*filter_label=*/std::nullopt);
|
||||
ListTransactions(*pwallet, wtx, 0, false, details, /*filter_label=*/std::nullopt);
|
||||
entry.pushKV("details", std::move(details));
|
||||
|
||||
entry.pushKV("hex", EncodeHexTx(*wtx.tx));
|
||||
|
||||
@ -525,8 +525,6 @@ RPCHelpMan simulaterawtransaction()
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
const auto& txs = request.params[0].get_array();
|
||||
CAmount changes{0};
|
||||
std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
|
||||
@ -559,7 +557,7 @@ RPCHelpMan simulaterawtransaction()
|
||||
if (coins.at(outpoint).IsSpent()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
|
||||
}
|
||||
changes -= wallet.GetDebit(txin, filter);
|
||||
changes -= wallet.GetDebit(txin);
|
||||
}
|
||||
spent.insert(outpoint);
|
||||
}
|
||||
@ -572,7 +570,7 @@ RPCHelpMan simulaterawtransaction()
|
||||
const auto& hash = mtx.GetHash();
|
||||
for (size_t i = 0; i < mtx.vout.size(); ++i) {
|
||||
const auto& txout = mtx.vout[i];
|
||||
bool is_mine = 0 < (wallet.IsMine(txout) & filter);
|
||||
bool is_mine = wallet.IsMine(txout);
|
||||
changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,15 +197,15 @@ IsMineResult LegacyWalletIsMineInnerDONOTUSE(const LegacyDataSPKM& keystore, con
|
||||
|
||||
} // namespace
|
||||
|
||||
isminetype LegacyDataSPKM::IsMine(const CScript& script) const
|
||||
bool LegacyDataSPKM::IsMine(const CScript& script) const
|
||||
{
|
||||
switch (LegacyWalletIsMineInnerDONOTUSE(*this, script, IsMineSigVersion::TOP)) {
|
||||
case IsMineResult::INVALID:
|
||||
case IsMineResult::NO:
|
||||
return ISMINE_NO;
|
||||
return false;
|
||||
case IsMineResult::WATCH_ONLY:
|
||||
case IsMineResult::SPENDABLE:
|
||||
return ISMINE_SPENDABLE;
|
||||
return true;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
@ -506,12 +506,12 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetCandidateScriptP
|
||||
|
||||
std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys() const
|
||||
{
|
||||
// Run IsMine() on each candidate output script. Any script that is not ISMINE_NO is an output
|
||||
// Run IsMine() on each candidate output script. Any script that IsMine is an output
|
||||
// script to return.
|
||||
// This both filters out things that are not watched by the wallet, and things that are invalid.
|
||||
std::unordered_set<CScript, SaltedSipHasher> spks;
|
||||
for (const CScript& script : GetCandidateScriptPubKeys()) {
|
||||
if (IsMine(script) != ISMINE_NO) {
|
||||
if (IsMine(script)) {
|
||||
spks.insert(script);
|
||||
}
|
||||
}
|
||||
@ -524,7 +524,7 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetNotMineScriptPub
|
||||
LOCK(cs_KeyStore);
|
||||
std::unordered_set<CScript, SaltedSipHasher> spks;
|
||||
for (const CScript& script : setWatchOnly) {
|
||||
if (IsMine(script) == ISMINE_NO) spks.insert(script);
|
||||
if (!IsMine(script)) spks.insert(script);
|
||||
}
|
||||
return spks;
|
||||
}
|
||||
@ -612,7 +612,7 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
|
||||
for (const CScript& spk : desc_spks) {
|
||||
size_t erased = spks.erase(spk);
|
||||
assert(erased == 1);
|
||||
assert(IsMine(spk) == ISMINE_SPENDABLE);
|
||||
assert(IsMine(spk));
|
||||
}
|
||||
|
||||
out.desc_spkms.push_back(std::move(desc_spk_man));
|
||||
@ -661,7 +661,7 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
|
||||
for (const CScript& spk : desc_spks) {
|
||||
size_t erased = spks.erase(spk);
|
||||
assert(erased == 1);
|
||||
assert(IsMine(spk) == ISMINE_SPENDABLE);
|
||||
assert(IsMine(spk));
|
||||
}
|
||||
|
||||
out.desc_spkms.push_back(std::move(desc_spk_man));
|
||||
@ -748,7 +748,7 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
|
||||
for (const CScript& desc_spk : desc_spks) {
|
||||
auto del_it = spks.find(desc_spk);
|
||||
assert(del_it != spks.end());
|
||||
assert(IsMine(desc_spk) != ISMINE_NO);
|
||||
assert(IsMine(desc_spk));
|
||||
it = spks.erase(del_it);
|
||||
}
|
||||
}
|
||||
@ -762,14 +762,14 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
|
||||
// Legacy wallets can also contain scripts whose P2SH, P2WSH, or P2SH-P2WSH it is not watching for
|
||||
// but can provide script data to a PSBT spending them. These "solvable" output scripts will need to
|
||||
// be put into the separate "solvables" wallet.
|
||||
// These can be detected by going through the entire candidate output scripts, finding the ISMINE_NO scripts,
|
||||
// These can be detected by going through the entire candidate output scripts, finding the not IsMine scripts,
|
||||
// and checking CanProvide() which will dummy sign.
|
||||
for (const CScript& script : GetCandidateScriptPubKeys()) {
|
||||
// Since we only care about P2SH, P2WSH, and P2SH-P2WSH, filter out any scripts that are not those
|
||||
if (!script.IsPayToScriptHash() && !script.IsPayToWitnessScriptHash()) {
|
||||
continue;
|
||||
}
|
||||
if (IsMine(script) != ISMINE_NO) {
|
||||
if (IsMine(script)) {
|
||||
continue;
|
||||
}
|
||||
SignatureData dummy_sigdata;
|
||||
@ -861,13 +861,10 @@ util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetNewDestination(const
|
||||
}
|
||||
}
|
||||
|
||||
isminetype DescriptorScriptPubKeyMan::IsMine(const CScript& script) const
|
||||
bool DescriptorScriptPubKeyMan::IsMine(const CScript& script) const
|
||||
{
|
||||
LOCK(cs_desc_man);
|
||||
if (m_map_script_pub_keys.count(script) > 0) {
|
||||
return ISMINE_SPENDABLE;
|
||||
}
|
||||
return ISMINE_NO;
|
||||
return m_map_script_pub_keys.contains(script);
|
||||
}
|
||||
|
||||
bool DescriptorScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key)
|
||||
|
||||
@ -85,7 +85,7 @@ public:
|
||||
explicit ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {}
|
||||
virtual ~ScriptPubKeyMan() = default;
|
||||
virtual util::Result<CTxDestination> GetNewDestination(const OutputType type) { return util::Error{Untranslated("Not supported")}; }
|
||||
virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; }
|
||||
virtual bool IsMine(const CScript& script) const { return false; }
|
||||
|
||||
//! Check that the given decryption key is valid for this ScriptPubKeyMan, i.e. it decrypts all of the keys handled by it.
|
||||
virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key) { return false; }
|
||||
@ -197,7 +197,7 @@ private:
|
||||
// Used only in migration.
|
||||
std::unordered_set<CScript, SaltedSipHasher> GetCandidateScriptPubKeys() const;
|
||||
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
bool IsMine(const CScript& script) const override;
|
||||
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
|
||||
public:
|
||||
using ScriptPubKeyMan::ScriptPubKeyMan;
|
||||
@ -324,7 +324,7 @@ public:
|
||||
mutable RecursiveMutex cs_desc_man;
|
||||
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type) override;
|
||||
isminetype IsMine(const CScript& script) const override;
|
||||
bool IsMine(const CScript& script) const override;
|
||||
|
||||
bool CheckDecryptionKey(const CKeyingMaterial& master_key) override;
|
||||
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
|
||||
|
||||
@ -309,8 +309,8 @@ util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const
|
||||
return util::Error{strprintf(_("Not solvable pre-selected input %s"), outpoint.ToString())}; // Not solvable, can't estimate size for fee
|
||||
}
|
||||
|
||||
/* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
|
||||
COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, coin_selection_params.m_effective_feerate);
|
||||
/* Set some defaults for depth, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
|
||||
COutput output(outpoint, txout, /*depth=*/0, input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, coin_selection_params.m_effective_feerate);
|
||||
output.ApplyBumpFee(map_of_bump_fees.at(output.outpoint));
|
||||
result.Insert(output, coin_selection_params.m_subtract_fee_outputs);
|
||||
}
|
||||
@ -437,14 +437,11 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||
if (wallet.IsSpent(outpoint))
|
||||
continue;
|
||||
|
||||
isminetype mine = wallet.IsMine(output);
|
||||
assert(mine != ISMINE_NO);
|
||||
|
||||
if (!allow_used_addresses && wallet.IsSpentKey(output.scriptPubKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool tx_from_me = CachedTxIsFromMe(wallet, wtx, ISMINE_ALL);
|
||||
bool tx_from_me = CachedTxIsFromMe(wallet, wtx);
|
||||
|
||||
std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(output.scriptPubKey);
|
||||
|
||||
@ -452,10 +449,6 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||
// Because CalculateMaximumSignedInputSize infers a solvable descriptor to get the satisfaction size,
|
||||
// it is safe to assume that this input is solvable if input_bytes is greater than -1.
|
||||
bool solvable = input_bytes > -1;
|
||||
bool spendable = (mine & ISMINE_SPENDABLE) != ISMINE_NO;
|
||||
|
||||
// Filter by spendable outputs only
|
||||
if (!spendable && params.only_spendable) continue;
|
||||
|
||||
// Obtain script type
|
||||
std::vector<std::vector<uint8_t>> script_solutions;
|
||||
@ -474,7 +467,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||
}
|
||||
|
||||
auto available_output_type = GetOutputType(type, is_from_p2sh);
|
||||
auto available_output = COutput(outpoint, output, nDepth, input_bytes, spendable, solvable, tx_safe, wtx.GetTxTime(), tx_from_me, feerate);
|
||||
auto available_output = COutput(outpoint, output, nDepth, input_bytes, solvable, tx_safe, wtx.GetTxTime(), tx_from_me, feerate);
|
||||
if (wtx.tx->version == TRUC_VERSION && nDepth == 0 && params.check_version_trucness) {
|
||||
unconfirmed_truc_coins.emplace_back(available_output_type, available_output);
|
||||
auto [it, _] = truc_txid_by_value.try_emplace(wtx.tx->GetHash(), 0);
|
||||
@ -528,13 +521,6 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||
return result;
|
||||
}
|
||||
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl, CoinFilterParams params)
|
||||
{
|
||||
params.only_spendable = false;
|
||||
params.check_version_trucness = false;
|
||||
return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, params);
|
||||
}
|
||||
|
||||
const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint)
|
||||
{
|
||||
AssertLockHeld(wallet.cs_wallet);
|
||||
@ -563,21 +549,18 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
|
||||
|
||||
CCoinControl coin_control;
|
||||
CoinFilterParams coins_params;
|
||||
coins_params.only_spendable = false;
|
||||
coins_params.skip_locked = false;
|
||||
for (const COutput& coin : AvailableCoins(wallet, &coin_control, /*feerate=*/std::nullopt, coins_params).All()) {
|
||||
CTxDestination address;
|
||||
if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable))) {
|
||||
if (!ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) {
|
||||
// For backwards compatibility, we convert P2PK output scripts into PKHash destinations
|
||||
if (auto pk_dest = std::get_if<PubKeyDestination>(&address)) {
|
||||
address = PKHash(pk_dest->GetPubKey());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (!ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) {
|
||||
// For backwards compatibility, we convert P2PK output scripts into PKHash destinations
|
||||
if (auto pk_dest = std::get_if<PubKeyDestination>(&address)) {
|
||||
address = PKHash(pk_dest->GetPubKey());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
result[address].emplace_back(coin);
|
||||
}
|
||||
result[address].emplace_back(coin);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -77,8 +77,6 @@ struct CoinFilterParams {
|
||||
CAmount min_sum_amount{MAX_MONEY};
|
||||
// Maximum number of outputs that can be returned
|
||||
uint64_t max_count{0};
|
||||
// By default, return only spendable outputs
|
||||
bool only_spendable{true};
|
||||
// By default, do not include immature coinbase outputs
|
||||
bool include_immature_coinbase{false};
|
||||
// By default, skip locked UTXOs
|
||||
@ -96,12 +94,6 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
||||
std::optional<CFeeRate> feerate = std::nullopt,
|
||||
const CoinFilterParams& params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
/**
|
||||
* Wrapper function for AvailableCoins which skips the `feerate` and `CoinFilterParams::only_spendable` parameters. Use this function
|
||||
* to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
|
||||
*/
|
||||
CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, CoinFilterParams params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
|
||||
|
||||
/**
|
||||
* Find non-change parent output.
|
||||
*/
|
||||
|
||||
@ -53,7 +53,7 @@ static OutputGroup MakeCoin(const CAmount& amount, bool is_eff_value = true, Coi
|
||||
tx.vout[0].nValue = amount + int(is_eff_value) * fees;
|
||||
tx.nLockTime = next_lock_time++; // so all transactions get different hashes
|
||||
OutputGroup group(cs_params);
|
||||
group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), 0), tx.vout.at(0), /*depth=*/1, /*input_bytes=*/custom_spending_vsize, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/fees), /*ancestors=*/0, /*descendants=*/0);
|
||||
group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), 0), tx.vout.at(0), /*depth=*/1, /*input_bytes=*/custom_spending_vsize, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/fees), /*ancestors=*/0, /*descendants=*/0);
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
|
||||
tx.vout.resize(nInput + 1);
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
|
||||
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, /*input_bytes=*/-1, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/ 0);
|
||||
OutputGroup group;
|
||||
group.Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*descendants=*/ 0);
|
||||
result.AddInput(group);
|
||||
@ -55,7 +55,7 @@ static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result,
|
||||
tx.vout.resize(nInput + 1);
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||
std::shared_ptr<COutput> coin = std::make_shared<COutput>(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ 148, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fee);
|
||||
std::shared_ptr<COutput> coin = std::make_shared<COutput>(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, /*input_bytes=*/148, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, fee);
|
||||
OutputGroup group;
|
||||
group.Insert(coin, /*ancestors=*/ 0, /*descendants=*/ 0);
|
||||
coin->long_term_fee = long_term_fee; // group.Insert() will modify long_term_fee, so we need to set it afterwards
|
||||
@ -78,7 +78,7 @@ static void add_coin(CoinsResult& available_coins, CWallet& wallet, const CAmoun
|
||||
assert(ret.second);
|
||||
CWalletTx& wtx = (*ret.first).second;
|
||||
const auto& txout = wtx.tx->vout.at(nInput);
|
||||
available_coins.Add(OutputType::BECH32, {COutPoint(wtx.GetHash(), nInput), txout, nAge, custom_size == 0 ? CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr) : custom_size, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx.GetTxTime(), fIsFromMe, feerate});
|
||||
available_coins.Add(OutputType::BECH32, {COutPoint(wtx.GetHash(), nInput), txout, nAge, custom_size == 0 ? CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr) : custom_size, /*solvable=*/true, /*safe=*/true, wtx.GetTxTime(), fIsFromMe, feerate});
|
||||
}
|
||||
|
||||
// Helpers
|
||||
@ -911,26 +911,26 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
|
||||
// standard case, pass feerate in constructor
|
||||
COutput output1(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
|
||||
COutput output1(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, feerate);
|
||||
const CAmount expected_ev1 = 9852; // 10000 - 148
|
||||
BOOST_CHECK_EQUAL(output1.GetEffectiveValue(), expected_ev1);
|
||||
|
||||
// input bytes unknown (input_bytes = -1), pass feerate in constructor
|
||||
COutput output2(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, feerate);
|
||||
COutput output2(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, /*input_bytes=*/-1, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/ false, feerate);
|
||||
BOOST_CHECK_EQUAL(output2.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
|
||||
|
||||
// negative effective value, pass feerate in constructor
|
||||
COutput output3(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, CFeeRate(100000));
|
||||
COutput output3(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, CFeeRate(100000));
|
||||
const CAmount expected_ev3 = -4800; // 10000 - 14800
|
||||
BOOST_CHECK_EQUAL(output3.GetEffectiveValue(), expected_ev3);
|
||||
|
||||
// standard case, pass fees in constructor
|
||||
const CAmount fees = 148;
|
||||
COutput output4(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, fees);
|
||||
COutput output4(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, fees);
|
||||
BOOST_CHECK_EQUAL(output4.GetEffectiveValue(), expected_ev1);
|
||||
|
||||
// input bytes unknown (input_bytes = -1), pass fees in constructor
|
||||
COutput output5(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
|
||||
COutput output5(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/1, /*input_bytes=*/-1, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/0);
|
||||
BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ static void AddCoin(const CAmount& value, int n_input, int n_input_bytes, int lo
|
||||
tx.vout.resize(n_input + 1);
|
||||
tx.vout[n_input].nValue = value;
|
||||
tx.nLockTime = locktime; // all transactions get different hashes
|
||||
coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, fee_rate);
|
||||
coins.emplace_back(COutPoint(tx.GetHash(), n_input), tx.vout.at(n_input), /*depth=*/0, n_input_bytes, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/true, fee_rate);
|
||||
}
|
||||
|
||||
// Randomly distribute coins to instances of OutputGroup
|
||||
|
||||
@ -124,15 +124,14 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
|
||||
fuzzed_data_provider,
|
||||
[&] {
|
||||
const CScript script{ConsumeScript(fuzzed_data_provider)};
|
||||
auto is_mine{spk_manager->IsMine(script)};
|
||||
if (is_mine == isminetype::ISMINE_SPENDABLE) {
|
||||
if (spk_manager->IsMine(script)) {
|
||||
assert(spk_manager->GetScriptPubKeys().count(script));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
auto spks{spk_manager->GetScriptPubKeys()};
|
||||
for (const CScript& spk : spks) {
|
||||
assert(spk_manager->IsMine(spk) == ISMINE_SPENDABLE);
|
||||
assert(spk_manager->IsMine(spk));
|
||||
CTxDestination dest;
|
||||
bool extract_dest{ExtractDestination(spk, dest)};
|
||||
if (extract_dest) {
|
||||
|
||||
@ -51,7 +51,6 @@ static void addCoin(CoinsResult& coins,
|
||||
txout,
|
||||
depth,
|
||||
CalculateMaximumSignedInputSize(txout, &wallet, /*coin_control=*/nullptr),
|
||||
/*spendable=*/ true,
|
||||
/*solvable=*/ true,
|
||||
/*safe=*/ true,
|
||||
wtx.GetTxTime(),
|
||||
|
||||
@ -34,7 +34,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
std::unique_ptr<interfaces::Chain>& chain = m_node.chain;
|
||||
|
||||
CScript scriptPubKey;
|
||||
isminetype result;
|
||||
bool result;
|
||||
|
||||
// P2PK compressed - Descriptor
|
||||
{
|
||||
@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2PK uncompressed - Descriptor
|
||||
@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2PKH compressed - Descriptor
|
||||
@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2PKH uncompressed - Descriptor
|
||||
@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2SH - Descriptor
|
||||
@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor
|
||||
@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2WPKH uncompressed (invalid) - Descriptor
|
||||
@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2SH multisig - Descriptor
|
||||
@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]});
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2WSH multisig with compressed keys - Descriptor
|
||||
@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
CScript redeemScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]});
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// P2WSH multisig with uncompressed key (invalid) - Descriptor
|
||||
@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
// Combo - Descriptor
|
||||
@ -229,28 +229,28 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
|
||||
// Test P2PK
|
||||
result = spk_manager->IsMine(GetScriptForRawPubKey(pubkeys[0]));
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
|
||||
// Test P2PKH
|
||||
result = spk_manager->IsMine(GetScriptForDestination(PKHash(pubkeys[0])));
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
|
||||
// Test P2SH (combo descriptor does not describe P2SH)
|
||||
CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0]));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(!result);
|
||||
|
||||
// Test P2WPKH
|
||||
scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
|
||||
// P2SH-P2WPKH output
|
||||
redeemScript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
|
||||
// Test P2TR (combo descriptor does not describe P2TR)
|
||||
XOnlyPubKey xpk(pubkeys[0]);
|
||||
@ -260,7 +260,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
WitnessV1Taproot output = builder.GetOutput();
|
||||
scriptPubKey = GetScriptForDestination(output);
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_NO);
|
||||
BOOST_CHECK(!result);
|
||||
}
|
||||
|
||||
// Taproot - Descriptor
|
||||
@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
|
||||
WitnessV1Taproot output = builder.GetOutput();
|
||||
scriptPubKey = GetScriptForDestination(output);
|
||||
result = spk_manager->IsMine(scriptPubKey);
|
||||
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE);
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -424,7 +424,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
|
||||
// Lock both coins. Confirm number of available coins drops to 0.
|
||||
{
|
||||
LOCK(wallet->cs_wallet);
|
||||
BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).Size(), 2U);
|
||||
BOOST_CHECK_EQUAL(AvailableCoins(*wallet).Size(), 2U);
|
||||
}
|
||||
for (const auto& group : list) {
|
||||
for (const auto& coin : group.second) {
|
||||
@ -434,7 +434,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
|
||||
}
|
||||
{
|
||||
LOCK(wallet->cs_wallet);
|
||||
BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).Size(), 0U);
|
||||
BOOST_CHECK_EQUAL(AvailableCoins(*wallet).Size(), 0U);
|
||||
}
|
||||
// Confirm ListCoins still returns same result as before, despite coins
|
||||
// being locked.
|
||||
|
||||
@ -128,21 +128,38 @@ std::string TxStateString(const T& state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cachable amount subdivided into watchonly and spendable parts.
|
||||
* Cachable amount subdivided into avoid reuse and all balances
|
||||
*/
|
||||
struct CachableAmount
|
||||
{
|
||||
// NO and ALL are never (supposed to be) cached
|
||||
std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
|
||||
CAmount m_value[ISMINE_ENUM_ELEMENTS];
|
||||
std::optional<CAmount> m_avoid_reuse_value;
|
||||
std::optional<CAmount> m_all_value;
|
||||
inline void Reset()
|
||||
{
|
||||
m_cached.reset();
|
||||
m_avoid_reuse_value.reset();
|
||||
m_all_value.reset();
|
||||
}
|
||||
void Set(isminefilter filter, CAmount value)
|
||||
void Set(bool avoid_reuse, CAmount value)
|
||||
{
|
||||
m_cached.set(filter);
|
||||
m_value[filter] = value;
|
||||
if (avoid_reuse) {
|
||||
m_avoid_reuse_value = value;
|
||||
} else {
|
||||
m_all_value = value;
|
||||
}
|
||||
}
|
||||
CAmount Get(bool avoid_reuse)
|
||||
{
|
||||
if (avoid_reuse) {
|
||||
Assert(m_avoid_reuse_value.has_value());
|
||||
return m_avoid_reuse_value.value();
|
||||
}
|
||||
Assert(m_all_value.has_value());
|
||||
return m_all_value.value();
|
||||
}
|
||||
bool IsCached(bool avoid_reuse)
|
||||
{
|
||||
if (avoid_reuse) return m_avoid_reuse_value.has_value();
|
||||
return m_all_value.has_value();
|
||||
}
|
||||
};
|
||||
|
||||
@ -371,13 +388,11 @@ class WalletTXO
|
||||
private:
|
||||
const CWalletTx& m_wtx;
|
||||
const CTxOut& m_output;
|
||||
isminetype m_ismine;
|
||||
|
||||
public:
|
||||
WalletTXO(const CWalletTx& wtx, const CTxOut& output, const isminetype ismine)
|
||||
WalletTXO(const CWalletTx& wtx, const CTxOut& output)
|
||||
: m_wtx(wtx),
|
||||
m_output(output),
|
||||
m_ismine(ismine)
|
||||
m_output(output)
|
||||
{
|
||||
Assume(std::ranges::find(wtx.tx->vout, output) != wtx.tx->vout.end());
|
||||
}
|
||||
@ -385,9 +400,6 @@ public:
|
||||
const CWalletTx& GetWalletTx() const { return m_wtx; }
|
||||
|
||||
const CTxOut& GetTxOut() const { return m_output; }
|
||||
|
||||
isminetype GetIsMine() const { return m_ismine; }
|
||||
void SetIsMine(isminetype ismine) { m_ismine = ismine; }
|
||||
};
|
||||
} // namespace wallet
|
||||
|
||||
|
||||
@ -17,37 +17,6 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace wallet {
|
||||
/**
|
||||
* IsMine() return codes, which depend on ScriptPubKeyMan implementation.
|
||||
* Not every ScriptPubKeyMan covers all types, please refer to
|
||||
* https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.21.0.md#ismine-semantics
|
||||
* for better understanding.
|
||||
*
|
||||
* For LegacyScriptPubKeyMan,
|
||||
* ISMINE_NO: the scriptPubKey is not in the wallet;
|
||||
* ISMINE_SPENDABLE: the scriptPubKey corresponds to an address owned by the wallet user (can spend with the private key);
|
||||
* ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user;
|
||||
* ISMINE_ALL: all ISMINE flags except for USED;
|
||||
* ISMINE_ALL_USED: all ISMINE flags including USED;
|
||||
* ISMINE_ENUM_ELEMENTS: the number of isminetype enum elements.
|
||||
*
|
||||
* For DescriptorScriptPubKeyMan and future ScriptPubKeyMan,
|
||||
* ISMINE_NO: the scriptPubKey is not in the wallet;
|
||||
* ISMINE_SPENDABLE: the scriptPubKey matches a scriptPubKey in the wallet.
|
||||
* ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user.
|
||||
*
|
||||
*/
|
||||
enum isminetype : unsigned int {
|
||||
ISMINE_NO = 0,
|
||||
ISMINE_SPENDABLE = 1 << 1,
|
||||
ISMINE_USED = 1 << 2,
|
||||
ISMINE_ALL = ISMINE_SPENDABLE,
|
||||
ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED,
|
||||
ISMINE_ENUM_ELEMENTS,
|
||||
};
|
||||
/** used for bitflags of isminetype */
|
||||
using isminefilter = std::underlying_type_t<isminetype>;
|
||||
|
||||
/**
|
||||
* Address purpose field that has been been stored with wallet sending and
|
||||
* receiving addresses since BIP70 payment protocol support was added in
|
||||
|
||||
@ -1569,45 +1569,45 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
|
||||
}
|
||||
|
||||
// Note that this function doesn't distinguish between a 0-valued input,
|
||||
// and a not-"is mine" (according to the filter) input.
|
||||
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
|
||||
// and a not-"is mine" input.
|
||||
CAmount CWallet::GetDebit(const CTxIn &txin) const
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
auto txo = GetTXO(txin.prevout);
|
||||
if (txo && (txo->GetIsMine() & filter)) {
|
||||
if (txo) {
|
||||
return txo->GetTxOut().nValue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
isminetype CWallet::IsMine(const CTxOut& txout) const
|
||||
bool CWallet::IsMine(const CTxOut& txout) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
return IsMine(txout.scriptPubKey);
|
||||
}
|
||||
|
||||
isminetype CWallet::IsMine(const CTxDestination& dest) const
|
||||
bool CWallet::IsMine(const CTxDestination& dest) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
return IsMine(GetScriptForDestination(dest));
|
||||
}
|
||||
|
||||
isminetype CWallet::IsMine(const CScript& script) const
|
||||
bool CWallet::IsMine(const CScript& script) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
// Search the cache so that IsMine is called only on the relevant SPKMs instead of on everything in m_spk_managers
|
||||
const auto& it = m_cached_spks.find(script);
|
||||
if (it != m_cached_spks.end()) {
|
||||
isminetype res = ISMINE_NO;
|
||||
bool res = false;
|
||||
for (const auto& spkm : it->second) {
|
||||
res = std::max(res, spkm->IsMine(script));
|
||||
res = res || spkm->IsMine(script);
|
||||
}
|
||||
Assume(res == ISMINE_SPENDABLE);
|
||||
Assume(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return ISMINE_NO;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWallet::IsMine(const CTransaction& tx) const
|
||||
@ -1619,30 +1619,30 @@ bool CWallet::IsMine(const CTransaction& tx) const
|
||||
return false;
|
||||
}
|
||||
|
||||
isminetype CWallet::IsMine(const COutPoint& outpoint) const
|
||||
bool CWallet::IsMine(const COutPoint& outpoint) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
auto wtx = GetWalletTx(outpoint.hash);
|
||||
if (!wtx) {
|
||||
return ISMINE_NO;
|
||||
return false;
|
||||
}
|
||||
if (outpoint.n >= wtx->tx->vout.size()) {
|
||||
return ISMINE_NO;
|
||||
return false;
|
||||
}
|
||||
return IsMine(wtx->tx->vout[outpoint.n]);
|
||||
}
|
||||
|
||||
bool CWallet::IsFromMe(const CTransaction& tx) const
|
||||
{
|
||||
return (GetDebit(tx, ISMINE_ALL) > 0);
|
||||
return (GetDebit(tx) > 0);
|
||||
}
|
||||
|
||||
CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const
|
||||
CAmount CWallet::GetDebit(const CTransaction& tx) const
|
||||
{
|
||||
CAmount nDebit = 0;
|
||||
for (const CTxIn& txin : tx.vin)
|
||||
{
|
||||
nDebit += GetDebit(txin, filter);
|
||||
nDebit += GetDebit(txin);
|
||||
if (!MoneyRange(nDebit))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
}
|
||||
@ -2377,7 +2377,7 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
|
||||
|
||||
CAddressBookData& record = mi != m_address_book.end() ? mi->second : m_address_book[address];
|
||||
record.SetLabel(strName);
|
||||
is_mine = IsMine(address) != ISMINE_NO;
|
||||
is_mine = IsMine(address);
|
||||
if (new_purpose) { /* update purpose only if requested */
|
||||
record.purpose = new_purpose;
|
||||
}
|
||||
@ -4444,15 +4444,11 @@ void CWallet::RefreshTXOsFromTx(const CWalletTx& wtx)
|
||||
AssertLockHeld(cs_wallet);
|
||||
for (uint32_t i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||
const CTxOut& txout = wtx.tx->vout.at(i);
|
||||
isminetype ismine = IsMine(txout);
|
||||
if (ismine == ISMINE_NO) {
|
||||
continue;
|
||||
}
|
||||
if (!IsMine(txout)) continue;
|
||||
COutPoint outpoint(wtx.GetHash(), i);
|
||||
if (m_txos.contains(outpoint)) {
|
||||
m_txos.at(outpoint).SetIsMine(ismine);
|
||||
} else {
|
||||
m_txos.emplace(outpoint, WalletTXO{wtx, txout, ismine});
|
||||
m_txos.emplace(outpoint, WalletTXO{wtx, txout});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -786,19 +786,16 @@ public:
|
||||
util::Result<CTxDestination> GetNewDestination(const OutputType type, const std::string label);
|
||||
util::Result<CTxDestination> GetNewChangeDestination(const OutputType type);
|
||||
|
||||
isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
/**
|
||||
* Returns amount of debit if the input matches the
|
||||
* filter, otherwise returns 0
|
||||
*/
|
||||
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
|
||||
isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
bool IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
bool IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
/** Returns amount of debit, i.e. the amount leaving this wallet due to this input */
|
||||
CAmount GetDebit(const CTxIn& txin) const;
|
||||
bool IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
isminetype IsMine(const COutPoint& outpoint) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
bool IsMine(const COutPoint& outpoint) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
/** should probably be renamed to IsRelevantToMe */
|
||||
bool IsFromMe(const CTransaction& tx) const;
|
||||
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
|
||||
CAmount GetDebit(const CTransaction& tx) const;
|
||||
|
||||
DBErrors LoadWallet();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user