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) 2019-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 <node/psbt.h>6#include <psbt.h>7#include <pubkey.h>8#include <script/script.h>9#include <streams.h>10#include <test/fuzz/FuzzedDataProvider.h>11#include <test/fuzz/fuzz.h>12#include <test/util/random.h>13#include <util/check.h>15#include <cstdint>16#include <optional>17#include <string>18#include <vector>20using node::AnalyzePSBT;21using node::PSBTAnalysis;22using node::PSBTInputAnalysis;24FUZZ_TARGET(psbt)25{26SeedRandomStateForTest(SeedRand::ZEROS);27FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};28auto str = fuzzed_data_provider.ConsumeRandomLengthString();29util::Result<PartiallySignedTransaction> psbt_res = DecodeRawPSBT(MakeByteSpan(str));30if (!psbt_res) {31return;32}33PartiallySignedTransaction psbt_mut = *psbt_res;34const PartiallySignedTransaction psbt = psbt_mut;36// We are on purpose not forward compatible, and version 1 is disabled.37const auto psbt_version{psbt.GetVersion()};38Assert(psbt_version == 0 || psbt_version == 2);40// A PSBT must roundtrip.41std::vector<uint8_t> psbt_ser;42VectorWriter{psbt_ser, 0, psbt};43SpanReader reader{psbt_ser};44PartiallySignedTransaction psbt_roundtrip(deserialize, reader);46// And be stable across roundtrips.47std::vector<uint8_t> roundtrip_ser;48VectorWriter{roundtrip_ser, 0, psbt_roundtrip};49Assert(psbt_ser == roundtrip_ser);51const PSBTAnalysis analysis = AnalyzePSBT(psbt);52(void)PSBTRoleName(analysis.next);53for (const PSBTInputAnalysis& input_analysis : analysis.inputs) {54(void)PSBTRoleName(input_analysis.next);55}57(void)psbt.GetUnsignedTx();59for (const PSBTInput& input : psbt.inputs) {60(void)PSBTInputSigned(input);61PSBTInput input_mod = input;62CTxOut tx_out;63if (input.GetUTXO(tx_out)) {64(void)tx_out.IsNull();65(void)tx_out.ToString();66}67// A PSBT input must roundtrip to signature data.68PSBTInput input_fill{psbt_version, input_mod.prev_txid, input_mod.prev_out, input_mod.sequence};69SignatureData sig_data;70input_mod.FillSignatureData(sig_data);71input_fill.FromSignatureData(sig_data);73// Only final_script_sig and final_script_witness are filled when sigdata is complete74if (sig_data.complete) {75Assert(input_mod.final_script_sig == input_fill.final_script_sig);76Assert(input_mod.final_script_witness == input_fill.final_script_witness);77} else {78// UTXOs don't go into SignatureData79input_mod.non_witness_utxo.reset();80input_mod.witness_utxo.SetNull();81// Sighash type doesn't go into SignatureData82input_mod.sighash_type.reset();83// Timelocks don't go into SignatureData84input_mod.time_locktime.reset();85input_mod.height_locktime.reset();86// Proprietary fields are not included in SignatureData87input_mod.m_proprietary.clear();88// Unknown fields are not included in SignatureData89input_mod.unknown.clear();91Assert(input_mod == input_fill);92}93}94(void)CountPSBTUnsignedInputs(psbt);96for (const PSBTOutput& output : psbt.outputs) {97PSBTOutput output_mod = output;98// A PSBT output must roundtrip to signature data.99PSBTOutput output_fill{psbt_version, output_mod.amount, output_mod.script};100SignatureData sig_data;101output_mod.FillSignatureData(sig_data);102output_fill.FromSignatureData(sig_data);104// FillSignatureData will not fill tap tree or internal key if the tree is empty or105// the key is not fully valid. These need to be cleared before checking for equivalence106if (output_mod.m_tap_tree.empty() || !output_mod.m_tap_internal_key.IsFullyValid()) {107output_mod.m_tap_tree.clear();108std::fill(output_mod.m_tap_internal_key.begin(), output_mod.m_tap_internal_key.end(), 0);109}110// Sort m_tap_tree to ensure the vectors match111std::sort(output_mod.m_tap_tree.begin(), output_mod.m_tap_tree.end());112std::sort(output_fill.m_tap_tree.begin(), output_fill.m_tap_tree.end());113// Proprietary fields are not included in SignatureData114output_mod.m_proprietary.clear();115// Unknown fields are not included in SignatureData116output_mod.unknown.clear();118Assert(output_mod.m_tap_internal_key == output_fill.m_tap_internal_key);119Assert(output_mod == output_fill);120}122psbt_mut = psbt;123(void)FinalizePSBT(psbt_mut);125psbt_mut = psbt;126CMutableTransaction result;127if (FinalizeAndExtractPSBT(psbt_mut, result)) {128const PartiallySignedTransaction psbt_from_tx{result};129}131PartiallySignedTransaction psbt_merge = psbt;132str = fuzzed_data_provider.ConsumeRandomLengthString();133util::Result<PartiallySignedTransaction> psbt_merge_res = DecodeRawPSBT(MakeByteSpan(str));134if (psbt_merge_res) {135psbt_merge = *psbt_merge_res;136}137psbt_mut = psbt;138(void)psbt_mut.Merge(psbt_merge);139psbt_mut = psbt;140std::optional<PartiallySignedTransaction> comb_res = CombinePSBTs({psbt_mut, psbt_merge});141if (comb_res) {142psbt_mut = *comb_res;143}144for (const auto& psbt_in : psbt_merge.inputs) {145(void)psbt_mut.AddInput(psbt_in);146}147for (const auto& psbt_out : psbt_merge.outputs) {148(void)psbt_mut.AddOutput(psbt_out);149}150psbt_mut.unknown.insert(psbt_merge.unknown.begin(), psbt_merge.unknown.end());152RemoveUnnecessaryTransactions(psbt_mut);153}
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.