Skip to content

Commit 1b2cc07

Browse files
82413c8 merge bitcoin#25790: improve `{LoadActive,Deactivate}ScriptPubKeyMan` log (Kittywhiskers Van Gogh) 4f44f2b trivial: add recognition of OutputType::UNKNOWN for completeness (Kittywhiskers Van Gogh) bffeb91 trivial: realign AddAndGetDestinationForScript() signature with upstream (Kittywhiskers Van Gogh) 8473831 partial bitcoin#11403: SegWit wallet support (Kittywhiskers Van Gogh) bcb826e merge bitcoin#24699: Improve AvailableCoins performance by reducing duplicated operations (Kittywhiskers Van Gogh) a9214d4 merge bitcoin#25481: unify max signature logic (Kittywhiskers Van Gogh) 10ddbc3 merge bitcoin#25118: unify "allow/block other inputs" concept (Kittywhiskers Van Gogh) ab93b98 fix: don't call SelectionResult::AddInput() on every iteration (Kittywhiskers Van Gogh) 8b5132b merge bitcoin#24649: do not count wallet utxos as external (Kittywhiskers Van Gogh) 329b7b0 chore: adopt `RANDOM_CHANGE_POSITION` in Dash-specific code (Kittywhiskers Van Gogh) 580f4ca merge bitcoin#20640: return out-params of CreateTransaction() as optional struct (Kittywhiskers Van Gogh) 60d8d89 fix: remove unnecessary `ReserveDestination`s from wallet interface (Kittywhiskers Van Gogh) e0aa417 merge bitcoin#24859: Change wallet validation order (Kittywhiskers Van Gogh) 6758f1e partial bitcoin#23201: Allow users to specify input weights when funding a transaction (Kittywhiskers Van Gogh) 2e9e1ec merge bitcoin#23188: fund transaction external input cleanups (Kittywhiskers Van Gogh) c2766ec merge bitcoin#17211: Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs (Kittywhiskers Van Gogh) 4a5cf5b fix: add embedded addresses awareness and field to `getaddressinfo` (Kittywhiskers Van Gogh) 7af55a5 chore: extract `ScriptHash` specialization of `operator()` to function (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * The `embedded` field was introduced in [bitcoin#11403](bitcoin#11403) (3eaa003) for adding RPC awareness for P2SH (Legacy) SegWit addresses. While it is primarily used for SegWit, it can be used in non-SegWit contexts (e.g. atypical descriptor usage). As Dash Core does not support SegWit, this was not implemented and the leftover reference to `embedded` was removed from `getaddressinfo` in [dash#4675](#4675) (ec162d7). * But, [bitcoin#17211](bitcoin#17211) engages in this exact type of atypical descriptor usage in `rpc_psbt.py`, embedding a P2PKH address (which is valid in Dash Core) in a P2SH ([source](https://github.com/bitcoin/bitcoin/blob/928af61cdb2c4de1c3d10e6fda13bbba5ca0bba9/test/functional/rpc_psbt.py#L619-L620)). The exact scenario that was considered outside the scope of `getaddressinfo` but now causes a test failure (see below). <details> <summary>Test failure:</summary> ``` $ ./test/functional/rpc_psbt.py 2025-07-15T11:46:42.239000Z TestFramework (INFO): PRNG seed is: 8258839804852209703 2025-07-15T11:46:42.239000Z TestFramework (INFO): Initializing test directory /tmp/dash_func_test_8e24i325 [...] 2025-07-15T11:47:02.519000Z TestFramework (INFO): PSBT with signed, but not finalized, inputs should have Finalizer as next 2025-07-15T11:47:03.553000Z TestFramework (ERROR): Key error Traceback (most recent call last): File "/home/eclair/Repositories/dashpay/dash/test/functional/test_framework/test_framework.py", line 161, in main self.run_test() File "/home/eclair/Repositories/dashpay/dash/./test/functional/rpc_psbt.py", line 490, in run_test psbt = self.nodes[1].walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {'add_inputs': True, "solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}}) ~~~~~~~~~^^^^^^^^^^ KeyError: 'pubkey' 2025-07-15T11:47:03.605000Z TestFramework (INFO): Stopping nodes [...] ``` </details> This has since been remedied. * Since [bitcoin#16208](bitcoin#16208) (78fdc99), neither `CreateTransaction()` nor `CommitTransaction()` have consumed a `ReserveDestination` (then `CReserveKey`) but they have still persisted despite serving no purpose. `CreateTransaction()`, through `CreateTransactionInternal()` generates its own `ReserveDestination` ([source](https://github.com/dashpay/dash/blob/9310ebc43cdc4ccde7b3033f4d18139258d7c7ad/src/wallet/spend.cpp#L689)) and `CommitTransaction()` has no use for it. This has since been remedied. * When backporting [bitcoin#20640](bitcoin#20640), upstream took the decision to define `RANDOM_CHANGE_POSITION` at every location it was used, while unclear why this is the case, this is somewhat feasible for them. Dash Core, courtesy of CoinJoin, references the value significantly more often. So, we deviate from upstream and define `RANDOM_CHANGE_POSITION` in `wallet/spend.h` to consolidate definitions. * Dash-specific code has been updated to use `RANDOM_CHANGE_POSITION` when possible to specify _what_ the magic number (`-1`) is supposed to signify. * This deviation from upstream will resolve itself when backporting 758501b from [bitcoin#25273](bitcoin#25273), which does away with `RANDOM_CHANGE_POSITION` altogether. * When translating `!fRequireAllInputs` behavior (introduced in [dash#2581](#2581)) for [bitcoin#22019](bitcoin#22019) (c5038c1), it engaged in erroneous behavior of adding the whole set of `preset_inputs` to `result` _while_ iterating instead of _after_ ([source](https://github.com/dashpay/dash/blob/9310ebc43cdc4ccde7b3033f4d18139258d7c7ad/src/wallet/spend.cpp#L452-L457)). This results in unwanted accumulative behavior but did not surface as `SelectionResult` internally uses a `std::set` ([source](https://github.com/dashpay/dash/blob/9310ebc43cdc4ccde7b3033f4d18139258d7c7ad/src/wallet/coinselection.h#L278-L279)), which meant accumulative behavior was mitigated by deduplication. But this is an implementation detail and should not be relied for correctness of malformed code. To remedy this, the code has been reworked to call `SelectionResult::AddInput()` _after_ `nTargetValue` is met and a test, `minimum_inputs_test` has been introduced to `coinselector_tests` to validate that: * `!fRequireAllInputs` will result in _only_ consuming the minimum required set of coin to match `nTargetValue` **and** * The selected coins are accounted for correctly (i.e. `GetSelectedValue()` doesn't count the same coin multiple times) * To preserve the expected behavior of `!m_allow_other_inputs && !fRequireAllInputs`, despite the goal of [bitcoin#25118](bitcoin#25118) to reduce `vCoins` usage, we retain it _only_ for the Dash-specific condition, falling back to upstream behavior that condition isn't met or if `nTargetValue` is not satisfied. * While Dash only supports P2PKH as the _primary_ address type (a.k.a. `OutputType::LEGACY`), upstream supports multiple with the introduction of SegWit and introduced helper functions to track those multiple address types. They were introduced in [bitcoin#11403](bitcoin#11403) and as Dash doesn't implement SegWit, it wasn't implemented. Though, despite that, the `outputtype.{cpp,h}` source files were introduced when backporting [bitcoin#17261](bitcoin#17261) (ed88ba7) to fulfil that backport. As there are backports that may require those helper functions (e.g. [bitcoin#25790](bitcoin#25790)), they were implemented from [bitcoin#11403](bitcoin#11403) but directly applied to `outputtype.{cpp,h}`. * As `OutputType::UNKNOWN` exists as a valid entry in `develop` ([source](https://github.com/dashpay/dash/blob/9310ebc43cdc4ccde7b3033f4d18139258d7c7ad/src/outputtype.h#L14)), the helper functions were modified to account for this valid enum value courtesy of f5649db from [bitcoin#25734](bitcoin#25734). Liberties were taken to ensure the smallest set of changes needed were implemented. ## Breaking Changes None expected. ## How Has This Been Tested? A full CoinJoin session run on 7194128 ![CoinJoin session run on build 7194128](https://github.com/user-attachments/assets/faab0b40-63ec-4178-8623-b2e6d51f55cb) ## 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)_ Top commit has no ACKs. Tree-SHA512: 6825df7c28355e7e2a9a0a4c497a83748a2481b164589a21d4db887ffad5bbe52c31bed36fd7b5ea5657b5e01555847ad15213b9dd47849a0269ee6dc69a40c7
2 parents b878b1f + 82413c8 commit 1b2cc07

