Skip to content

Commit 06de2b0

Browse files
Merge #6654: backport: merge bitcoin#17526, bitcoin#22928, bitcoin#22019, bitcoin#23762, bitcoin#24067, bitcoin#24091, bitcoin#24560, partial bitcoin#17211 (wallet backports: part 4)
e901183 merge bitcoin#24560: Use single FastRandomContext when creating a wallet tx (Kittywhiskers Van Gogh) 5a1850d merge bitcoin#24091: Consolidate CInputCoin and COutput (Kittywhiskers Van Gogh) 8b815dc refactor: make `vecInputCoins` use `COutPoint`, rename to `outpoints` (Kittywhiskers Van Gogh) 3260e07 merge bitcoin#24067: Actually treat (un)confirmed txs as (un)confirmed (Kittywhiskers Van Gogh) 84d9194 merge bitcoin#23762: Replace Assume with Assert where needed in coinselection (Kittywhiskers Van Gogh) 66b5302 wallet: trigger coin selection fast-fail condition over ignoring result (UdjinM6) d9ee3db wallet: fast-fail coin selection when supplied empty output groups (UdjinM6) c5038c1 merge bitcoin#22019: Introduce SelectionResult for encapsulating a coin selection solution (Kittywhiskers Van Gogh) c352062 trivial: invert `SelectCoins` condition check to move bail-out condition (Kittywhiskers Van Gogh) 40550c9 merge bitcoin#22928: Remove gArgs from wallet.h and wallet.cpp (2) (Kittywhiskers Van Gogh) aae8e91 partial bitcoin#17211: Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs (Kittywhiskers Van Gogh) 870548a merge bitcoin#17526: Add Single Random Draw as an additional coin selection algorithm (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * While [bitcoin#17526](bitcoin#17526) introduces Single Random Draw (SRD) as a new coin-selection algorithm, it remains disabled (like BnB) as it is unaware of mixed funds. * UTXO (un)locking as introduced in df765a4 for `rpc_fundrawtransaction.py` has been omitted in the `test_fee_p2pkh()` sub-test as we do not have any other outputs except for `p2pkh`. Failing to do so results in test failure (see below). <details> <summary>Test error:</summary> ``` dash@507c3774b68c:/src/dash$ ./test/functional/rpc_fundrawtransaction.py 2025-05-06T06:48:01.101000Z TestFramework (INFO): PRNG seed is: 4612096820677160739 2025-05-06T06:48:01.101000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_n3i0k9yc 2025-05-06T06:48:04.177000Z TestFramework (INFO): Connect nodes, set fees, generate blocks, and sync [...] 2025-05-06T06:48:09.249000Z TestFramework (INFO): Test fundrawtxn p2pkh fee 2025-05-06T06:48:09.298000Z TestFramework (ERROR): JSONRPC error Traceback (most recent call last): File "/src/dash/test/functional/test_framework/test_framework.py", line 162, in main self.run_test() File "/src/dash/./test/functional/rpc_fundrawtransaction.py", line 115, in run_test self.test_fee_p2pkh() File "/src/dash/./test/functional/rpc_fundrawtransaction.py", line 398, in test_fee_p2pkh fundedTx = self.nodes[0].fundrawtransaction(rawtx) File "/src/dash/test/functional/test_framework/coverage.py", line 49, in __call__ return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs) File "/src/dash/test/functional/test_framework/authproxy.py", line 143, in __call__ raise JSONRPCException(response['error'], status) test_framework.authproxy.JSONRPCException: Insufficient funds. (-4) 2025-05-06T06:48:09.802000Z TestFramework (INFO): Stopping nodes ``` </details> * In [bitcoin#22019](bitcoin#22019) we don't use `SelectionResult::GetShuffledInputVector()` in `CreateTransactionInternal()` because it interferes with our BIP69 sorting implementation that sorts `vin`s instead of `CRecipient`s. ## How Has This Been Tested? A full CoinJoin session run on 0e33db2 ![CoinJoin session run on build 0e33db2](https://github.com/user-attachments/assets/2aca71dc-4931-44fd-93e4-fc260b22b885) ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: LGTM, utACK e901183 PastaPastaPasta: utACK e901183 Tree-SHA512: 8631107e23fc581f23a366e468c10079d72c41b81475e1f33f79262ec3706b48cbc464fe83e31d33070f58c03340189431e4fbd81a7d13b5f056bfcc2f96929a
2 parents e853d0b + e901183 commit 06de2b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+973
-717
lines changed

