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) 2021-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 <core_io.h>6#include <hash.h>7#include <key.h>8#include <script/miniscript.h>9#include <script/script.h>10#include <script/signingprovider.h>11#include <test/fuzz/FuzzedDataProvider.h>12#include <test/fuzz/fuzz.h>13#include <test/fuzz/util.h>14#include <test/fuzz/util/descriptor.h>15#include <util/strencodings.h>17#include <algorithm>18#include <optional>20namespace {22using Fragment = miniscript::Fragment;23using Node = miniscript::Node<CPubKey>;24using Type = miniscript::Type;25using MsCtx = miniscript::MiniscriptContext;26using miniscript::operator""_mst;28//! Some pre-computed data for more efficient string roundtrips and to simulate challenges.29struct TestData {30typedef CPubKey Key;32// Precomputed public keys, and a dummy signature for each of them.33std::vector<Key> dummy_keys;34std::map<Key, int> dummy_key_idx_map;35std::map<CKeyID, Key> dummy_keys_map;36std::map<Key, std::pair<std::vector<unsigned char>, bool>> dummy_sigs;37std::map<XOnlyPubKey, std::pair<std::vector<unsigned char>, bool>> schnorr_sigs;39// Precomputed hashes of each kind.40std::vector<std::vector<unsigned char>> sha256;41std::vector<std::vector<unsigned char>> ripemd160;42std::vector<std::vector<unsigned char>> hash256;43std::vector<std::vector<unsigned char>> hash160;44std::map<std::vector<unsigned char>, std::vector<unsigned char>> sha256_preimages;45std::map<std::vector<unsigned char>, std::vector<unsigned char>> ripemd160_preimages;46std::map<std::vector<unsigned char>, std::vector<unsigned char>> hash256_preimages;47std::map<std::vector<unsigned char>, std::vector<unsigned char>> hash160_preimages;49//! Set the precomputed data.50void Init() {51unsigned char keydata[32] = {1};52// All our signatures sign (and are required to sign) this constant message.53constexpr uint256 MESSAGE_HASH{"0000000000000000f5cd94e18b6fe77dd7aca9e35c2b0c9cbd86356c80a71065"};54// We don't pass additional randomness when creating a schnorr signature.55const auto EMPTY_AUX{uint256::ZERO};57for (size_t i = 0; i < 256; i++) {58keydata[31] = i;59CKey privkey;60privkey.Set(keydata, keydata + 32, true);61const Key pubkey = privkey.GetPubKey();63dummy_keys.push_back(pubkey);64dummy_key_idx_map.emplace(pubkey, i);65dummy_keys_map.insert({pubkey.GetID(), pubkey});66XOnlyPubKey xonly_pubkey{pubkey};67dummy_key_idx_map.emplace(xonly_pubkey, i);68uint160 xonly_hash{Hash160(xonly_pubkey)};69dummy_keys_map.emplace(xonly_hash, pubkey);71std::vector<unsigned char> sig, schnorr_sig(64);72privkey.Sign(MESSAGE_HASH, sig);73sig.push_back(1); // SIGHASH_ALL74dummy_sigs.insert({pubkey, {sig, i & 1}});75assert(privkey.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX));76schnorr_sig.push_back(1); // Maximally-sized signature has sighash byte77schnorr_sigs.emplace(XOnlyPubKey{pubkey}, std::make_pair(std::move(schnorr_sig), i & 1));79std::vector<unsigned char> hash;80hash.resize(32);81CSHA256().Write(keydata, 32).Finalize(hash.data());82sha256.push_back(hash);83if (i & 1) sha256_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);84CHash256().Write(keydata).Finalize(hash);85hash256.push_back(hash);86if (i & 1) hash256_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);87hash.resize(20);88CRIPEMD160().Write(keydata, 32).Finalize(hash.data());89assert(hash.size() == 20);90ripemd160.push_back(hash);91if (i & 1) ripemd160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);92CHash160().Write(keydata).Finalize(hash);93hash160.push_back(hash);94if (i & 1) hash160_preimages[hash] = std::vector<unsigned char>(keydata, keydata + 32);95}96}98//! Get the (Schnorr or ECDSA, depending on context) signature for this pubkey.99const std::pair<std::vector<unsigned char>, bool>* GetSig(const MsCtx script_ctx, const Key& key) const {100if (!miniscript::IsTapscript(script_ctx)) {101const auto it = dummy_sigs.find(key);102if (it == dummy_sigs.end()) return nullptr;103return &it->second;104} else {105const auto it = schnorr_sigs.find(XOnlyPubKey{key});106if (it == schnorr_sigs.end()) return nullptr;107return &it->second;108}109}110} TEST_DATA;112/**113* Context to parse a Miniscript node to and from Script or text representation.114* Uses an integer (an index in the dummy keys array from the test data) as keys in order115* to focus on fuzzing the Miniscript nodes' test representation, not the key representation.116*/117struct ParserContext {118typedef CPubKey Key;120const MsCtx script_ctx;122constexpr ParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {}124bool KeyCompare(const Key& a, const Key& b) const {125return a < b;126}128std::optional<std::string> ToString(const Key& key, bool& has_priv_key) const129{130has_priv_key = false;131auto it = TEST_DATA.dummy_key_idx_map.find(key);132if (it == TEST_DATA.dummy_key_idx_map.end()) {133return HexStr(key);134}135has_priv_key = true;136uint8_t idx = it->second;137return HexStr(std::span{&idx, 1});138}140std::vector<unsigned char> ToPKBytes(const Key& key) const {141if (!miniscript::IsTapscript(script_ctx)) {142return {key.begin(), key.end()};143}144const XOnlyPubKey xonly_pubkey{key};145return {xonly_pubkey.begin(), xonly_pubkey.end()};146}148std::vector<unsigned char> ToPKHBytes(const Key& key) const {149if (!miniscript::IsTapscript(script_ctx)) {150const auto h = Hash160(key);151return {h.begin(), h.end()};152}153const auto h = Hash160(XOnlyPubKey{key});154return {h.begin(), h.end()};155}157std::optional<Key> FromString(std::span<const char>& in) const {158if (in.size() != 2) return {};159auto idx = ParseHex(std::string(in.begin(), in.end()));160if (idx.size() != 1) return {};161return TEST_DATA.dummy_keys[idx[0]];162}164template<typename I>165std::optional<Key> FromPKBytes(I first, I last) const {166if (!miniscript::IsTapscript(script_ctx)) {167Key key{first, last};168if (key.IsValid()) return key;169return {};170}171if (last - first != 32) return {};172XOnlyPubKey xonly_pubkey;173std::copy(first, last, xonly_pubkey.begin());174return xonly_pubkey.GetEvenCorrespondingCPubKey();175}177template<typename I>178std::optional<Key> FromPKHBytes(I first, I last) const {179assert(last - first == 20);180CKeyID keyid;181std::copy(first, last, keyid.begin());182const auto it = TEST_DATA.dummy_keys_map.find(keyid);183if (it == TEST_DATA.dummy_keys_map.end()) return {};184return it->second;185}187MsCtx MsContext() const {188return script_ctx;189}190};192//! Context that implements naive conversion from/to script only, for roundtrip testing.193struct ScriptParserContext {194const MsCtx script_ctx;196constexpr ScriptParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {}198//! For Script roundtrip we never need the key from a key hash.199struct Key {200bool is_hash;201std::vector<unsigned char> data;202};204bool KeyCompare(const Key& a, const Key& b) const {205return a.data < b.data;206}208const std::vector<unsigned char>& ToPKBytes(const Key& key) const209{210assert(!key.is_hash);211return key.data;212}214std::vector<unsigned char> ToPKHBytes(const Key& key) const215{216if (key.is_hash) return key.data;217const auto h = Hash160(key.data);218return {h.begin(), h.end()};219}221template<typename I>222std::optional<Key> FromPKBytes(I first, I last) const223{224Key key;225key.data.assign(first, last);226key.is_hash = false;227return key;228}230template<typename I>231std::optional<Key> FromPKHBytes(I first, I last) const232{233Key key;234key.data.assign(first, last);235key.is_hash = true;236return key;237}239MsCtx MsContext() const {240return script_ctx;241}242};244//! Context to produce a satisfaction for a Miniscript node using the pre-computed data.245struct SatisfierContext : ParserContext {247constexpr SatisfierContext(MsCtx ctx) noexcept : ParserContext(ctx) {}249// Timelock challenges satisfaction. Make the value (deterministically) vary to explore different250// paths.
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.