26 files changed

+832
-307
lines changed

src/bench/coin_selection.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ static void CoinSelection(benchmark::Bench& bench)
5656
// Create coins
5757
std::vector<COutput> coins;
5858
for (const auto& wtx : wtxs) {
59-
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, /*fees=*/ 0);
59+
const auto txout = wtx->tx->vout.at(0);
60+
coins.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);
6061
}
6162
const CoinEligibilityFilter filter_standard(1, 6, 0);
6263
FastRandomContext rand{};

src/coinjoin/util.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ using wallet::CRecipient;
1919
using wallet::CWallet;
2020
using wallet::FEATURE_COMPRPUBKEY;
2121
using wallet::GetDiscardRate;
22+
using wallet::RANDOM_CHANGE_POSITION;
2223
using wallet::WalletBatch;
2324

2425
inline unsigned int GetSizeOfCompactSizeDiff(uint64_t nSizePrev, uint64_t nSizeNew)
@@ -127,7 +128,7 @@ CTransactionBuilder::CTransactionBuilder(CWallet& wallet, const CompactTallyItem
127128
// Change always goes back to origin
128129
coinControl.destChange = tallyItemIn.txdest;
129130
// Only allow tallyItems inputs for tx creation
130-
coinControl.fAllowOtherInputs = false;
131+
coinControl.m_allow_other_inputs = false;
131132
// Create dummy tx to calculate the exact required fees upfront for accurate amount and fee calculations
132133
CMutableTransaction dummyTx;
133134
// Select all tallyItem outputs in the coinControl so that CreateTransaction knows what to use
@@ -147,7 +148,7 @@ CTransactionBuilder::CTransactionBuilder(CWallet& wallet, const CompactTallyItem
147148
dummyBatch.TxnAbort();
148149
dummyScript = ::GetScriptForDestination(PKHash(dummyPubkey));
149150
// Calculate required bytes for the dummy signed tx with tallyItem's inputs only
150-
nBytesBase = CalculateMaximumSignedTxSize(CTransaction(dummyTx), &m_wallet, false);
151+
nBytesBase = CalculateMaximumSignedTxSize(CTransaction(dummyTx), &m_wallet, /*coin_control=*/nullptr);
151152
}
152153
// Calculate the output size
153154
nBytesOutput = ::GetSerializeSize(CTxOut(0, dummyScript), PROTOCOL_VERSION);
@@ -266,7 +267,7 @@ bool CTransactionBuilder::IsDust(CAmount nAmount) const
266267
bool CTransactionBuilder::Commit(bilingual_str& strResult)
267268
{
268269
CAmount nFeeRet = 0;
269-
int nChangePosRet = -1;
270+
int nChangePosRet{RANDOM_CHANGE_POSITION};
270271

271272
// Transform the outputs to the format CWallet::CreateTransaction requires
272273
std::vector<CRecipient> vecSend;
@@ -282,21 +283,25 @@ bool CTransactionBuilder::Commit(bilingual_str& strResult)
282283
{
283284
LOCK2(m_wallet.cs_wallet, ::cs_main);
284285
FeeCalculation fee_calc_out;
285-
if (!CreateTransaction(m_wallet, vecSend, tx, nFeeRet, nChangePosRet, strResult, coinControl, fee_calc_out)) {
286+
if (auto txr = wallet::CreateTransaction(m_wallet, vecSend, nChangePosRet, strResult, coinControl, fee_calc_out)) {
287+
tx = txr->tx;
288+
nFeeRet = txr->fee;
289+
nChangePosRet = txr->change_pos;
290+
} else {
286291
return false;
287292
}
288293
}
289294

290295
CAmount nAmountLeft = GetAmountLeft();
291296
bool fDust = IsDust(nAmountLeft);
292297
// If there is a either remainder which is considered to be dust (will be added to fee in this case) or no amount left there should be no change output, return if there is a change output.
293-
if (nChangePosRet != -1 && fDust) {
298+
if (nChangePosRet != RANDOM_CHANGE_POSITION && fDust) {
294299
strResult = Untranslated(strprintf("Unexpected change output %s at position %d", tx->vout[nChangePosRet].ToString(), nChangePosRet));
295300
return false;
296301
}
297302

298303
// If there is a remainder which is not considered to be dust it should end up in a change output, return if not.
299-
if (nChangePosRet == -1 && !fDust) {
304+
if (nChangePosRet == RANDOM_CHANGE_POSITION && !fDust) {
300305
strResult = Untranslated(strprintf("Change output missing: %d", nAmountLeft));
301306
return false;
302307
}

src/interfaces/wallet.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ class Wallet
198198
virtual WalletTx getWalletTx(const uint256& txid) = 0;
199199

200200
//! Get list of all wallet transactions.
201-
virtual std::vector<WalletTx> getWalletTxs() = 0;
201+
virtual std::set<WalletTx> getWalletTxs() = 0;
202202

203203
//! Try to get updated status for a particular transaction, if possible without blocking.
204204
virtual bool tryGetTxStatus(const uint256& txid,
@@ -432,6 +432,8 @@ struct WalletTx
432432
bool is_coinbase;
433433
bool is_platform_transfer{false};
434434
bool is_denominate;
435+
436+
bool operator<(const WalletTx& a) const { return tx->GetHash() < a.tx->GetHash(); }
435437
};
436438

437439
//! Updated transaction status.

src/outputtype.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,69 @@
55

66
#include <outputtype.h>
77

8+
#include <pubkey.h>
9+
#include <script/script.h>
10+
#include <script/sign.h>
811
#include <script/signingprovider.h>
912
#include <script/standard.h>
13+
#include <util/vector.h>
1014

1115
#include <assert.h>
16+
#include <string>
1217

13-
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script)
18+
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
19+
static const std::string OUTPUT_TYPE_STRING_UNKNOWN = "unknown";
20+
21+
const std::array<OutputType, 2> OUTPUT_TYPES = {OutputType::LEGACY, OutputType::UNKNOWN};
22+
23+
bool ParseOutputType(const std::string& type, OutputType& output_type)
24+
{
25+
if (type == OUTPUT_TYPE_STRING_LEGACY) {
26+
output_type = OutputType::LEGACY;
27+
return true;
28+
} else if (type == OUTPUT_TYPE_STRING_UNKNOWN) {
29+
output_type = OutputType::UNKNOWN;
30+
return true;
31+
}
32+
return false;
33+
}
34+
35+
const std::string& FormatOutputType(OutputType type)
36+
{
37+
switch (type) {
38+
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
39+
case OutputType::UNKNOWN: return OUTPUT_TYPE_STRING_UNKNOWN;
40+
} // no default case, so the compiler can warn about missing cases
41+
assert(false);
42+
}
43+
44+
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
45+
{
46+
switch (type) {
47+
case OutputType::LEGACY: return PKHash(key);
48+
case OutputType::UNKNOWN: {} // This function should never be used with UNKNOWN, so let it assert
49+
} // no default case, so the compiler can warn about missing cases
50+
assert(false);
51+
}
52+
53+
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
54+
{
55+
PKHash keyid(key);
56+
CTxDestination p2pkh{keyid};
57+
return Vector(std::move(p2pkh));
58+
}
59+
60+
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType type)
1461
{
1562
// Add script to keystore
1663
keystore.AddCScript(script);
64+
ScriptHash sh(script);
1765
// Note that scripts over 520 bytes are not yet supported.
18-
return ScriptHash(script);
66+
switch (type) {
67+
case OutputType::LEGACY:
68+
keystore.AddCScript(GetScriptForDestination(sh));
69+
return sh;
70+
case OutputType::UNKNOWN: {} // This function should not be used for UNKNOWN, so let it assert
71+
} // no default case, so the compiler can warn about missing cases
72+
assert(false);
1973
}

src/outputtype.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,34 @@
99
#include <script/signingprovider.h>
1010
#include <script/standard.h>
1111

12+
#include <array>
13+
#include <string>
14+
#include <vector>
15+
1216
enum class OutputType {
1317
LEGACY,
1418
UNKNOWN,
1519
};
1620

21+
extern const std::array<OutputType, 2> OUTPUT_TYPES;
22+
23+
[[nodiscard]] bool ParseOutputType(const std::string& str, OutputType& output_type);
24+
const std::string& FormatOutputType(OutputType type);
25+
26+
/**
27+
* Get a destination of the requested type (if possible) to the specified key.
28+
* The caller must make sure LearnRelatedScripts has been called beforehand.
29+
*/
30+
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
31+
32+
/** Get all destinations (potentially) supported by the wallet for the given key. */
33+
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
34+
1735
/**
1836
* Get a destination of the requested type (if possible) to the specified script.
1937
* This function will automatically add the script (and any other
2038
* necessary scripts) to the keystore.
2139
*/
22-
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script);
40+
CTxDestination AddAndGetDestinationForScript(FillableSigningProvider& keystore, const CScript& script, OutputType);
2341

2442
#endif // BITCOIN_OUTPUTTYPE_H

src/rpc/evo.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ using wallet::DEFAULT_DISABLE_WALLET;
6363
using wallet::GetWalletForJSONRPCRequest;
6464
using wallet::HELP_REQUIRING_PASSPHRASE;
6565
using wallet::isminetype;
66+
using wallet::RANDOM_CHANGE_POSITION;
6667
#endif // ENABLE_WALLET
6768

6869
static RPCArg GetRpcArg(const std::string& strParamName)
@@ -286,16 +287,14 @@ static void FundSpecialTx(CWallet& wallet, CMutableTransaction& tx, const Specia
286287
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("No funds at specified address %s", EncodeDestination(fundDest)));
287288
}
288289

289-
CTransactionRef newTx;
290-
CAmount nFee;
291-
int nChangePos = -1;
292290
bilingual_str strFailReason;
293-
294291
FeeCalculation fee_calc_out;
295-
if (!CreateTransaction(wallet, vecSend, newTx, nFee, nChangePos, strFailReason, coinControl, fee_calc_out, false,
296-
tx.vExtraPayload.size())) {
292+
auto txr = CreateTransaction(wallet, vecSend, RANDOM_CHANGE_POSITION, strFailReason, coinControl, fee_calc_out,
293+
true, tx.vExtraPayload.size());
294+
if (!txr) {
297295
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason.original);
298296
}
297+
CTransactionRef newTx = txr->tx;
299298

300299
tx.vin = newTx->vin;
301300
tx.vout = newTx->vout;

src/rpc/util.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,7 @@ CTxDestination AddAndGetMultisigDestination(const int required, const std::vecto
291291
throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
292292
}
293293

294-
// Make the address (simpler implementation in compare to bitcoin)
295-
return AddAndGetDestinationForScript(keystore, script_out);
294+
return AddAndGetDestinationForScript(keystore, script_out, OutputType::LEGACY);
296295
}
297296

