Skip to content

Commit c4654ae

Browse files
thepastaclawclaude
andcommitted
backport: bitcoin#25939 In utxoupdatepsbt also look for the tx in the txindex
This backports Bitcoin PR bitcoin#25939 (commits a5b4883, 6e9f8bb) which: - Extracts PSBT updating logic from utxoupdatepsbt into ProcessPSBT - Makes utxoupdatepsbt look for full transactions in the txindex and mempool before falling back to the UTXO set - Moves RemoveUnnecessaryTransactions from wallet to psbt module Dash adaptations: - Use CoreContext instead of std::any for context parameter - RemoveUnnecessaryTransactions is a no-op (Dash has no segwit) - UTXO set fallback for witness_utxo removed (Dash has no segwit) - Test expectations adapted for non-segwit addresses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 32fa1a8 commit c4654ae

File tree

5 files changed

+97
-53
lines changed

5 files changed

+97
-53
lines changed

src/psbt.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,13 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
286286
return sig_complete;
287287
}
288288

289+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& /* psbtx */, const int& /* sighash_type */)
290+
{
291+
// Dash does not support segwit, so there are no witness_utxos to consider.
292+
// In Bitcoin, this function drops non_witness_utxos when all inputs are
293+
// segwit v1+, which cannot occur in Dash.
294+
}
295+
289296
bool FinalizePSBT(PartiallySignedTransaction& psbtx)
290297
{
291298
// Finalize input signatures -- in case we have partial signatures that add up to a complete

src/psbt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ bool PSBTInputSigned(const PSBTInput& input);
875875
**/
876876
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
877877

878+
/** Reduces the size of the PSBT by dropping unnecessary `non_witness_utxos` (i.e. complete previous transactions) from a psbt when all inputs are segwit v1. */
879+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type);
880+
878881
/** Counts the unsigned inputs of a PSBT. */
879882
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
880883

src/rpc/rawtransaction.cpp

Lines changed: 78 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,78 @@ static std::vector<RPCArg> CreateTxDoc()
220220
};
221221
}
222222

