Skip to content

Commit 6e9f8bb

Browse files
committed
rpc, tests: in utxoupdatepsbt also look for the transaction in the txindex
Previously only the segwit utxos being spent by the psbt were looked for and added to the psbt. Now, the full transaction corresponding to each of these utxos (legacy and segwit) is looked for in the txindex and mempool and added to the psbt. If txindex is disabled and the transaction is not in the mempool, then we fall back to getting just the utxo (if segwit) from the utxo set.
1 parent a5b4883 commit 6e9f8bb

File tree

5 files changed

+98
-62
lines changed

5 files changed

+98
-62
lines changed

src/psbt.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,38 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
430430
return sig_complete;
431431
}
432432

433+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type)
434+
{
435+
// Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
436+
if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
437+
// Figure out if any non_witness_utxos should be dropped
438+
std::vector<unsigned int> to_drop;
439+
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
440+
const auto& input = psbtx.inputs.at(i);
441+
int wit_ver;
442+
std::vector<unsigned char> wit_prog;
443+
if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
444+
// There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
445+
to_drop.clear();
446+
break;
447+
}
448+
if (wit_ver == 0) {
449+
// Segwit v0, so we cannot drop any non_witness_utxos
450+
to_drop.clear();
451+
break;
452+
}
453+
if (input.non_witness_utxo) {
454+
to_drop.push_back(i);
455+
}
456+
}
457+
458+
// Drop the non_witness_utxos that we can drop
459+
for (unsigned int i : to_drop) {
460+
psbtx.inputs.at(i).non_witness_utxo = nullptr;
461+
}
462+
}
463+
}
464+
433465
bool FinalizePSBT(PartiallySignedTransaction& psbtx)
434466
{
435467
// 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
@@ -1231,6 +1231,9 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
12311231
**/
12321232
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
12331233

1234+
/** 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. */
1235+
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type);
1236+
12341237
/** Counts the unsigned inputs of a PSBT. */
12351238
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
12361239

src/rpc/rawtransaction.cpp

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ static std::vector<RPCArg> CreateTxDoc()
169169
};
170170
}
171171

172-
// Update PSBT with information from the mempool, the UTXO set, and the provided descriptors
172+
// Update PSBT with information from the mempool, the UTXO set, the txindex, and the provided descriptors
173173
PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std::any& context, const HidingSigningProvider& provider)
174174
{
175175
// Unserialize the transactions
@@ -179,50 +179,78 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
179179
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
180180
}
181181

182-
// Fetch previous transactions (inputs):
183-
CCoinsView viewDummy;
184-
CCoinsViewCache view(&viewDummy);
185-
{
186-
NodeContext& node = EnsureAnyNodeContext(context);
187-
const CTxMemPool& mempool = EnsureMemPool(node);
188-
ChainstateManager& chainman = EnsureChainman(node);
189-
LOCK2(cs_main, mempool.cs);
190-
CCoinsViewCache &viewChain = chainman.ActiveChainstate().CoinsTip();
191-
CCoinsViewMemPool viewMempool(&viewChain, mempool);
192-
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
193-
194-
for (const CTxIn& txin : psbtx.tx->vin) {
195-
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
196-
}
182+
if (g_txindex) g_txindex->BlockUntilSyncedToCurrentChain();
183+
const NodeContext& node = EnsureAnyNodeContext(context);
197184

198-
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
199-
}
185+
// If we can't find the corresponding full transaction for all of our inputs,
186+
// this will be used to find just the utxos for the segwit inputs for which
187+
// the full transaction isn't found
188+
std::map<COutPoint, Coin> coins;
200189

201-
// Fill the inputs
202-
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
190+
// Fetch previous transactions:
191+
// First, look in the txindex and the mempool
203192
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
204-
PSBTInput& input = psbtx.inputs.at(i);
193+
PSBTInput& psbt_input = psbtx.inputs.at(i);
194+
const CTxIn& tx_in = psbtx.tx->vin.at(i);
205195

