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.
751[](DummyState, const Node&, size_t) { return DummyState{}; },752[&upfn](DummyState, const Node& node, std::span<Result> subs) {753Result res{upfn(node, subs)};754return std::optional<Result>(std::move(res));755}756));757}759/** Compare two miniscript subtrees, using a non-recursive algorithm. */760friend int Compare(const Node<Key>& node1, const Node<Key>& node2)761{762std::vector<std::pair<const Node<Key>&, const Node<Key>&>> queue;763queue.emplace_back(node1, node2);764while (!queue.empty()) {765const auto& [a, b] = queue.back();766queue.pop_back();767if (std::tie(a.fragment, a.k, a.keys, a.data) < std::tie(b.fragment, b.k, b.keys, b.data)) return -1;768if (std::tie(b.fragment, b.k, b.keys, b.data) < std::tie(a.fragment, a.k, a.keys, a.data)) return 1;769if (a.subs.size() < b.subs.size()) return -1;770if (b.subs.size() < a.subs.size()) return 1;771size_t n = a.subs.size();772for (size_t i = 0; i < n; ++i) {773queue.emplace_back(a.subs[n - 1 - i], b.subs[n - 1 - i]);774}775}776return 0;777}779//! Compute the type for this miniscript.780Type CalcType() const {781using namespace internal;783// THRESH has a variable number of subexpressions784std::vector<Type> sub_types;785if (fragment == Fragment::THRESH) {786for (const auto& sub : subs) sub_types.push_back(sub.GetType());787}788// All other nodes than THRESH can be computed just from the types of the 0-3 subexpressions.789Type x = subs.size() > 0 ? subs[0].GetType() : ""_mst;790Type y = subs.size() > 1 ? subs[1].GetType() : ""_mst;791Type z = subs.size() > 2 ? subs[2].GetType() : ""_mst;793return SanitizeType(ComputeType(fragment, x, y, z, sub_types, k, data.size(), subs.size(), keys.size(), m_script_ctx));794}796public:797template<typename Ctx>798CScript ToScript(const Ctx& ctx) const799{800// To construct the CScript for a Miniscript object, we use the TreeEval algorithm.801// The State is a boolean: whether or not the node's script expansion is followed802// by an OP_VERIFY (which may need to be combined with the last script opcode).803auto downfn = [](bool verify, const Node& node, size_t index) {804// For WRAP_V, the subexpression is certainly followed by OP_VERIFY.805if (node.fragment == Fragment::WRAP_V) return true;806// The subexpression of WRAP_S, and the last subexpression of AND_V807// inherit the followed-by-OP_VERIFY property from the parent.808if (node.fragment == Fragment::WRAP_S ||809(node.fragment == Fragment::AND_V && index == 1)) return verify;810return false;811};812// The upward function computes for a node, given its followed-by-OP_VERIFY status813// and the CScripts of its child nodes, the CScript of the node.814const bool is_tapscript{IsTapscript(m_script_ctx)};815auto upfn = [&ctx, is_tapscript](bool verify, const Node& node, std::span<CScript> subs) -> CScript {816switch (node.fragment) {817case Fragment::PK_K: return BuildScript(ctx.ToPKBytes(node.keys[0]));818case Fragment::PK_H: return BuildScript(OP_DUP, OP_HASH160, ctx.ToPKHBytes(node.keys[0]), OP_EQUALVERIFY);819case Fragment::OLDER: return BuildScript(node.k, OP_CHECKSEQUENCEVERIFY);820case Fragment::AFTER: return BuildScript(node.k, OP_CHECKLOCKTIMEVERIFY);821case Fragment::SHA256: return BuildScript(OP_SIZE, 32, OP_EQUALVERIFY, OP_SHA256, node.data, verify ? OP_EQUALVERIFY : OP_EQUAL);822case Fragment::RIPEMD160: return BuildScript(OP_SIZE, 32, OP_EQUALVERIFY, OP_RIPEMD160, node.data, verify ? OP_EQUALVERIFY : OP_EQUAL);823case Fragment::HASH256: return BuildScript(OP_SIZE, 32, OP_EQUALVERIFY, OP_HASH256, node.data, verify ? OP_EQUALVERIFY : OP_EQUAL);824case Fragment::HASH160: return BuildScript(OP_SIZE, 32, OP_EQUALVERIFY, OP_HASH160, node.data, verify ? OP_EQUALVERIFY : OP_EQUAL);825case Fragment::WRAP_A: return BuildScript(OP_TOALTSTACK, subs[0], OP_FROMALTSTACK);826case Fragment::WRAP_S: return BuildScript(OP_SWAP, subs[0]);827case Fragment::WRAP_C: return BuildScript(std::move(subs[0]), verify ? OP_CHECKSIGVERIFY : OP_CHECKSIG);828case Fragment::WRAP_D: return BuildScript(OP_DUP, OP_IF, subs[0], OP_ENDIF);829case Fragment::WRAP_V: {830if (node.subs[0].GetType() << "x"_mst) {831return BuildScript(std::move(subs[0]), OP_VERIFY);832} else {833return std::move(subs[0]);834}835}836case Fragment::WRAP_J: return BuildScript(OP_SIZE, OP_0NOTEQUAL, OP_IF, subs[0], OP_ENDIF);837case Fragment::WRAP_N: return BuildScript(std::move(subs[0]), OP_0NOTEQUAL);838case Fragment::JUST_1: return BuildScript(OP_1);839case Fragment::JUST_0: return BuildScript(OP_0);840case Fragment::AND_V: return BuildScript(std::move(subs[0]), subs[1]);841case Fragment::AND_B: return BuildScript(std::move(subs[0]), subs[1], OP_BOOLAND);842case Fragment::OR_B: return BuildScript(std::move(subs[0]), subs[1], OP_BOOLOR);843case Fragment::OR_D: return BuildScript(std::move(subs[0]), OP_IFDUP, OP_NOTIF, subs[1], OP_ENDIF);844case Fragment::OR_C: return BuildScript(std::move(subs[0]), OP_NOTIF, subs[1], OP_ENDIF);845case Fragment::OR_I: return BuildScript(OP_IF, subs[0], OP_ELSE, subs[1], OP_ENDIF);846case Fragment::ANDOR: return BuildScript(std::move(subs[0]), OP_NOTIF, subs[2], OP_ELSE, subs[1], OP_ENDIF);847case Fragment::MULTI: {848CHECK_NONFATAL(!is_tapscript);849CScript script = BuildScript(node.k);850for (const auto& key : node.keys) {851script = BuildScript(std::move(script), ctx.ToPKBytes(key));852}853return BuildScript(std::move(script), node.keys.size(), verify ? OP_CHECKMULTISIGVERIFY : OP_CHECKMULTISIG);854}855case Fragment::MULTI_A: {856CHECK_NONFATAL(is_tapscript);857CScript script = BuildScript(ctx.ToPKBytes(*node.keys.begin()), OP_CHECKSIG);858for (auto it = node.keys.begin() + 1; it != node.keys.end(); ++it) {859script = BuildScript(std::move(script), ctx.ToPKBytes(*it), OP_CHECKSIGADD);860}861return BuildScript(std::move(script), node.k, verify ? OP_NUMEQUALVERIFY : OP_NUMEQUAL);862}863case Fragment::THRESH: {864CScript script = std::move(subs[0]);865for (size_t i = 1; i < subs.size(); ++i) {866script = BuildScript(std::move(script), subs[i], OP_ADD);867}868return BuildScript(std::move(script), node.k, verify ? OP_EQUALVERIFY : OP_EQUAL);869}870}871assert(false);872};873return TreeEval<CScript>(false, downfn, upfn);874}876template<typename CTx>877std::optional<std::string> ToString(const CTx& ctx) const {878bool dummy{false};879return ToString(ctx, dummy);880}882template<typename CTx>883std::optional<std::string> ToString(const CTx& ctx, bool& has_priv_key) const {884// To construct the std::string representation for a Miniscript object, we use885// the TreeEvalMaybe algorithm. The State is a boolean: whether the parent node is a886// wrapper. If so, non-wrapper expressions must be prefixed with a ":".887auto downfn = [](bool, const Node& node, size_t) {888return (node.fragment == Fragment::WRAP_A || node.fragment == Fragment::WRAP_S ||889node.fragment == Fragment::WRAP_D || node.fragment == Fragment::WRAP_V ||890node.fragment == Fragment::WRAP_J || node.fragment == Fragment::WRAP_N ||891node.fragment == Fragment::WRAP_C ||892(node.fragment == Fragment::AND_V && node.subs[1].fragment == Fragment::JUST_1) ||893(node.fragment == Fragment::OR_I && node.subs[0].fragment == Fragment::JUST_0) ||894(node.fragment == Fragment::OR_I && node.subs[1].fragment == Fragment::JUST_0));895};896auto toString = [&ctx, &has_priv_key](Key key) -> std::optional<std::string> {897bool fragment_has_priv_key{false};898auto key_str{ctx.ToString(key, fragment_has_priv_key)};899if (key_str) has_priv_key = has_priv_key || fragment_has_priv_key;900return key_str;901};902// The upward function computes for a node, given whether its parent is a wrapper,903// and the string representations of its child nodes, the string representation of the node.904const bool is_tapscript{IsTapscript(m_script_ctx)};905auto upfn = [is_tapscript, &toString](bool wrapped, const Node& node, std::span<std::string> subs) -> std::optional<std::string> {906std::string ret = wrapped ? ":" : "";908switch (node.fragment) {909case Fragment::WRAP_A: return "a" + std::move(subs[0]);910case Fragment::WRAP_S: return "s" + std::move(subs[0]);911case Fragment::WRAP_C:912if (node.subs[0].fragment == Fragment::PK_K) {913// pk(K) is syntactic sugar for c:pk_k(K)914auto key_str = toString(node.subs[0].keys[0]);915if (!key_str) return {};916return std::move(ret) + "pk(" + std::move(*key_str) + ")";917}918if (node.subs[0].fragment == Fragment::PK_H) {919// pkh(K) is syntactic sugar for c:pk_h(K)920auto key_str = toString(node.subs[0].keys[0]);921if (!key_str) return {};922return std::move(ret) + "pkh(" + std::move(*key_str) + ")";923}924return "c" + std::move(subs[0]);925case Fragment::WRAP_D: return "d" + std::move(subs[0]);926case Fragment::WRAP_V: return "v" + std::move(subs[0]);927case Fragment::WRAP_J: return "j" + std::move(subs[0]);928case Fragment::WRAP_N: return "n" + std::move(subs[0]);929case Fragment::AND_V:930// t:X is syntactic sugar for and_v(X,1).931if (node.subs[1].fragment == Fragment::JUST_1) return "t" + std::move(subs[0]);932break;933case Fragment::OR_I:934if (node.subs[0].fragment == Fragment::JUST_0) return "l" + std::move(subs[1]);935if (node.subs[1].fragment == Fragment::JUST_0) return "u" + std::move(subs[0]);936break;937default: break;938}939switch (node.fragment) {940case Fragment::PK_K: {941auto key_str = toString(node.keys[0]);942if (!key_str) return {};943return std::move(ret) + "pk_k(" + std::move(*key_str) + ")";944}945case Fragment::PK_H: {946auto key_str = toString(node.keys[0]);947if (!key_str) return {};948return std::move(ret) + "pk_h(" + std::move(*key_str) + ")";949}950case Fragment::AFTER: return std::move(ret) + "after(" + util::ToString(node.k) + ")";951case Fragment::OLDER: return std::move(ret) + "older(" + util::ToString(node.k) + ")";952case Fragment::HASH256: return std::move(ret) + "hash256(" + HexStr(node.data) + ")";953case Fragment::HASH160: return std::move(ret) + "hash160(" + HexStr(node.data) + ")";954case Fragment::SHA256: return std::move(ret) + "sha256(" + HexStr(node.data) + ")";955case Fragment::RIPEMD160: return std::move(ret) + "ripemd160(" + HexStr(node.data) + ")";956case Fragment::JUST_1: return std::move(ret) + "1";957case Fragment::JUST_0: return std::move(ret) + "0";958case Fragment::AND_V: return std::move(ret) + "and_v(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";959case Fragment::AND_B: return std::move(ret) + "and_b(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";960case Fragment::OR_B: return std::move(ret) + "or_b(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";961case Fragment::OR_D: return std::move(ret) + "or_d(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";962case Fragment::OR_C: return std::move(ret) + "or_c(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";963case Fragment::OR_I: return std::move(ret) + "or_i(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";964case Fragment::ANDOR:965// and_n(X,Y) is syntactic sugar for andor(X,Y,0).966if (node.subs[2].fragment == Fragment::JUST_0) return std::move(ret) + "and_n(" + std::move(subs[0]) + "," + std::move(subs[1]) + ")";967return std::move(ret) + "andor(" + std::move(subs[0]) + "," + std::move(subs[1]) + "," + std::move(subs[2]) + ")";968case Fragment::MULTI: {969CHECK_NONFATAL(!is_tapscript);970auto str = std::move(ret) + "multi(" + util::ToString(node.k);971for (const auto& key : node.keys) {972auto key_str = toString(key);973if (!key_str) return {};974str += "," + std::move(*key_str);975}976return std::move(str) + ")";977}978case Fragment::MULTI_A: {979CHECK_NONFATAL(is_tapscript);980auto str = std::move(ret) + "multi_a(" + util::ToString(node.k);981for (const auto& key : node.keys) {982auto key_str = toString(key);983if (!key_str) return {};984str += "," + std::move(*key_str);985}986return std::move(str) + ")";987}988case Fragment::THRESH: {989auto str = std::move(ret) + "thresh(" + util::ToString(node.k);990for (auto& sub : subs) {991str += "," + std::move(sub);992}993return std::move(str) + ")";994}995default: break;996}997assert(false);998};1000return TreeEvalMaybe<std::string>(false, downfn, upfn);
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.