223+
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors
224+
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const CoreContext& context, const HidingSigningProvider& provider)
225+
{
226+
// Unserialize the transactions
227+
PartiallySignedTransaction psbtx;
228+
std::string error;
229+
if (!DecodeBase64PSBT(psbtx, psbt_string, error)) {
230+
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
231+
}
232+
233+
if (g_txindex) g_txindex->BlockUntilSyncedToCurrentChain();
234+
const NodeContext& node = EnsureAnyNodeContext(context);
235+
236+
// If we can't find the corresponding full transaction for all of our inputs,
237+
// this will be used to find just the utxos for the segwit inputs for which
238+
// the full transaction isn't found
239+
std::map<COutPoint, Coin> coins;
240+
241+
// Fetch previous transactions:
242+
// First, look in the txindex and the mempool
243+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
244+
PSBTInput& psbt_input = psbtx.inputs.at(i);
245+
const CTxIn& tx_in = psbtx.tx->vin.at(i);
246+
247+
// The `non_witness_utxo` is the whole previous transaction
248+
if (psbt_input.non_witness_utxo) continue;
249+
250+
CTransactionRef tx;
251+
252+
// Look in the txindex
253+
if (g_txindex) {
254+
uint256 block_hash;
255+
g_txindex->FindTx(tx_in.prevout.hash, block_hash, tx);
256+
}
257+
// If we still don't have it look in the mempool
258+
if (!tx) {
259+
tx = node.mempool->get(tx_in.prevout.hash);
260+
}
261+
if (tx) {
262+
psbt_input.non_witness_utxo = tx;
263+
} else {
264+
coins[tx_in.prevout]; // Create empty map entry keyed by prevout
265+
}
266+
}
267+
268+
// In Bitcoin, if we still haven't found all of the inputs, the utxo set
269+
// is searched and segwit inputs are updated with just the utxo. Dash does
270+
// not support segwit, so this fallback is not applicable.
271+
272+
const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx);
273+
274+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
275+
if (PSBTInputSigned(psbtx.inputs.at(i))) {
276+
continue;
277+
}
278+
279+
// Update script/keypath information using descriptor data.
280+
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
281+
// we don't actually care about those here, in fact.
282+
SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
283+
}
284+
285+
// Update script/keypath information using descriptor data.
286+
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
287+
UpdatePSBTOutput(provider, psbtx, i);
288+
}
289+
290+
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1);
291+
292+
return psbtx;
293+
}
294+
223295
static RPCHelpMan getrawtransaction()
224296
{
225297
return RPCHelpMan{
@@ -1659,7 +1731,7 @@ static RPCHelpMan converttopsbt()
16591731
static RPCHelpMan utxoupdatepsbt()
16601732
{
16611733
return RPCHelpMan{"utxoupdatepsbt",
1662-
"\nUpdates a PSBT with data from output descriptors, UTXOs retrieved from the UTXO set or the mempool.\n",
1734+
"\nUpdates a PSBT with data from output descriptors, the UTXO set, txindex, or the mempool.\n",
16631735
{
16641736
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
16651737
{"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of either strings or objects", {
@@ -1680,12 +1752,6 @@ static RPCHelpMan utxoupdatepsbt()
16801752
{
16811753
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
16821754

1683-
// Unserialize the transactions
1684-
PartiallySignedTransaction psbtx;
1685-
std::string error;
1686-
if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
1687-
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
1688-
}
16891755

16901756
// Parse descriptors, if any.
16911757
FlatSigningProvider provider;
@@ -1695,47 +1761,12 @@ static RPCHelpMan utxoupdatepsbt()
16951761
EvalDescriptorStringOrObject(descs[i], provider);
16961762
}
16971763
}
1698-
// We don't actually need private keys further on; hide them as a precaution.
1699-
HidingSigningProvider public_provider(&provider, /*hide_secret=*/true, /*hide_origin=*/false);
1700-
1701-
// Fetch previous transactions (inputs):
1702-
CCoinsView viewDummy;
1703-
CCoinsViewCache view(&viewDummy);
1704-
{
1705-
const NodeContext& node = EnsureAnyNodeContext(request.context);
1706-
const CTxMemPool& mempool = EnsureMemPool(node);
1707-
ChainstateManager& chainman = EnsureChainman(node);
1708-
LOCK2(cs_main, mempool.cs);
1709-
CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
1710-
CCoinsViewMemPool viewMempool(&viewChain, mempool);
1711-
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
17121764

1713-
for (const CTxIn& txin : psbtx.tx->vin) {
1714-
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
1715-
}
1716-
1717-
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
1718-
}
1719-
1720-
// Fill the inputs
1721-
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
1722-
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
1723-
PSBTInput& input = psbtx.inputs.at(i);
1724-
1725-
if (input.non_witness_utxo) {
1726-
continue;
1727-
}
1728-
1729-
// Update script/keypath information using descriptor data.
1730-
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
1731-
// we don't actually care about those here, in fact.
1732-
SignPSBTInput(public_provider, psbtx, i, &txdata, /*sighash=*/SIGHASH_ALL);
1733-
}
1734-
1735-
// Update script/keypath information using descriptor data.
1736-
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
1737-
UpdatePSBTOutput(public_provider, psbtx, i);
1738-
}
1765+
// We don't actually need private keys further on; hide them as a precaution.
1766+
const PartiallySignedTransaction& psbtx = ProcessPSBT(
1767+
request.params[0].get_str(),
1768+
request.context,
1769+
HidingSigningProvider(&provider, /*hide_secret=*/true, /*hide_origin=*/false));
17391770

17401771
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
17411772
ssTx << psbtx;

src/wallet/wallet.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,6 +2261,8 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
22612261
}
22622262
}
22632263

2264+
RemoveUnnecessaryTransactions(psbtx, sighash_type);
2265+
22642266
// Complete if every input is now signed
22652267
complete = true;
22662268
for (const auto& input : psbtx.inputs) {

test/functional/rpc_psbt.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,20 @@ def test_psbt_input_keys(psbt_input, keys):
401401
test_psbt_input_keys(decoded['inputs'][2], [])
402402

403403
# Update a PSBT with UTXOs from the node
404-
# No inputs should be filled because they are non-witness
404+
# Inputs should be filled with non_witness_utxo from the mempool
405405
updated = self.nodes[1].utxoupdatepsbt(psbt)
406406
decoded = self.nodes[1].decodepsbt(updated)
407-
test_psbt_input_keys(decoded['inputs'][1], [])
408-
test_psbt_input_keys(decoded['inputs'][2], [])
407+
test_psbt_input_keys(decoded['inputs'][0], ['non_witness_utxo'])
408+
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo'])
409+
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo'])
409410

410411
# Try again, now while providing descriptors
411412
descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
412413
updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
413414
decoded = self.nodes[1].decodepsbt(updated)
414-
test_psbt_input_keys(decoded['inputs'][0], [])
415-
test_psbt_input_keys(decoded['inputs'][1], [])
416-
test_psbt_input_keys(decoded['inputs'][2], [])
415+
test_psbt_input_keys(decoded['inputs'][0], ['non_witness_utxo', 'bip32_derivs'])
416+
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo', 'bip32_derivs'])
417+
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo', 'bip32_derivs'])
417418

418419
# Two PSBTs with a common input should not be joinable
419420
psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})

0 commit comments

Comments
 (0)