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) 2022 The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or https://www.opensource.org/licenses/mit-license.php.5#include <addresstype.h>6#include <key.h>7#include <psbt.h>8#include <script/descriptor.h>9#include <script/script.h>10#include <script/signingprovider.h>11#include <script/solver.h>12#include <test/util/setup_common.h>13#include <util/strencodings.h>14#include <util/string.h>16#include <boost/test/unit_test.hpp>18#include <string>19#include <vector>21BOOST_FIXTURE_TEST_SUITE(psbt_tests, BasicTestingSetup)23static PSBTProprietary MakeProprietary(uint64_t subtype, uint8_t key_data, uint8_t value)24{25return PSBTProprietary{26.subtype = subtype,27.identifier = {'p', 's', 'b', 't'},28.key = {key_data},29.value = {value},30};31}33void CheckTimeLock(const std::string& base64_psbt, std::optional<uint32_t> timelock)34{35util::Result<PartiallySignedTransaction> psbt = DecodeBase64PSBT(base64_psbt);36BOOST_CHECK(psbt);38std::optional<uint32_t> computed_timelock = psbt->ComputeTimeLock();39std::optional<CMutableTransaction> tx = psbt->GetUnsignedTx();40if (timelock) {41BOOST_CHECK(computed_timelock);42BOOST_CHECK_EQUAL(*computed_timelock, *timelock);43BOOST_CHECK(tx);44BOOST_CHECK_EQUAL(tx->nLockTime, *timelock);45} else {46BOOST_CHECK(!computed_timelock);47BOOST_CHECK(!tx);48}49}51BOOST_AUTO_TEST_CASE(psbt2_timelock_test)52{53CheckTimeLock("cHNidP8BAgQCAAAAAQQBAQEFAQIB+wQCAAAAAAEOIAsK2SFBnByHGXNdctxzn56p4GONH+TB7vD5lECEgV/IAQ8EAAAAAAABAwgACK8vAAAAAAEEFgAUxDD2TEdW2jENvRoIVXLvKZkmJywAAQMIi73rCwAAAAABBBYAFE3Rk6yWSlasG54cyoRU/i9HT4UTAA==", 0);54CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 0);55CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 10000);56CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAESBCgjAAAAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA==", 10000);57CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 10000);58CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiARIEKCMAAAABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 10000);59CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIBEgQoIwAAAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 1657048460);60CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEQSLjcRiARIEECcAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiAAEDCE+TNXcAAAAAAQQWABQLE1LKzQPPaqG388jWOIZxs0peEQA=", 1657048460);61CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAAAAQ4gOhs7PIN9ZInqejHY5sfdUDwAG+8+BpWOdXSAjWjKeKUBDwQAAAAAAREEjI3EYgABAwhPkzV3AAAAAAEEFgAUCxNSys0Dz2qht/PI1jiGcbNKXhEA", 1657048460);62CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiAPdY2/vU2nwWyKMwnDyB4RAPVh6mRttbAXUsSF4b3enwEPBAEAAAABEgQQJwAAAAEOIDobOzyDfWSJ6nox2ObH3VA8ABvvPgaVjnV0gI1oynilAQ8EAAAAAAERBIyNxGIAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA==", std::nullopt);63CheckTimeLock("cHNidP8BAgQCAAAAAQMEAAAAAAEEAQIBBQEBAfsEAgAAAAABDiA6Gzs8g31kiep6Mdjmx91QPAAb7z4GlY51dICNaMp4pQEPBAAAAAABEQSMjcRiAAEOIA91jb+9TafBbIozCcPIHhEA9WHqZG21sBdSxIXhvd6fAQ8EAQAAAAESBBAnAAAAAQMIT5M1dwAAAAABBBYAFAsTUsrNA89qobfzyNY4hnGzSl4RAA==", std::nullopt);64}66BOOST_AUTO_TEST_CASE(psbt2_addinput)67{68FastRandomContext rng(/*fDeterministic=*/true);70CMutableTransaction mtx;71PartiallySignedTransaction psbt(mtx, /*version=*/2);72psbt.m_tx_modifiable.emplace();73psbt.m_tx_modifiable->set(0, true);74BOOST_CHECK_EQUAL(psbt.inputs.size(), 0);76// Same PSBT version is required77uint256 txid;78rng.fillrand(MakeWritableByteSpan(txid));79PSBTInput psbtin_v0(/*psbt_version=*/0, Txid::FromUint256(txid), /*prev_out=*/0);80BOOST_CHECK(!psbt.AddInput(psbtin_v0));81BOOST_CHECK_EQUAL(psbt.inputs.size(), 0);82rng.fillrand(MakeWritableByteSpan(txid));83PSBTInput psbtin(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);84BOOST_CHECK(psbt.AddInput(psbtin));85BOOST_CHECK_EQUAL(psbt.inputs.size(), 1);87// Duplicates are not allowed88BOOST_CHECK(!psbt.AddInput(psbtin));89BOOST_CHECK_EQUAL(psbt.inputs.size(), 1);91// Input with a unique txid is allowed92rng.fillrand(MakeWritableByteSpan(txid));93PSBTInput psbtin2(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);94BOOST_CHECK(psbt.AddInput(psbtin2));95BOOST_CHECK_EQUAL(psbt.inputs.size(), 2);97// Disabling inputs modifiable flag prevents adding new inputs98psbt.m_tx_modifiable->set(0, false);99rng.fillrand(MakeWritableByteSpan(txid));100PSBTInput psbtin3(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);101BOOST_CHECK(!psbt.AddInput(psbtin3));102BOOST_CHECK_EQUAL(psbt.inputs.size(), 2);103psbt.m_tx_modifiable->set(0, true);105// Make sure that timelock compatibility checks are working106// No previous required timelocks, new input with both height and time timelocks is allowed107rng.fillrand(MakeWritableByteSpan(txid));108PSBTInput psbtin4(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);109psbtin4.time_locktime = LOCKTIME_THRESHOLD;110psbtin4.height_locktime = 100;111BOOST_CHECK(psbt.AddInput(psbtin4));112BOOST_CHECK_EQUAL(psbt.inputs.size(), 3);114// Input with only a time timelock is allowed115rng.fillrand(MakeWritableByteSpan(txid));116PSBTInput psbtin5(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);117psbtin5.time_locktime = LOCKTIME_THRESHOLD + 1;118BOOST_CHECK(psbt.AddInput(psbtin5));119BOOST_CHECK_EQUAL(psbt.inputs.size(), 4);121// Input with only a height timelock is not allowed because of previous122rng.fillrand(MakeWritableByteSpan(txid));123PSBTInput psbtin6(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);124psbtin6.height_locktime = 100;125BOOST_CHECK(!psbt.AddInput(psbtin6));126BOOST_CHECK_EQUAL(psbt.inputs.size(), 4);128// Adding an input that already has a signature is allowed129rng.fillrand(MakeWritableByteSpan(txid));130PSBTInput psbtin7(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);131psbtin7.final_script_sig << OP_1;132BOOST_CHECK(psbt.AddInput(psbtin7));133BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);135// Same thing, but with other things that have signatures136psbtin7.final_script_sig.clear();137psbtin7.final_script_witness.stack.emplace_back();138BOOST_CHECK(!psbt.AddInput(psbtin7));139BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);140psbtin7.final_script_witness.SetNull();141psbtin7.partial_sigs.emplace();142BOOST_CHECK(!psbt.AddInput(psbtin7));143BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);144psbtin7.partial_sigs.clear();145psbtin7.m_tap_key_sig.push_back(0);146BOOST_CHECK(!psbt.AddInput(psbtin7));147BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);148psbtin7.m_tap_key_sig.clear();149psbtin7.m_tap_script_sigs.emplace();150BOOST_CHECK(!psbt.AddInput(psbtin7));151BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);152psbtin7.m_tap_script_sigs.clear();153psbtin7.m_musig2_partial_sigs.emplace();154BOOST_CHECK(!psbt.AddInput(psbtin7));155BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);157// Adding an input that changes the timelock is no longer allowed158rng.fillrand(MakeWritableByteSpan(txid));159PSBTInput psbtin8(/*psbt_version=*/2, Txid::FromUint256(txid), /*prev_out=*/0);160psbtin8.time_locktime = LOCKTIME_THRESHOLD + 2;161BOOST_CHECK(!psbt.AddInput(psbtin8));162BOOST_CHECK_EQUAL(psbt.inputs.size(), 5);163}165BOOST_AUTO_TEST_CASE(psbt2_addoutput)166{167CMutableTransaction mtx;168PartiallySignedTransaction psbt(mtx, /*version=*/2);169psbt.m_tx_modifiable.emplace();170psbt.m_tx_modifiable->set(1, true);171BOOST_CHECK_EQUAL(psbt.outputs.size(), 0);173// Same PSBT version is required174PSBTOutput psbtout_v0(/*psbt_version=*/0, /*amount=*/1, CScript());175BOOST_CHECK(!psbt.AddOutput(psbtout_v0));176BOOST_CHECK_EQUAL(psbt.outputs.size(), 0);177PSBTOutput psbtout(/*psbt_version=*/2, /*amount=*/1, CScript());178BOOST_CHECK(psbt.AddOutput(psbtout));179BOOST_CHECK_EQUAL(psbt.outputs.size(), 1);181// Disabling outputs modifiable flag prevents adding new outputs182psbt.m_tx_modifiable->set(1, false);183PSBTOutput psbtout2(/*psbt_version=*/2, /*amount=*/1, CScript());184BOOST_CHECK(!psbt.AddOutput(psbtout2));185BOOST_CHECK_EQUAL(psbt.outputs.size(), 1);186psbt.m_tx_modifiable->set(1, true);187PSBTOutput psbtout3(/*psbt_version=*/2, /*amount=*/1, CScript());188BOOST_CHECK(psbt.AddOutput(psbtout3));189BOOST_CHECK_EQUAL(psbt.outputs.size(), 2);190}192BOOST_AUTO_TEST_CASE(merge_proprietary_fields)193{194CMutableTransaction tx;195tx.vin.emplace_back(COutPoint{});196tx.vout.emplace_back(0, CScript{});198PartiallySignedTransaction left(tx);199PartiallySignedTransaction right(tx);201const auto left_prop = MakeProprietary(/*subtype=*/1, /*key_data=*/0x01, /*value=*/0xaa);202const auto right_prop = MakeProprietary(/*subtype=*/2, /*key_data=*/0x02, /*value=*/0xbb);204left.m_proprietary.insert(left_prop);205left.inputs[0].m_proprietary.insert(left_prop);206left.outputs[0].m_proprietary.insert(left_prop);208right.m_proprietary.insert(right_prop);209right.inputs[0].m_proprietary.insert(right_prop);210right.outputs[0].m_proprietary.insert(right_prop);212BOOST_REQUIRE(left.Merge(right));214BOOST_REQUIRE_EQUAL(left.m_proprietary.size(), 2U);215BOOST_REQUIRE_EQUAL(left.inputs[0].m_proprietary.size(), 2U);216BOOST_REQUIRE_EQUAL(left.outputs[0].m_proprietary.size(), 2U);218const auto global_it = left.m_proprietary.find(right_prop);219BOOST_REQUIRE(global_it != left.m_proprietary.end());220BOOST_CHECK(global_it->value == right_prop.value);222const auto input_it = left.inputs[0].m_proprietary.find(right_prop);223BOOST_REQUIRE(input_it != left.inputs[0].m_proprietary.end());224BOOST_CHECK(input_it->value == right_prop.value);226const auto output_it = left.outputs[0].m_proprietary.find(right_prop);227BOOST_REQUIRE(output_it != left.outputs[0].m_proprietary.end());228BOOST_CHECK(output_it->value == right_prop.value);229}231struct PSBTOutputTest {232CPubKey pubkey;233FlatSigningProvider provider;234CScript script_pubkey;236explicit PSBTOutputTest(std::string descriptor)237{238CKey key{GenerateRandomKey()};239pubkey = key.GetPubKey();240provider.keys.emplace(pubkey.GetID(), key);242util::ReplaceAll(descriptor, "<KEY>", HexStr(pubkey));243std::string error;244auto descriptors{Parse(descriptor, provider, error, /*require_checksum=*/false)};245BOOST_REQUIRE_MESSAGE(!descriptors.empty(), error);246std::vector<CScript> output_scripts;247BOOST_REQUIRE(descriptors[0]->Expand(/*pos=*/0, provider, output_scripts, provider));248BOOST_REQUIRE_EQUAL(output_scripts.size(), 1);249script_pubkey = output_scripts[0];250}
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.