206-
if (input.non_witness_utxo || !input.witness_utxo.IsNull()) {
207-
continue;
196+
// The `non_witness_utxo` is the whole previous transaction
197+
if (psbt_input.non_witness_utxo) continue;
198+
199+
CTransactionRef tx;
200+
201+
// Look in the txindex
202+
if (g_txindex) {
203+
uint256 block_hash;
204+
g_txindex->FindTx(tx_in.prevout.hash, block_hash, tx);
205+
}
206+
// If we still don't have it look in the mempool
207+
if (!tx) {
208+
tx = node.mempool->get(tx_in.prevout.hash);
209+
}
210+
if (tx) {
211+
psbt_input.non_witness_utxo = tx;
212+
} else {
213+
coins[tx_in.prevout]; // Create empty map entry keyed by prevout
214+
}
215+
}
216+
217+
// If we still haven't found all of the inputs, look for the missing ones in the utxo set
218+
if (!coins.empty()) {
219+
FindCoins(node, coins);
220+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
221+
PSBTInput& input = psbtx.inputs.at(i);
222+
223+
// If there are still missing utxos, add them if they were found in the utxo set
224+
if (!input.non_witness_utxo) {
225+
const CTxIn& tx_in = psbtx.tx->vin.at(i);
226+
const Coin& coin = coins.at(tx_in.prevout);
227+
if (!coin.out.IsNull() && IsSegWitOutput(provider, coin.out.scriptPubKey)) {
228+
input.witness_utxo = coin.out;
229+
}
230+
}
208231
}
232+
}
209233

210-
const Coin& coin = view.AccessCoin(psbtx.tx->vin[i].prevout);
234+
const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx);
211235

212-
if (IsSegWitOutput(provider, coin.out.scriptPubKey)) {
213-
input.witness_utxo = coin.out;
236+
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
237+
if (PSBTInputSigned(psbtx.inputs.at(i))) {
238+
continue;
214239
}
215240

216241
// Update script/keypath information using descriptor data.
217242
// Note that SignPSBTInput does a lot more than just constructing ECDSA signatures
218243
// we don't actually care about those here, in fact.
219-
SignPSBTInput(provider, psbtx, i, &txdata, /*sighash=*/1);
244+
SignPSBTInput(provider, psbtx, /*index=*/i, &txdata, /*sighash=*/1);
220245
}
221246

222247
// Update script/keypath information using descriptor data.
223248
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
224249
UpdatePSBTOutput(provider, psbtx, i);
225250
}
251+
252+
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/1);
253+
226254
return psbtx;
227255
}
228256

@@ -1632,7 +1660,7 @@ static RPCHelpMan converttopsbt()
16321660
static RPCHelpMan utxoupdatepsbt()
16331661
{
16341662
return RPCHelpMan{"utxoupdatepsbt",
1635-
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
1663+
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set, txindex, or the mempool.\n",
16361664
{
16371665
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
16381666
{"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of either strings or objects", {

src/wallet/wallet.cpp

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,34 +2156,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
21562156
}
21572157
}
21582158

2159-
// Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
2160-
if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
2161-
// Figure out if any non_witness_utxos should be dropped
2162-
std::vector<unsigned int> to_drop;
2163-
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
2164-
const auto& input = psbtx.inputs.at(i);
2165-
int wit_ver;
2166-
std::vector<unsigned char> wit_prog;
2167-
if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
2168-
// There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
2169-
to_drop.clear();
2170-
break;
2171-
}
2172-
if (wit_ver == 0) {
2173-
// Segwit v0, so we cannot drop any non_witness_utxos
2174-
to_drop.clear();
2175-
break;
2176-
}
2177-
if (input.non_witness_utxo) {
2178-
to_drop.push_back(i);
2179-
}
2180-
}
2181-
2182-
// Drop the non_witness_utxos that we can drop
2183-
for (unsigned int i : to_drop) {
2184-
psbtx.inputs.at(i).non_witness_utxo = nullptr;
2185-
}
2186-
}
2159+
RemoveUnnecessaryTransactions(psbtx, sighash_type);
21872160

21882161
// Complete if every input is now signed
21892162
complete = true;

test/functional/rpc_psbt.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,17 +611,17 @@ def test_psbt_input_keys(psbt_input, keys):
611611
# Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
612612
updated = self.nodes[1].utxoupdatepsbt(psbt)
613613
decoded = self.nodes[1].decodepsbt(updated)
614-
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo'])
615-
test_psbt_input_keys(decoded['inputs'][1], [])
616-
test_psbt_input_keys(decoded['inputs'][2], [])
614+
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'non_witness_utxo'])
615+
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo'])
616+
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo'])
617617

618618
# Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in
619619
descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
620620
updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
621621
decoded = self.nodes[1].decodepsbt(updated)
622-
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'bip32_derivs'])
623-
test_psbt_input_keys(decoded['inputs'][1], [])
624-
test_psbt_input_keys(decoded['inputs'][2], ['witness_utxo', 'bip32_derivs', 'redeem_script'])
622+
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'non_witness_utxo', 'bip32_derivs'])
623+
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo', 'bip32_derivs'])
624+
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo','witness_utxo', 'bip32_derivs', 'redeem_script'])
625625

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

0 commit comments

Comments
 (0)