mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
psbt: avoid duplicate taproot leaf script keys when merging
m_tap_scripts maps a leaf script to a set of control blocks, but is serialized one record per control block, keyed by the control block. PSBTInput::Merge unions it by the map key, so two PSBTs that map the same control block to different leaf scripts merge into an input serializing the 0x15 key twice. Duplicate keys are invalid, so combinepsbt hands back a PSBT that can no longer be decoded. Present since #22558 (v24.0). Merge the records instead of the map entries, keeping the leaf script already there, as BIP 174 lets the Combiner pick arbitrarily when conflicts occur. The control blocks already present are collected once rather than searched for per incoming record, which would be quadratic in the size of the PSBTs. Control blocks under a leaf script that both PSBTs carry are now kept as well, where the map level union dropped them. The test covers conflicting and non-conflicting merges, including an incoming leaf script whose control blocks only partly conflict, so records that do not conflict are not dropped alongside those that do.
This commit is contained in:
13
src/psbt.cpp
13
src/psbt.cpp
@@ -14,6 +14,7 @@
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
using common::PSBTError;
|
||||
|
||||
@@ -436,7 +437,17 @@ bool PSBTInput::Merge(const PSBTInput& input)
|
||||
m_proprietary.insert(input.m_proprietary.begin(), input.m_proprietary.end());
|
||||
unknown.insert(input.unknown.begin(), input.unknown.end());
|
||||
m_tap_script_sigs.insert(input.m_tap_script_sigs.begin(), input.m_tap_script_sigs.end());
|
||||
m_tap_scripts.insert(input.m_tap_scripts.begin(), input.m_tap_scripts.end());
|
||||
// Merge by control block, the serialized key (BIP 371), to avoid duplicate keys. Keep the
|
||||
// leaf script already present; BIP 174 lets the Combiner pick arbitrarily on conflict.
|
||||
std::set<std::vector<unsigned char>> seen_control_blocks;
|
||||
for (const auto& [_, control_blocks] : m_tap_scripts) {
|
||||
seen_control_blocks.insert(control_blocks.begin(), control_blocks.end());
|
||||
}
|
||||
for (const auto& [leaf, control_blocks] : input.m_tap_scripts) {
|
||||
for (const auto& control_block : control_blocks) {
|
||||
if (seen_control_blocks.insert(control_block).second) m_tap_scripts[leaf].insert(control_block);
|
||||
}
|
||||
}
|
||||
m_tap_bip32_paths.insert(input.m_tap_bip32_paths.begin(), input.m_tap_bip32_paths.end());
|
||||
|
||||
if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
|
||||
|
||||
Reference in New Issue
Block a user