298297
class DescribeAddressVisitor

src/wallet/coincontrol.h

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
#include <policy/feerate.h>
1010
#include <policy/fees.h>
1111
#include <primitives/transaction.h>
12+
#include <script/keyorigin.h>
13+
#include <script/signingprovider.h>
1214
#include <script/standard.h>
1315

1416
#include <optional>
17+
#include <algorithm>
18+
#include <map>
19+
#include <set>
1520

1621
namespace wallet {
1722
enum class CoinType : uint8_t
@@ -39,13 +44,12 @@ class CCoinControl
3944
public:
4045
//! Custom change destination, if not set an address is generated
4146
CTxDestination destChange = CNoDestination();
42-
//! If false, only selected inputs are used
43-
bool m_add_inputs = true;
4447
//! If false, only safe inputs will be used
4548
bool m_include_unsafe_inputs = false;
46-
//! If false, allows unselected inputs, but requires all selected inputs be used if fAllowOtherInputs is true (default)
47-
bool fAllowOtherInputs = false;
48-
//! If false, only include as many inputs as necessary to fulfill a coin selection request. Only usable together with fAllowOtherInputs
49+
//! If true, the selection process can add extra unselected inputs from the wallet
50+
//! while requires all selected inputs be used
51+
bool m_allow_other_inputs = false;
52+
//! If false, only include as many inputs as necessary to fulfill a coin selection request. Only usable together with m_allow_other_inputs
4953
bool fRequireAllInputs = true;
5054
//! Includes watch only addresses which are solvable
5155
bool fAllowWatchOnly = false;
@@ -67,6 +71,8 @@ class CCoinControl
6771
int m_min_depth = DEFAULT_MIN_DEPTH;
6872
//! Maximum chain depth value for coin availability
6973
int m_max_depth = DEFAULT_MAX_DEPTH;
74+
//! SigningProvider that has pubkeys and scripts to do spend size estimation for external inputs
75+
FlatSigningProvider m_external_provider;
7076
//! Controls which types of coins are allowed to be used (default: ALL_COINS)
7177
CoinType nCoinType = CoinType::ALL_COINS;
7278

@@ -82,11 +88,32 @@ class CCoinControl
8288
return (setSelected.count(output) > 0);
8389
}
8490

