psbt: Fill hash preimages and taproot builder from SignatureData
What changed, and why it matters
This commit fixes a gap in Bitcoin Core's Partially Signed Bitcoin Transaction (PSBT) handling. When converting signature data into a PSBT input or output, several pieces of information—hash preimages for certain script operations and the taproot script tree builder—were not being copied over. Missing preimages could prevent a PSBT from being finalized correctly, and missing the taproot builder could prevent proper analysis or signing of taproot outputs. It is a correctness fix in wallet/transaction code rather than a remote-exploitable vulnerability.
Treat as a routine correctness/robustness fix. Review related PSBT merge and deserialization paths to ensure these fields are also handled consistently, and add regression tests for PSBTs containing hash preimages and taproot script trees.
Security signals we found
Missing data flow in PSBT serialization could lead to incomplete or unspendable PSBTs
Hash preimage omission affects scripts relying on OP_RIPEMD160/SHA256/HASH160/HASH256 hash locks
Taproot builder omission affects taproot output signing and script-path spend analysis
No explicit bounds, memory, or cryptographic validation changes in the patch
Evidence from the diff
PSBTInput::FromSignatureData() now populates ripemd160_preimages, sha256_preimages, hash160_preimages, and hash256_preimages from the corresponding SignatureData maps. PSBTOutput::FillSignatureData() now assigns sigdata.tr_builder from the reconstructed TaprootBuilder. These fields were previously left empty, causing incomplete PSBT serialization/deserialization for inputs using hash-lock scripts and for taproot outputs that need the script tree.
Changed components
src/psbt.cppPSBTInput::FromSignatureDataPSBTOutput::FillSignatureDataBitcoin Core PSBT wallet/transaction serializationInspect captured patch +13 / −0
diff --git a/src/psbt.cpp b/src/psbt.cpp
index cfc720b0..3086ae77 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -210,6 +210,18 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata)
for (const auto& [agg_key_lh, psigs] : sigdata.musig2_partial_sigs) {
m_musig2_partial_sigs[agg_key_lh].insert(psigs.begin(), psigs.end());
}
+ for (const auto& [hash, preimage] : sigdata.ripemd160_preimages) {
+ ripemd160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
+ }
+ for (const auto& [hash, preimage] : sigdata.sha256_preimages) {
+ sha256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
+ }
+ for (const auto& [hash, preimage] : sigdata.hash160_preimages) {
+ hash160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
+ }
+ for (const auto& [hash, preimage] : sigdata.hash256_preimages) {
+ hash256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
+ }
}
void PSBTInput::Merge(const PSBTInput& input)
@@ -268,6 +280,7 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
sigdata.tr_spenddata.internal_key = m_tap_internal_key;
sigdata.tr_spenddata.Merge(spenddata);
+ sigdata.tr_builder = builder;
}
for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
Why this scored 32/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.