From c5a2d080116270ecd0414c14eb412fa30eaaedaf Mon Sep 17 00:00:00 2001 From: ishaanam Date: Tue, 12 Aug 2025 14:41:53 -0400 Subject: [PATCH] wallet: don't return utxos from multiple truc txs in AvailableCoins --- src/wallet/spend.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index d76e6118379..e29ca045263 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -325,6 +325,9 @@ CoinsResult AvailableCoins(const CWallet& wallet, AssertLockHeld(wallet.cs_wallet); CoinsResult result; + // track unconfirmed truc outputs separately if we are tracking trucness + std::vector> unconfirmed_truc_coins; + std::unordered_map truc_txid_by_value; // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses bool allow_used_addresses = !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); @@ -470,8 +473,15 @@ CoinsResult AvailableCoins(const CWallet& wallet, is_from_p2sh = true; } - result.Add(GetOutputType(type, is_from_p2sh), - COutput(outpoint, output, nDepth, input_bytes, spendable, solvable, tx_safe, wtx.GetTxTime(), tx_from_me, feerate)); + 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); + 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); + it->second += output.nValue; + } else { + result.Add(available_output_type, available_output); + } outpoints.push_back(outpoint); @@ -488,6 +498,23 @@ CoinsResult AvailableCoins(const CWallet& wallet, } } + // Return all the coins from one TRUC transaction, that have the highest value. + // This could be improved in the future by encoding these restrictions in + // the coin selection itself so that we don't have to filter out + // other unconfirmed TRUC coins beforehand. + if (params.check_version_trucness && unconfirmed_truc_coins.size() > 0) { + auto highest_value_truc_tx = std::max_element(truc_txid_by_value.begin(), truc_txid_by_value.end(), [](const auto& tx1, const auto& tx2){ + return tx1.second < tx2.second; + }); + + const Txid& truc_txid = highest_value_truc_tx->first; + for (const auto& [type, output] : unconfirmed_truc_coins) { + if (output.outpoint.hash == truc_txid) { + result.Add(type, output); + } + } + } + if (feerate.has_value()) { std::map map_of_bump_fees = wallet.chain().calculateIndividualBumpFees(outpoints, feerate.value());