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.
1501{1502// We cannot use a lambda here, as lambdas are non assignable, and the set operations1503// below require moving the comparators around.1504struct Comp {1505const Ctx* ctx_ptr;1506Comp(const Ctx& ctx) : ctx_ptr(&ctx) {}1507bool operator()(const Key& a, const Key& b) const { return ctx_ptr->KeyCompare(a, b); }1508};1510// state in the recursive computation:1511// - std::nullopt means "this node has duplicates"1512// - an std::set means "this node has no duplicate keys, and they are: ...".1513using keyset = std::set<Key, Comp>;1514using state = std::optional<keyset>;1516auto upfn = [&ctx](const Node& node, std::span<state> subs) -> state {1517// If this node is already known to have duplicates, nothing left to do.1518if (node.has_duplicate_keys.has_value() && *node.has_duplicate_keys) return {};1520// Check if one of the children is already known to have duplicates.1521for (auto& sub : subs) {1522if (!sub.has_value()) {1523node.has_duplicate_keys = true;1524return {};1525}1526}1528// Start building the set of keys involved in this node and children.1529// Start by keys in this node directly.1530size_t keys_count = node.keys.size();1531keyset key_set{node.keys.begin(), node.keys.end(), Comp(ctx)};1532if (key_set.size() != keys_count) {1533// It already has duplicates; bail out.1534node.has_duplicate_keys = true;1535return {};1536}1538// Merge the keys from the children into this set.1539for (auto& sub : subs) {1540keys_count += sub->size();1541// Small optimization: std::set::merge is linear in the size of the second arg but1542// logarithmic in the size of the first.1543if (key_set.size() < sub->size()) std::swap(key_set, *sub);1544key_set.merge(*sub);1545if (key_set.size() != keys_count) {1546node.has_duplicate_keys = true;1547return {};1548}1549}1551node.has_duplicate_keys = false;1552return key_set;1553};1555TreeEval<state>(upfn);1556}1558//! Return the size of the script for this expression (faster than ToScript().size()).1559size_t ScriptSize() const { return scriptlen; }1561//! Return the maximum number of ops needed to satisfy this script non-malleably.1562std::optional<uint32_t> GetOps() const {1563if (!ops.sat.Valid()) return {};1564return ops.count + ops.sat.Value();1565}1567//! Return the number of ops in the script (not counting the dynamic ones that depend on execution).1568uint32_t GetStaticOps() const { return ops.count; }1570//! Check the ops limit of this script against the consensus limit.1571bool CheckOpsLimit() const {1572if (IsTapscript(m_script_ctx)) return true;1573if (const auto ops = GetOps()) return *ops <= MAX_OPS_PER_SCRIPT;1574return true;1575}1577/** Whether this node is of type B, K or W. (That is, anything but V.) */1578bool IsBKW() const {1579return !((GetType() & "BKW"_mst) == ""_mst);1580}1582/** Return the maximum number of stack elements needed to satisfy this script non-malleably. */1583std::optional<uint32_t> GetStackSize() const {1584if (!ss.Sat().Valid()) return {};1585return ss.Sat().NetDiff() + static_cast<int32_t>(IsBKW());1586}1588//! Return the maximum size of the stack during execution of this script.1589std::optional<uint32_t> GetExecStackSize() const {1590if (!ss.Sat().Valid()) return {};1591return ss.Sat().Exec() + static_cast<int32_t>(IsBKW());1592}1594//! Check the maximum stack size for this script against the policy limit.1595bool CheckStackSize() const {1596// Since in Tapscript there is no standardness limit on the script and witness sizes, we may run1597// into the maximum stack size while executing the script. Make sure it doesn't happen.1598if (IsTapscript(m_script_ctx)) {1599if (const auto exec_ss = GetExecStackSize()) return exec_ss <= MAX_STACK_SIZE;1600return true;1601}1602if (const auto ss = GetStackSize()) return *ss <= MAX_STANDARD_P2WSH_STACK_ITEMS;1603return true;1604}1606//! Whether no satisfaction exists for this node.1607bool IsNotSatisfiable() const { return !GetStackSize(); }1609/** Return the maximum size in bytes of a witness to satisfy this script non-malleably. Note this does1610* not include the witness script push. */1611std::optional<uint32_t> GetWitnessSize() const {1612if (!ws.sat.Valid()) return {};1613return ws.sat.Value();1614}1616//! Return the expression type.1617Type GetType() const { return typ; }1619//! Return the script context for this node.1620MiniscriptContext GetMsCtx() const { return m_script_ctx; }1622//! Find an insane subnode which has no insane children. Nullptr if there is none.1623const Node* FindInsaneSub() const {1624return TreeEval<const Node*>([](const Node& node, std::span<const Node*> subs) -> const Node* {1625for (auto& sub: subs) if (sub) return sub;1626if (!node.IsSaneSubexpression()) return &node;1627return nullptr;1628});1629}1631//! Determine whether a Miniscript node is satisfiable. fn(node) will be invoked for all1632//! key, time, and hashing nodes, and should return their satisfiability.1633template<typename F>1634bool IsSatisfiable(F fn) const1635{1636// TreeEval() doesn't support bool as NodeType, so use int instead.1637return TreeEval<int>([&fn](const Node& node, std::span<int> subs) -> bool {1638switch (node.fragment) {1639case Fragment::JUST_0:1640return false;1641case Fragment::JUST_1:1642return true;1643case Fragment::PK_K:1644case Fragment::PK_H:1645case Fragment::MULTI:1646case Fragment::MULTI_A:1647case Fragment::AFTER:1648case Fragment::OLDER:1649case Fragment::HASH256:1650case Fragment::HASH160:1651case Fragment::SHA256:1652case Fragment::RIPEMD160:1653return bool{fn(node)};1654case Fragment::ANDOR:1655return (subs[0] && subs[1]) || subs[2];1656case Fragment::AND_V:1657case Fragment::AND_B:1658return subs[0] && subs[1];1659case Fragment::OR_B:1660case Fragment::OR_C:1661case Fragment::OR_D:1662case Fragment::OR_I:1663return subs[0] || subs[1];1664case Fragment::THRESH:1665return static_cast<uint32_t>(std::count(subs.begin(), subs.end(), true)) >= node.k;1666default: // wrappers1667assert(subs.size() >= 1);1668CHECK_NONFATAL(subs.size() == 1);1669return subs[0];1670}1671});1672}1674//! Check whether this node is valid at all.1675bool IsValid() const {1676if (GetType() == ""_mst) return false;1677return ScriptSize() <= internal::MaxScriptSize(m_script_ctx);1678}1680//! Check whether this node is valid as a script on its own.1681bool IsValidTopLevel() const { return IsValid() && GetType() << "B"_mst; }1683//! Check whether this script can always be satisfied in a non-malleable way.1684bool IsNonMalleable() const { return GetType() << "m"_mst; }1686//! Check whether this script always needs a signature.1687bool NeedsSignature() const { return GetType() << "s"_mst; }1689//! Check whether there is no satisfaction path that contains both timelocks and heightlocks1690bool CheckTimeLocksMix() const { return GetType() << "k"_mst; }1692//! Check whether there is no duplicate key across this fragment and all its sub-fragments.1693bool CheckDuplicateKey() const { return has_duplicate_keys && !*has_duplicate_keys; }1695//! Whether successful non-malleable satisfactions are guaranteed to be valid.1696bool ValidSatisfactions() const { return IsValid() && CheckOpsLimit() && CheckStackSize(); }1698//! Whether the apparent policy of this node matches its script semantics. Doesn't guarantee it is a safe script on its own.1699bool IsSaneSubexpression() const { return ValidSatisfactions() && IsNonMalleable() && CheckTimeLocksMix() && CheckDuplicateKey(); }1701//! Check whether this node is safe as a script on its own.1702bool IsSane() const { return IsValidTopLevel() && IsSaneSubexpression() && NeedsSignature(); }1704//! Produce a witness for this script, if possible and given the information available in the context.1705//! The non-malleable satisfaction is guaranteed to be valid if it exists, and ValidSatisfaction()1706//! is true. If IsSane() holds, this satisfaction is guaranteed to succeed in case the node's1707//! conditions are satisfied (private keys and hash preimages available, locktimes satisfied).1708template<typename Ctx>1709Availability Satisfy(const Ctx& ctx, std::vector<std::vector<unsigned char>>& stack, bool nonmalleable = true) const {1710auto ret = ProduceInput(ctx);1711if (nonmalleable && (ret.sat.malleable || !ret.sat.has_sig)) return Availability::NO;1712stack = std::move(ret.sat.stack);1713return ret.sat.available;1714}1716//! Equality testing.1717bool operator==(const Node<Key>& arg) const { return Compare(*this, arg) == 0; }1719// Constructors with various argument combinations, which bypass the duplicate key check.1720Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, std::vector<Node> sub, std::vector<unsigned char> arg, uint32_t val = 0)1721: fragment(nt), k(val), data(std::move(arg)), subs(std::move(sub)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1722Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0)1723: fragment(nt), k(val), data(std::move(arg)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1724Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, std::vector<Node> sub, std::vector<Key> key, uint32_t val = 0)1725: fragment(nt), k(val), keys(std::move(key)), m_script_ctx{script_ctx}, subs(std::move(sub)), ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1726Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, std::vector<Key> key, uint32_t val = 0)1727: fragment(nt), k(val), keys(std::move(key)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1728Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, std::vector<Node> sub, uint32_t val = 0)1729: fragment(nt), k(val), subs(std::move(sub)), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1730Node(internal::NoDupCheck, MiniscriptContext script_ctx, enum Fragment nt, uint32_t val = 0)1731: fragment(nt), k(val), m_script_ctx{script_ctx}, ops(CalcOps()), ss(CalcStackSize()), ws(CalcWitnessSize()), typ(CalcType()), scriptlen(CalcScriptLen()) {}1733// Constructors with various argument combinations, which do perform the duplicate key check.1734template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, std::vector<Node> sub, std::vector<unsigned char> arg, uint32_t val = 0)1735: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), std::move(arg), val) { DuplicateKeyCheck(ctx); }1736template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, std::vector<unsigned char> arg, uint32_t val = 0)1737: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(arg), val) { DuplicateKeyCheck(ctx);}1738template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, std::vector<Node> sub, std::vector<Key> key, uint32_t val = 0)1739: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), std::move(key), val) { DuplicateKeyCheck(ctx); }1740template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, std::vector<Key> key, uint32_t val = 0)1741: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(key), val) { DuplicateKeyCheck(ctx); }1742template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, std::vector<Node> sub, uint32_t val = 0)1743: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, std::move(sub), val) { DuplicateKeyCheck(ctx); }1744template <typename Ctx> Node(const Ctx& ctx, enum Fragment nt, uint32_t val = 0)1745: Node(internal::NoDupCheck{}, ctx.MsContext(), nt, val) { DuplicateKeyCheck(ctx); }1747// Delete copy constructor and assignment operator, use Clone() instead1748Node(const Node&) = delete;1749Node& operator=(const Node&) = delete;
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.