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.
2251}2253std::unique_ptr<PubkeyProvider> InferXOnlyPubkey(const XOnlyPubKey& xkey, ParseScriptContext ctx, const SigningProvider& provider)2254{2255CPubKey pubkey{xkey.GetEvenCorrespondingCPubKey()};2256std::unique_ptr<PubkeyProvider> key_provider = std::make_unique<ConstPubkeyProvider>(0, pubkey, true);2257KeyOriginInfo info;2258if (provider.GetKeyOriginByXOnly(xkey, info)) {2259return std::make_unique<OriginPubkeyProvider>(0, std::move(info), std::move(key_provider), /*apostrophe=*/false);2260}2261return key_provider;2262}2264/**2265* The context for parsing a Miniscript descriptor (either from Script or from its textual representation).2266*/2267struct KeyParser {2268//! The Key type is an index in DescriptorImpl::m_pubkey_args2269using Key = uint32_t;2270//! Must not be nullptr if parsing from string.2271FlatSigningProvider* m_out;2272//! Must not be nullptr if parsing from Script.2273const SigningProvider* m_in;2274//! List of multipath expanded keys contained in the Miniscript.2275mutable std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> m_keys;2276//! Used to detect key parsing errors within a Miniscript.2277mutable std::string m_key_parsing_error;2278//! The script context we're operating within (Tapscript or P2WSH).2279const miniscript::MiniscriptContext m_script_ctx;2280//! The current key expression index2281uint32_t& m_expr_index;2283KeyParser(FlatSigningProvider* out LIFETIMEBOUND, const SigningProvider* in LIFETIMEBOUND,2284miniscript::MiniscriptContext ctx, uint32_t& key_exp_index LIFETIMEBOUND)2285: m_out(out), m_in(in), m_script_ctx(ctx), m_expr_index(key_exp_index) {}2287bool KeyCompare(const Key& a, const Key& b) const {2288// Deriving a hardened step needs the private key, so use the provider that was filled2289// while parsing, or the one we are inferring from, rather than an empty one.2290const SigningProvider& provider{m_out ? *m_out : (m_in ? *m_in : DUMMY_SIGNING_PROVIDER)};2291const PubkeyProvider& key_a{*m_keys.at(a).at(0)};2292const PubkeyProvider& key_b{*m_keys.at(b).at(0)};2293FlatSigningProvider out_a, out_b;2294const std::optional<CPubKey> pub_a{key_a.GetPubKey(0, provider, out_a)};2295const std::optional<CPubKey> pub_b{key_b.GetPubKey(0, provider, out_b)};2296if (pub_a && pub_b) return *pub_a < *pub_b;2297// Keys that cannot be derived sort before the ones that can, and are compared by their2298// expression so that two different keys are not taken for duplicates.2299if (pub_a.has_value() != pub_b.has_value()) return !pub_a.has_value();2300return key_a.ToString(PubkeyProvider::StringType::PUBLIC) < key_b.ToString(PubkeyProvider::StringType::PUBLIC);2301}2303ParseScriptContext ParseContext() const {2304switch (m_script_ctx) {2305case miniscript::MiniscriptContext::P2WSH: return ParseScriptContext::P2WSH;2306case miniscript::MiniscriptContext::TAPSCRIPT: return ParseScriptContext::P2TR;2307}2308assert(false);2309}2311std::optional<Key> FromString(std::span<const char>& in) const2312{2313assert(m_out);2314Key key = m_keys.size();2315auto pk = ParsePubkey(m_expr_index, in, ParseContext(), *m_out, m_key_parsing_error);2316if (pk.empty()) return {};2317m_keys.emplace_back(std::move(pk));2318return key;2319}2321std::optional<std::string> ToString(const Key& key, bool&) const2322{2323return m_keys.at(key).at(0)->ToString(PubkeyProvider::StringType::PUBLIC);2324}2326template<typename I> std::optional<Key> FromPKBytes(I begin, I end) const2327{2328assert(m_in);2329Key key = m_keys.size();2330if (miniscript::IsTapscript(m_script_ctx) && end - begin == 32) {2331XOnlyPubKey pubkey;2332std::copy(begin, end, pubkey.begin());2333if (auto pubkey_provider = InferXOnlyPubkey(pubkey, ParseContext(), *m_in)) {2334m_keys.emplace_back();2335m_keys.back().push_back(std::move(pubkey_provider));2336return key;2337}2338} else if (!miniscript::IsTapscript(m_script_ctx)) {2339CPubKey pubkey(begin, end);2340if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) {2341m_keys.emplace_back();2342m_keys.back().push_back(std::move(pubkey_provider));2343return key;2344}2345}2346return {};2347}2349template<typename I> std::optional<Key> FromPKHBytes(I begin, I end) const2350{2351assert(end - begin == 20);2352assert(m_in);2353uint160 hash;2354std::copy(begin, end, hash.begin());2355CKeyID keyid(hash);2356CPubKey pubkey;2357if (m_in->GetPubKey(keyid, pubkey)) {2358if (auto pubkey_provider = InferPubkey(pubkey, ParseContext(), *m_in)) {2359Key key = m_keys.size();2360m_keys.emplace_back();2361m_keys.back().push_back(std::move(pubkey_provider));2362return key;2363}2364}2365return {};2366}2368miniscript::MiniscriptContext MsContext() const {2369return m_script_ctx;2370}2371};2373/** Parse a script in a particular context. */2374// NOLINTNEXTLINE(misc-no-recursion)2375std::vector<std::unique_ptr<DescriptorImpl>> ParseScript(uint32_t& key_exp_index, std::span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)2376{2377using namespace script;2378Assume(ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH || ctx == ParseScriptContext::P2TR);2379std::vector<std::unique_ptr<DescriptorImpl>> ret;2380auto expr = Expr(sp);2381if (Func("pk", expr)) {2382auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);2383if (pubkeys.empty()) {2384error = strprintf("pk(): %s", error);2385return {};2386}2387for (auto& pubkey : pubkeys) {2388ret.emplace_back(std::make_unique<PKDescriptor>(std::move(pubkey), ctx == ParseScriptContext::P2TR));2389}2390return ret;2391}2392if ((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && Func("pkh", expr)) {2393auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);2394if (pubkeys.empty()) {2395error = strprintf("pkh(): %s", error);2396return {};2397}2398for (auto& pubkey : pubkeys) {2399ret.emplace_back(std::make_unique<PKHDescriptor>(std::move(pubkey)));2400}2401return ret;2402}2403if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {2404auto pubkeys = ParsePubkey(key_exp_index, expr, ctx, out, error);2405if (pubkeys.empty()) {2406error = strprintf("combo(): %s", error);2407return {};2408}2409for (auto& pubkey : pubkeys) {2410ret.emplace_back(std::make_unique<ComboDescriptor>(std::move(pubkey)));2411}2412return ret;2413} else if (Func("combo", expr)) {2414error = "Can only have combo() at top level";2415return {};2416}2417const bool multi = Func("multi", expr);2418const bool sortedmulti = !multi && Func("sortedmulti", expr);2419const bool multi_a = !(multi || sortedmulti) && Func("multi_a", expr);2420const bool sortedmulti_a = !(multi || sortedmulti || multi_a) && Func("sortedmulti_a", expr);2421if (((ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH) && (multi || sortedmulti)) ||2422(ctx == ParseScriptContext::P2TR && (multi_a || sortedmulti_a))) {2423auto threshold = Expr(expr);2424uint32_t thres;2425std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers; // List of multipath expanded pubkeys2426if (const auto maybe_thres{ToIntegral<uint32_t>(std::string_view{threshold.begin(), threshold.end()})}) {2427thres = *maybe_thres;2428} else {2429error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()));2430return {};2431}2432size_t script_size = 0;2433size_t max_providers_len = 0;2434while (expr.size()) {2435if (!Const(",", expr)) {2436error = strprintf("Multi: expected ',', got '%c'", expr[0]);2437return {};2438}2439auto arg = Expr(expr);2440auto pks = ParsePubkey(key_exp_index, arg, ctx, out, error);2441if (pks.empty()) {2442error = strprintf("Multi: %s", error);2443return {};2444}2445script_size += pks.at(0)->GetSize() + 1;2446max_providers_len = std::max(max_providers_len, pks.size());2447providers.emplace_back(std::move(pks));2448}2449if ((multi || sortedmulti) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTISIG)) {2450error = strprintf("Cannot have %u keys in multisig; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTISIG);2451return {};2452} else if ((multi_a || sortedmulti_a) && (providers.empty() || providers.size() > MAX_PUBKEYS_PER_MULTI_A)) {2453error = strprintf("Cannot have %u keys in multi_a; must have between 1 and %d keys, inclusive", providers.size(), MAX_PUBKEYS_PER_MULTI_A);2454return {};2455} else if (thres < 1) {2456error = strprintf("Multisig threshold cannot be %d, must be at least 1", thres);2457return {};2458} else if (thres > providers.size()) {2459error = strprintf("Multisig threshold cannot be larger than the number of keys; threshold is %d but only %u keys specified", thres, providers.size());2460return {};2461}2462if (ctx == ParseScriptContext::TOP) {2463if (providers.size() > 3) {2464error = strprintf("Cannot have %u pubkeys in bare multisig; only at most 3 pubkeys", providers.size());2465return {};2466}2467}2468if (ctx == ParseScriptContext::P2SH) {2469// This limits the maximum number of compressed pubkeys to 15.2470if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) {2471error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE);2472return {};2473}2474}2476// Make sure all vecs are of the same length, or exactly length 12477// For length 1 vectors, clone key providers until vector is the same length2478for (auto& vec : providers) {2479if (vec.size() == 1) {2480for (size_t i = 1; i < max_providers_len; ++i) {2481vec.emplace_back(vec.at(0)->Clone());2482}2483} else if (vec.size() != max_providers_len) {2484error = strprintf("multi(): Multipath derivation paths have mismatched lengths");2485return {};2486}2487}2489// Build the final descriptors vector2490for (size_t i = 0; i < max_providers_len; ++i) {2491// Build final pubkeys vectors by retrieving the i'th subscript for each vector in subscripts2492std::vector<std::unique_ptr<PubkeyProvider>> pubs;2493pubs.reserve(providers.size());2494for (auto& pub : providers) {2495pubs.emplace_back(std::move(pub.at(i)));2496}2497if (multi || sortedmulti) {2498ret.emplace_back(std::make_unique<MultisigDescriptor>(thres, std::move(pubs), sortedmulti));2499} else {2500ret.emplace_back(std::make_unique<MultiADescriptor>(thres, std::move(pubs), sortedmulti_a));