91+
bool IsExternalSelected(const COutPoint& output) const
92+
{
93+
return (m_external_txouts.count(output) > 0);
94+
}
95+
96+
bool GetExternalOutput(const COutPoint& outpoint, CTxOut& txout) const
97+
{
98+
const auto ext_it = m_external_txouts.find(outpoint);
99+
if (ext_it == m_external_txouts.end()) {
100+
return false;
101+
}
102+
txout = ext_it->second;
103+
return true;
104+
}
105+
85106
void Select(const COutPoint& output)
86107
{
87108
setSelected.insert(output);
88109
}
89110

111+
void SelectExternal(const COutPoint& outpoint, const CTxOut& txout)
112+
{
113+
setSelected.insert(outpoint);
114+
m_external_txouts.emplace(outpoint, txout);
115+
}
116+
90117
void UnSelect(const COutPoint& output)
91118
{
92119
setSelected.erase(output);
@@ -102,8 +129,24 @@ class CCoinControl
102129
vOutpoints.assign(setSelected.begin(), setSelected.end());
103130
}
104131

105-
// Dash-specific helpers
132+
void SetInputWeight(const COutPoint& outpoint, int64_t weight)
133+
{
134+
m_input_weights[outpoint] = weight;
135+
}
136+
137+
bool HasInputWeight(const COutPoint& outpoint) const
138+
{
139+
return m_input_weights.count(outpoint) > 0;
140+
}
106141

