Replace PSBT::GetInputUTXO with PSBTInput::GetUTXO
What changed, and why it matters
This commit is a straightforward internal code cleanup in Bitcoin Core. It moves a helper function that looks up the previous transaction output (UTXO) for a PSBT input from the whole-transaction object (PartiallySignedTransaction) into the input object itself (PSBTInput). The behavior is unchanged; only the code organization is simplified.
No security action required. This is a benign refactoring commit. Normal code review and regression testing are sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors PartiallySignedTransaction::GetInputUTXO(CTxOut&, int) into PSBTInput::GetUTXO(CTxOut&). Because PSBTInput now stores its own prev_txid and prev_out, the global helper that previously indexed into tx->vin and inputs is redundant. All call sites (AnalyzePSBT, PrecomputePSBTData, and the PSBT fuzz target) are updated to call the new member function. Logic for selecting between non_witness_utxo and witness_utxo and validating the previous transaction hash/index remains identical.
Changed components
src/psbt.hsrc/psbt.cppsrc/node/psbt.cppsrc/test/fuzz/psbt.cppInspect captured patch +21 / −24
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
index cbcbfa0e..38272909 100644
--- a/src/node/psbt.cpp
+++ b/src/node/psbt.cpp
@@ -35,7 +35,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
// Check for a UTXO
CTxOut utxo;
- if (psbtx.GetInputUTXO(utxo, i)) {
+ if (input.GetUTXO(utxo)) {
if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) {
result.SetInvalid(strprintf("PSBT is not valid. Input %u has invalid value", i));
return result;
@@ -123,7 +123,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
PSBTInput& input = psbtx.inputs[i];
Coin newcoin;
- if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, /*options=*/{}) != PSBTError::OK || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, /*options=*/{}) != PSBTError::OK || !input.GetUTXO(newcoin.out)) {
success = false;
break;
} else {
diff --git a/src/psbt.cpp b/src/psbt.cpp
index cff76611..2c67af23 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -100,20 +100,18 @@ bool PartiallySignedTransaction::AddOutput(const PSBTOutput& psbtout)
return false;
}
-bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
+bool PSBTInput::GetUTXO(CTxOut& utxo) const
{
- const PSBTInput& input = inputs[input_index];
- uint32_t prevout_index = tx->vin[input_index].prevout.n;
- if (input.non_witness_utxo) {
- if (prevout_index >= input.non_witness_utxo->vout.size()) {
+ if (non_witness_utxo) {
+ if (prev_out >= non_witness_utxo->vout.size()) {
return false;
}
- if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
+ if (non_witness_utxo->GetHash() != prev_txid) {
return false;
}
- utxo = input.non_witness_utxo->vout[prevout_index];
- } else if (!input.witness_utxo.IsNull()) {
- utxo = input.witness_utxo;
+ utxo = non_witness_utxo->vout[prev_out];
+ } else if (!witness_utxo.IsNull()) {
+ utxo = witness_utxo;
} else {
return false;
}
@@ -435,9 +433,9 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
{
const CMutableTransaction& tx = *psbt.tx;
bool have_all_spent_outputs = true;
- std::vector<CTxOut> utxos(tx.vin.size());
- for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
- if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
+ std::vector<CTxOut> utxos;
+ for (const PSBTInput& input : psbt.inputs) {
+ if (!input.GetUTXO(utxos.emplace_back())) have_all_spent_outputs = false;
}
PrecomputedTransactionData txdata;
if (have_all_spent_outputs) {
diff --git a/src/psbt.h b/src/psbt.h
index 70bdae96..6e7e0cb9 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -310,6 +310,13 @@ public:
void Merge(const PSBTInput& input);
uint32_t GetVersion() const { return m_psbt_version; }
COutPoint GetOutPoint() const;
+ /**
+ * Retrieves the UTXO for this input
+ *
+ * @param[out] utxo The UTXO of this input
+ * @return Whether the UTXO could be retrieved
+ */
+ bool GetUTXO(CTxOut& utxo) const;
explicit PSBTInput(uint32_t psbt_version, const Txid& prev_txid, uint32_t prev_out, std::optional<uint32_t> sequence = std::nullopt)
: m_psbt_version(psbt_version),
@@ -1087,14 +1094,6 @@ public:
bool AddInput(const PSBTInput& psbtin);
bool AddOutput(const PSBTOutput& psbtout);
explicit PartiallySignedTransaction(const CMutableTransaction& tx);
- /**
- * Finds the UTXO for a given input index
- *
- * @param[out] utxo The UTXO of the input if found
- * @param[in] input_index Index of the input to retrieve the UTXO of
- * @return Whether the UTXO for the specified input was found
- */
- bool GetInputUTXO(CTxOut& utxo, int input_index) const;
template <typename Stream>
inline void Serialize(Stream& s) const {
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index a1b1a5e9..7481d02e 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -68,9 +68,9 @@ FUZZ_TARGET(psbt)
(void)output.IsNull();
}
- for (size_t i = 0; i < psbt.inputs.size(); ++i) {
+ for (const PSBTInput& input : psbt.inputs) {
CTxOut tx_out;
- if (psbt.GetInputUTXO(tx_out, i)) {
+ if (input.GetUTXO(tx_out)) {
(void)tx_out.IsNull();
(void)tx_out.ToString();
}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.