Inside Bitcoin
Opening the selected file…
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.
2751return {};2752}2753// A signature check is required for a miniscript to be sane. Therefore no sane miniscript2754// may have an empty list of public keys.2755CHECK_NONFATAL(!parser.m_keys.empty());2756// Make sure all vecs are of the same length, or exactly length 12757// For length 1 vectors, clone subdescs until vector is the same length2758size_t num_multipath = std::max_element(parser.m_keys.begin(), parser.m_keys.end(),2759[](const std::vector<std::unique_ptr<PubkeyProvider>>& a, const std::vector<std::unique_ptr<PubkeyProvider>>& b) {2760return a.size() < b.size();2761})->size();2763for (auto& vec : parser.m_keys) {2764if (vec.size() == 1) {2765for (size_t i = 1; i < num_multipath; ++i) {2766vec.emplace_back(vec.at(0)->Clone());2767}2768} else if (vec.size() != num_multipath) {2769error = strprintf("Miniscript: Multipath derivation paths have mismatched lengths");2770return {};2771}2772}2774// Build the final descriptors vector2775for (size_t i = 0; i < num_multipath; ++i) {2776// Build final pubkeys vectors by retrieving the i'th subscript for each vector in subscripts2777std::vector<std::unique_ptr<PubkeyProvider>> pubs;2778pubs.reserve(parser.m_keys.size());2779for (auto& pub : parser.m_keys) {2780pubs.emplace_back(std::move(pub.at(i)));2781}2782ret.emplace_back(std::make_unique<MiniscriptDescriptor>(std::move(pubs), node->Clone()));2783}2784return ret;2785}2786}2787if (ctx == ParseScriptContext::P2SH) {2788error = "A function is needed within P2SH";2789return {};2790} else if (ctx == ParseScriptContext::P2WSH) {2791error = "A function is needed within P2WSH";2792return {};2793}2794error = strprintf("'%s' is not a valid descriptor function", std::string(expr.begin(), expr.end()));2795return {};2796}2798std::unique_ptr<DescriptorImpl> InferMultiA(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)2799{2800auto match = MatchMultiA(script);2801if (!match) return {};2802std::vector<std::unique_ptr<PubkeyProvider>> keys;2803keys.reserve(match->second.size());2804for (const auto keyspan : match->second) {2805if (keyspan.size() != 32) return {};2806auto key = InferXOnlyPubkey(XOnlyPubKey{keyspan}, ctx, provider);2807if (!key) return {};2808keys.push_back(std::move(key));2809}2810return std::make_unique<MultiADescriptor>(match->first, std::move(keys));2811}2813// NOLINTNEXTLINE(misc-no-recursion)2814std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)2815{2816if (ctx == ParseScriptContext::P2TR && script.size() == 34 && script[0] == 32 && script[33] == OP_CHECKSIG) {2817XOnlyPubKey key{std::span{script}.subspan(1, 32)};2818return std::make_unique<PKDescriptor>(InferXOnlyPubkey(key, ctx, provider), true);2819}2821if (ctx == ParseScriptContext::P2TR) {2822auto ret = InferMultiA(script, ctx, provider);2823if (ret) return ret;2824}2826std::vector<std::vector<unsigned char>> data;2827TxoutType txntype = Solver(script, data);2829if (txntype == TxoutType::PUBKEY && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {2830CPubKey pubkey(data[0]);2831if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) {2832return std::make_unique<PKDescriptor>(std::move(pubkey_provider));2833}2834}2835if (txntype == TxoutType::PUBKEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {2836uint160 hash(data[0]);2837CKeyID keyid(hash);2838CPubKey pubkey;2839if (provider.GetPubKey(keyid, pubkey)) {2840if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) {2841return std::make_unique<PKHDescriptor>(std::move(pubkey_provider));2842}2843}2844}2845if (txntype == TxoutType::WITNESS_V0_KEYHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) {2846uint160 hash(data[0]);2847CKeyID keyid(hash);2848CPubKey pubkey;2849if (provider.GetPubKey(keyid, pubkey)) {2850if (auto pubkey_provider = InferPubkey(pubkey, ParseScriptContext::P2WPKH, provider)) {2851return std::make_unique<WPKHDescriptor>(std::move(pubkey_provider));2852}2853}2854}2855if (txntype == TxoutType::MULTISIG && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {2856bool ok = true;2857std::vector<std::unique_ptr<PubkeyProvider>> providers;2858for (size_t i = 1; i + 1 < data.size(); ++i) {2859CPubKey pubkey(data[i]);2860if (auto pubkey_provider = InferPubkey(pubkey, ctx, provider)) {2861providers.push_back(std::move(pubkey_provider));2862} else {2863ok = false;2864break;2865}2866}2867if (ok) return std::make_unique<MultisigDescriptor>((int)data[0][0], std::move(providers));2868}2869if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) {2870uint160 hash(data[0]);2871CScriptID scriptid(hash);2872CScript subscript;2873if (provider.GetCScript(scriptid, subscript)) {2874auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);2875if (sub) return std::make_unique<SHDescriptor>(std::move(sub));2876}2877}2878if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH)) {2879CScriptID scriptid{RIPEMD160(data[0])};2880CScript subscript;2881if (provider.GetCScript(scriptid, subscript)) {2882auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);2883if (sub) return std::make_unique<WSHDescriptor>(std::move(sub));2884}2885}2886if (txntype == TxoutType::WITNESS_V1_TAPROOT && ctx == ParseScriptContext::TOP) {2887// Extract x-only pubkey from output.2888XOnlyPubKey pubkey;2889std::copy(data[0].begin(), data[0].end(), pubkey.begin());2890// Request spending data.2891TaprootSpendData tap;2892if (provider.GetTaprootSpendData(pubkey, tap)) {2893// If found, convert it back to tree form.2894auto tree = InferTaprootTree(tap, pubkey);2895if (tree) {2896// If that works, try to infer subdescriptors for all leaves.2897bool ok = true;2898std::vector<std::unique_ptr<DescriptorImpl>> subscripts; //!< list of script subexpressions2899std::vector<int> depths; //!< depth in the tree of each subexpression (same length subscripts)2900for (const auto& [depth, script, leaf_ver] : *tree) {2901std::unique_ptr<DescriptorImpl> subdesc;2902if (leaf_ver == TAPROOT_LEAF_TAPSCRIPT) {2903subdesc = InferScript(CScript(script.begin(), script.end()), ParseScriptContext::P2TR, provider);2904}2905if (!subdesc) {2906ok = false;2907break;2908} else {2909subscripts.push_back(std::move(subdesc));2910depths.push_back(depth);2911}2912}2913if (ok) {2914auto key = InferXOnlyPubkey(tap.internal_key, ParseScriptContext::P2TR, provider);2915return std::make_unique<TRDescriptor>(std::move(key), std::move(subscripts), std::move(depths));2916}2917}2918}2919// If the above doesn't work, construct a rawtr() descriptor with just the encoded x-only pubkey.2920if (pubkey.IsFullyValid()) {2921auto key = InferXOnlyPubkey(pubkey, ParseScriptContext::P2TR, provider);2922if (key) {2923return std::make_unique<RawTRDescriptor>(std::move(key));2924}2925}2926}2928if (ctx == ParseScriptContext::P2WSH || ctx == ParseScriptContext::P2TR) {2929const auto script_ctx{ctx == ParseScriptContext::P2WSH ? miniscript::MiniscriptContext::P2WSH : miniscript::MiniscriptContext::TAPSCRIPT};2930uint32_t key_exp_index = 0;2931KeyParser parser(/* out = */nullptr, /* in = */&provider, /* ctx = */script_ctx, key_exp_index);2932auto node = miniscript::FromScript(script, parser);2933if (node && node->IsSane()) {2934std::vector<std::unique_ptr<PubkeyProvider>> keys;2935keys.reserve(parser.m_keys.size());2936for (auto& key : parser.m_keys) {2937keys.emplace_back(std::move(key.at(0)));2938}2939return std::make_unique<MiniscriptDescriptor>(std::move(keys), std::move(*node));2940}2941}2943// The following descriptors are all top-level only descriptors.2944// So if we are not at the top level, return early.2945if (ctx != ParseScriptContext::TOP) return nullptr;2947CTxDestination dest;2948if (ExtractDestination(script, dest)) {2949if (GetScriptForDestination(dest) == script) {2950return std::make_unique<AddressDescriptor>(std::move(dest));2951}2952}2954return std::make_unique<RawDescriptor>(script);2955}2958} // namespace2960/** Check a descriptor checksum, and update desc to be the checksum-less part. */2961bool CheckChecksum(std::span<const char>& sp, bool require_checksum, std::string& error, std::string* out_checksum = nullptr)2962{2963auto check_split = Split(sp, '#');2964if (check_split.size() > 2) {2965error = "Multiple '#' symbols";2966return false;2967}2968if (check_split.size() == 1 && require_checksum){2969error = "Missing checksum";2970return false;2971}2972if (check_split.size() == 2) {2973if (check_split[1].size() != 8) {2974error = strprintf("Expected 8 character checksum, not %u characters", check_split[1].size());2975return false;2976}2977}2978auto checksum = DescriptorChecksum(check_split[0]);2979if (checksum.empty()) {2980error = "Invalid characters in payload";2981return false;2982}2983if (check_split.size() == 2) {2984if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) {2985error = strprintf("Provided checksum '%s' does not match computed checksum '%s'", std::string(check_split[1].begin(), check_split[1].end()), checksum);2986return false;2987}2988}2989if (out_checksum) *out_checksum = std::move(checksum);2990sp = check_split[0];2991return true;2992}2994std::vector<std::unique_ptr<Descriptor>> Parse(std::string_view descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)2995{2996std::span<const char> sp{descriptor};2997if (!CheckChecksum(sp, require_checksum, error)) return {};2998uint32_t key_exp_index = 0;2999auto ret = ParseScript(key_exp_index, sp, ParseScriptContext::TOP, out, error);3000if (sp.empty() && !ret.empty()) {