142+
int64_t GetInputWeight(const COutPoint& outpoint) const
143+
{
144+
auto it = m_input_weights.find(outpoint);
145+
assert(it != m_input_weights.end());
146+
return it->second;
147+
}
148+
149+
// Dash-specific helpers
107150
void UseCoinJoin(bool fUseCoinJoin)
108151
{
109152
nCoinType = fUseCoinJoin ? CoinType::ONLY_FULLY_MIXED : CoinType::ALL_COINS;
@@ -116,6 +159,9 @@ class CCoinControl
116159

117160
private:
118161
std::set<COutPoint> setSelected;
162+
std::map<COutPoint, CTxOut> m_external_txouts;
163+
//! Map of COutPoints to the maximum weight for that input
164+
std::map<COutPoint, int64_t> m_input_weights;
119165
};
120166
} // namespace wallet
121167

src/wallet/coinjoin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ std::vector<CompactTallyItem> CWallet::SelectCoinsGroupedByAddresses(bool fSkipD
149149

150150
if (!setWalletTxesCounted.emplace(outpoint.hash).second) continue;
151151

152-
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
152+
const auto it = mapWallet.find(outpoint.hash);
153153
if (it == mapWallet.end()) continue;
154154

155155
const CWalletTx& wtx = (*it).second;

0 commit comments

Comments
 (0)