src/bench/coin_selection.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static void CoinSelection(benchmark::Bench& bench)
3232
{
3333
NodeContext node;
3434
auto chain = interfaces::MakeChain(node);
35-
CWallet wallet(chain.get(), /*coinjoin_loader=*/ nullptr, "", CreateDummyWalletDatabase());
35+
CWallet wallet(chain.get(), /*coinjoin_loader=*/nullptr, "", gArgs, CreateDummyWalletDatabase());
3636
std::vector<std::unique_ptr<CWalletTx>> wtxs;
3737
LOCK(wallet.cs_wallet);
3838

@@ -45,34 +45,37 @@ static void CoinSelection(benchmark::Bench& bench)
4545
// Create coins
4646
std::vector<COutput> coins;
4747
for (const auto& wtx : wtxs) {
48-
coins.emplace_back(wallet, *wtx, 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
48+
coins.emplace_back(COutPoint(wtx->GetHash(), 0), wtx->tx->vout.at(0), /*depth=*/ 6 * 24, GetTxSpendSize(wallet, *wtx, 0), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, wtx->GetTxTime(), /*from_me=*/ true);
4949
}
5050
const CoinEligibilityFilter filter_standard(1, 6, 0);
51-
const CoinSelectionParams coin_selection_params(/* change_output_size= */ 34,
52-
/* change_spend_size= */ 148, /* effective_feerate= */ CFeeRate(0),
53-
/* long_term_feerate= */ CFeeRate(0), /* discard_feerate= */ CFeeRate(0),
54-
/* tx_noinputs_size= */ 0, /* avoid_partial= */ false);
51+
FastRandomContext rand{};
52+
const CoinSelectionParams coin_selection_params{
53+
rand,
54+
/* change_output_size= */ 34,
55+
/* change_spend_size= */ 148,
56+
/* effective_feerate= */ CFeeRate(0),
57+
/* long_term_feerate= */ CFeeRate(0),
58+
/* discard_feerate= */ CFeeRate(0),
59+
/* tx_noinputs_size= */ 0,
60+
/* avoid_partial= */ false,
61+
};
5562
bench.run([&] {
56-
std::set<CInputCoin> setCoinsRet;
57-
CAmount nValueRet;
58-
bool success = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, setCoinsRet, nValueRet, coin_selection_params);
59-
assert(success);
60-
assert(nValueRet == 1003 * COIN);
61-
assert(setCoinsRet.size() == 2);
63+
auto result = AttemptSelection(wallet, 1003 * COIN, filter_standard, coins, coin_selection_params);
64+
assert(result);
65+
assert(result->GetSelectedValue() == 1003 * COIN);
66+
assert(result->GetInputSet().size() == 2);
6267
});
6368
}
6469

65-
typedef std::set<CInputCoin> CoinSet;
66-
6770
// Copied from src/wallet/test/coinselector_tests.cpp
6871
static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>& set)
6972
{
7073
CMutableTransaction tx;
7174
tx.vout.resize(nInput + 1);
7275
tx.vout[nInput].nValue = nValue;
73-
CInputCoin coin(MakeTransactionRef(tx), nInput);
76+
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);
7477
set.emplace_back();
75-
set.back().Insert(coin, 0, true, 0, 0, false);
78+
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
7679
}
7780
// Copied from src/wallet/test/coinselector_tests.cpp
7881
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
@@ -91,17 +94,14 @@ static void BnBExhaustion(benchmark::Bench& bench)
9194
{
9295
// Setup
9396
std::vector<OutputGroup> utxo_pool;
94-
CoinSet selection;
95-
CAmount value_ret = 0;
9697

9798
bench.run([&] {
9899
// Benchmark
99100
CAmount target = make_hard_case(17, utxo_pool);
100-
SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret); // Should exhaust
101+
SelectCoinsBnB(utxo_pool, target, 0); // Should exhaust
101102

102103
// Cleanup
103104
utxo_pool.clear();
104-
selection.clear();
105105
});
106106
}
107107

src/bench/wallet_balance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
1919
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
2020
const auto& ADDRESS_WATCHONLY = ADDRESS_B58T_UNSPENDABLE;
2121

