Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2009-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <psbt.h>7#include <common/types.h>8#include <node/types.h>9#include <policy/policy.h>10#include <primitives/transaction.h>11#include <script/signingprovider.h>12#include <util/check.h>13#include <util/result.h>14#include <util/strencodings.h>16#include <algorithm>17#include <set>19using common::PSBTError;21PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx, uint32_t version) : m_version(version)22{23assert(m_version == 0 || m_version == 2);25tx_version = tx.version;26fallback_locktime = tx.nLockTime;27inputs.reserve(tx.vin.size());28for (const CTxIn& input : tx.vin) {29inputs.emplace_back(GetVersion(), input.prevout.hash, input.prevout.n, input.nSequence);30}31outputs.reserve(tx.vout.size());32for (const CTxOut& output : tx.vout) {33outputs.emplace_back(GetVersion(), output.nValue, output.scriptPubKey);34}35}37bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)38{39// Prohibited to merge two PSBTs over different transactions40std::optional<Txid> this_id = GetUniqueID();41std::optional<Txid> psbt_id = psbt.GetUniqueID();42if (!this_id || !psbt_id || this_id != psbt_id) {43return false;44}45if (GetVersion() != psbt.GetVersion()) {46return false;47}49for (unsigned int i = 0; i < inputs.size(); ++i) {50inputs[i].Merge(psbt.inputs[i]);51}52for (unsigned int i = 0; i < outputs.size(); ++i) {53outputs[i].Merge(psbt.outputs[i]);54}55MergeGlobalXPubs(psbt);56if (fallback_locktime == std::nullopt && psbt.fallback_locktime != std::nullopt) fallback_locktime = psbt.fallback_locktime;58// Set m_tx_modifiable only if either PSBT had it set59if (m_tx_modifiable.has_value() || psbt.m_tx_modifiable.has_value()) {60// In general, we AND the modifiable flags61std::bitset<8> this_modifiable = m_tx_modifiable.value_or(0);62std::bitset<8> psbt_modifiable = psbt.m_tx_modifiable.value_or(0);63std::bitset<8> final_modifiable = this_modifiable & psbt_modifiable;64// SIGHASH_SINGLE Modifiable (bit 2) needs to be bitwise OR'd65final_modifiable.set(2, this_modifiable[2] || psbt_modifiable[2]);67m_tx_modifiable = final_modifiable;68}70m_proprietary.insert(psbt.m_proprietary.begin(), psbt.m_proprietary.end());71unknown.insert(psbt.unknown.begin(), psbt.unknown.end());73return true;74}76void PartiallySignedTransaction::MergeGlobalXPubs(const PartiallySignedTransaction& psbt)77{78for (const auto& [origin, xpubs] : psbt.m_xpubs) {79for (const CExtPubKey& xpub : xpubs) {80const bool known{std::ranges::any_of(m_xpubs, [&](const auto& entry) { return entry.second.contains(xpub); })};81if (!known) m_xpubs[origin].insert(xpub);82}83}84}86std::optional<uint32_t> PartiallySignedTransaction::ComputeTimeLock() const87{88if (GetVersion() >= 2) {89std::optional<uint32_t> time_lock{0};90std::optional<uint32_t> height_lock{0};91for (const PSBTInput& input : inputs) {92if (input.time_locktime.has_value() && !input.height_locktime.has_value()) {93height_lock.reset(); // Transaction can no longer have a height locktime94if (!time_lock.has_value()) {95return std::nullopt;96}97} else if (!input.time_locktime.has_value() && input.height_locktime.has_value()) {98time_lock.reset(); // Transaction can no longer have a time locktime99if (!height_lock.has_value()) {100return std::nullopt;101}102}103if (input.time_locktime && time_lock.has_value()) {104time_lock = std::max(time_lock, input.time_locktime);105}106if (input.height_locktime && height_lock.has_value()) {107height_lock = std::max(height_lock, input.height_locktime);108}109}110if (height_lock.has_value() && *height_lock > 0) {111return *height_lock;112}113if (time_lock.has_value() && *time_lock > 0) {114return *time_lock;115}116}117return fallback_locktime.value_or(0);118}120std::optional<CMutableTransaction> PartiallySignedTransaction::GetUnsignedTx() const121{122CMutableTransaction mtx;123mtx.version = tx_version;124std::optional<uint32_t> locktime = ComputeTimeLock();125if (!locktime) {126return std::nullopt;127}128mtx.nLockTime = *locktime;129uint32_t max_sequence = CTxIn::SEQUENCE_FINAL;130for (const PSBTInput& input : inputs) {131CTxIn txin;132txin.prevout.hash = input.prev_txid;133txin.prevout.n = input.prev_out;134txin.nSequence = input.sequence.value_or(max_sequence);135mtx.vin.push_back(txin);136}137for (const PSBTOutput& output : outputs) {138CTxOut txout;139txout.nValue = output.amount;140txout.scriptPubKey = output.script;141mtx.vout.push_back(txout);142}143return mtx;144}146std::optional<Txid> PartiallySignedTransaction::GetUniqueID() const147{148// Get the unsigned transaction149std::optional<CMutableTransaction> mtx = GetUnsignedTx();150if (!mtx) {151return std::nullopt;152}153if (GetVersion() >= 2) {154for (CTxIn& txin : mtx->vin) {155txin.nSequence = 0;156}157}158return mtx->GetHash();159}161bool PartiallySignedTransaction::AddInput(const PSBTInput& psbtin)162{163// The input being added must be for this PSBT's version164if (psbtin.GetVersion() != GetVersion()) {165return false;166}168// Prevent duplicate inputs169if (std::find_if(inputs.begin(), inputs.end(),170[psbtin](const PSBTInput& psbt) {171return psbt.prev_txid == psbtin.prev_txid && psbt.prev_out == psbtin.prev_out;172}173) != inputs.end()) {174return false;175}177if (GetVersion() < 2) {178// This is a v0 psbt, so do the v0 AddInput179inputs.push_back(psbtin);180inputs.back().partial_sigs.clear();181inputs.back().final_script_sig.clear();182inputs.back().final_script_witness.SetNull();183return true;184}186// Check inputs modifiable flag187if (!m_tx_modifiable.has_value() || !m_tx_modifiable->test(0)) {188return false;189}191// Determine if we need to iterate the inputs.192// For now, we only do this if the new input has a required time lock.193// BIP 370 states that we should also do this if m_tx_modifiable's bit 2 is set194// (Has SIGHASH_SINGLE flag) but since we are only adding inputs at the end of the vector,195// we don't care about that.196bool iterate_inputs = psbtin.time_locktime != std::nullopt || psbtin.height_locktime != std::nullopt;197if (iterate_inputs) {198std::optional<uint32_t> old_timelock = ComputeTimeLock();199if (!old_timelock) {200return false;201}203std::optional<uint32_t> time_lock = psbtin.time_locktime;204std::optional<uint32_t> height_lock = psbtin.height_locktime;205bool has_sigs = false;206for (const PSBTInput& input : inputs) {207if (input.time_locktime.has_value() && !input.height_locktime.has_value()) {208height_lock.reset(); // Transaction can no longer have a height locktime209if (time_lock == std::nullopt) {210return false;211}212} else if (!input.time_locktime.has_value() && input.height_locktime.has_value()) {213time_lock.reset(); // Transaction can no longer have a time locktime214if (height_lock == std::nullopt) {215return false;216}217}218if (input.time_locktime && time_lock.has_value()) {219time_lock = std::max(time_lock, input.time_locktime);220}221if (input.height_locktime && height_lock.has_value()) {222height_lock = std::max(height_lock, input.height_locktime);223}224if (input.HasSignatures()) {225has_sigs = true;226}227}228uint32_t new_timelock = fallback_locktime.value_or(0);229if (height_lock.has_value() && *height_lock > 0) {230new_timelock = *height_lock;231} else if (time_lock.has_value() && *time_lock > 0) {232new_timelock = *time_lock;233}234if (has_sigs && *old_timelock != new_timelock) {235return false;236}237}239// Add the input to the end240inputs.push_back(psbtin);241return true;242}244bool PartiallySignedTransaction::AddOutput(const PSBTOutput& psbtout)245{246// The output being added must be for this PSBT's version247if (psbtout.GetVersion() != GetVersion()) {248return false;249}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.