22-
CWallet wallet{test_setup->m_node.chain.get(), test_setup->m_node.coinjoin_loader.get(), "", CreateMockWalletDatabase()};
22+
CWallet wallet{test_setup->m_node.chain.get(), test_setup->m_node.coinjoin_loader.get(), "", gArgs, CreateMockWalletDatabase()};
2323
{
2424
LOCK(wallet.cs_wallet);
2525
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/coinjoin/client.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,12 +1468,12 @@ bool CCoinJoinClientSession::MakeCollateralAmounts(const CompactTallyItem& tally
14681468
if (!CCoinJoinClientOptions::IsEnabled()) return false;
14691469

14701470
// Denominated input is always a single one, so we can check its amount directly and return early
1471-
if (!fTryDenominated && tallyItem.vecInputCoins.size() == 1 && CoinJoin::IsDenominatedAmount(tallyItem.nAmount)) {
1471+
if (!fTryDenominated && tallyItem.outpoints.size() == 1 && CoinJoin::IsDenominatedAmount(tallyItem.nAmount)) {
14721472
return false;
14731473
}
14741474

14751475
// Skip single inputs that can be used as collaterals already
1476-
if (tallyItem.vecInputCoins.size() == 1 && CoinJoin::IsCollateralAmount(tallyItem.nAmount)) {
1476+
if (tallyItem.outpoints.size() == 1 && CoinJoin::IsCollateralAmount(tallyItem.nAmount)) {
14771477
return false;
14781478
}
14791479

@@ -1562,10 +1562,10 @@ bool CCoinJoinClientSession::CreateCollateralTransaction(CMutableTransaction& tx
15621562
}
15631563

15641564
const auto& output = vCoins.at(GetRand(vCoins.size()));
1565-
const CTxOut txout = output.tx->tx->vout[output.i];
1565+
const CTxOut txout = output.txout;
15661566

15671567
txCollateral.vin.clear();
1568-
txCollateral.vin.emplace_back(output.tx->GetHash(), output.i);
1568+
txCollateral.vin.emplace_back(output.outpoint.hash, output.outpoint.n);
15691569
txCollateral.vout.clear();
15701570

15711571
// pay collateral charge in fees
@@ -1636,7 +1636,7 @@ bool CCoinJoinClientSession::CreateDenominated(CAmount nBalanceToDenominate, con
16361636
if (!CCoinJoinClientOptions::IsEnabled()) return false;
16371637

16381638
// denominated input is always a single one, so we can check its amount directly and return early
1639-
if (tallyItem.vecInputCoins.size() == 1 && CoinJoin::IsDenominatedAmount(tallyItem.nAmount)) {
1639+
if (tallyItem.outpoints.size() == 1 && CoinJoin::IsDenominatedAmount(tallyItem.nAmount)) {
16401640
return false;
16411641
}
16421642

src/coinjoin/util.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ CTransactionBuilder::CTransactionBuilder(CWallet& wallet, const CompactTallyItem
124124
// Create dummy tx to calculate the exact required fees upfront for accurate amount and fee calculations
125125
CMutableTransaction dummyTx;
126126
// Select all tallyItem outputs in the coinControl so that CreateTransaction knows what to use
127-
for (const auto& coin : tallyItem.vecInputCoins) {
128-
coinControl.Select(coin.outpoint);
129-
dummyTx.vin.emplace_back(coin.outpoint, CScript());
127+
for (const auto& outpoint : tallyItem.outpoints) {
128+
coinControl.Select(outpoint);
129+
dummyTx.vin.emplace_back(outpoint, CScript());
130130
}
131131
// Get a comparable dummy scriptPubKey, avoid writing/flushing to the actual wallet db
132132
CScript dummyScript;

src/interfaces/chain.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,6 @@ class Chain
133133
//! or one of its ancestors.
134134
virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
135135

136-
//! Check if transaction will be final given chain height current time.
137-
virtual bool checkFinalTx(const CTransaction& tx) = 0;
138-
139136
//! Check if transaction is locked by InstantSendManager
140137
virtual bool isInstantSendLockedTx(const uint256& hash) = 0;
141138

src/interfaces/wallet.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ struct WalletTxStatus
433433
int depth_in_main_chain;
434434
unsigned int time_received;
435435
uint32_t lock_time;
436-
bool is_final;
437436
bool is_trusted;
438437
bool is_abandoned;
439438
bool is_coinbase;

src/node/interfaces.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -771,11 +771,6 @@ class ChainImpl : public Chain
771771
}
772772
return std::nullopt;
773773
}
774-
bool checkFinalTx(const CTransaction& tx) override
775-
{
776-
LOCK(cs_main);
777-
return CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), tx);
778-
}
779774
bool isInstantSendLockedTx(const uint256& hash) override
780775
{
781776
if (m_node.llmq_ctx == nullptr || m_node.llmq_ctx->isman == nullptr) return false;

src/qt/test/addressbooktests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
6464
{
6565
TestChain100Setup test;
6666
node.setContext(&test.m_node);
67-
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), node.context()->coinjoin_loader.get(), "", CreateMockWalletDatabase());
67+
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), node.context()->coinjoin_loader.get(), "", gArgs, CreateMockWalletDatabase());
6868
wallet->LoadWallet();
6969
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
7070
{

src/qt/test/wallettests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void TestGUI(interfaces::Node& node)
110110
}
111111
node.setContext(&test.m_node);
112112
WalletContext& context = *node.walletLoader().context();
113-
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), node.context()->coinjoin_loader.get(), "", CreateMockWalletDatabase());
113+
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), node.context()->coinjoin_loader.get(), "", gArgs, CreateMockWalletDatabase());
114114
AddWallet(context, wallet);
115115
wallet->LoadWallet();
116116
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);

src/qt/transactiondesc.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include <key_io.h>
1919
#include <interfaces/node.h>
2020
#include <interfaces/wallet.h>
21-
#include <script/script.h>
2221
#include <util/system.h>
2322
#include <validation.h>
2423
#include <wallet/ismine.h>
@@ -30,14 +29,6 @@
3029

3130
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
3231
{
33-
if (!status.is_final)
34-
{
35-
if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
36-
return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks);
37-
else
38-
return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
39-
}
40-
else
4132
{
4233
int nDepth = status.depth_in_main_chain;
4334
if (nDepth < 0) return tr("conflicted");

0 commit comments

Comments
 (0)