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[](const recipe& a, const recipe& b) {752if (a.second.size() < b.second.size()) return true;753if (a.second.size() > b.second.size()) return false;754return a < b;755}756);757}758}759} SMARTINFO;761/**762* Consume a Miniscript node from the fuzzer's output.763*764* This is similar to ConsumeNodeStable, but uses a precomputed table with permitted765* fragments/subnode type for each required type. It is intended to more quickly explore766* interesting miniscripts, at the cost of higher implementation complexity (which could767* cause it miss things if incorrect), and with less regard for stability of the seeds768* (as improvements to the tables or changes to the typing rules could invalidate769* everything).770*/771std::optional<NodeInfo> ConsumeNodeSmart(MsCtx script_ctx, FuzzedDataProvider& provider, Type type_needed) {772/** Table entry for the requested type. */773const auto& table{IsTapscript(script_ctx) ? SMARTINFO.tap_table : SMARTINFO.wsh_table};774auto recipes_it = table.find(type_needed);775assert(recipes_it != table.end());776/** Pick one recipe from the available ones for that type. */777const auto& [frag, subt] = PickValue(provider, recipes_it->second);779// Based on the fragment the recipe uses, fill in other data (k, keys, data).780switch (frag) {781case Fragment::PK_K:782case Fragment::PK_H:783return {{frag, ConsumePubKey(provider)}};784case Fragment::MULTI: {785const auto n_keys = provider.ConsumeIntegralInRange<uint8_t>(1, 20);786const auto k = provider.ConsumeIntegralInRange<uint8_t>(1, n_keys);787std::vector<CPubKey> keys{n_keys};788for (auto& key: keys) key = ConsumePubKey(provider);789return {{frag, k, std::move(keys)}};790}791case Fragment::MULTI_A: {792const auto n_keys = provider.ConsumeIntegralInRange<uint16_t>(1, 999);793const auto k = provider.ConsumeIntegralInRange<uint16_t>(1, n_keys);794std::vector<CPubKey> keys{n_keys};795for (auto& key: keys) key = ConsumePubKey(provider);796return {{frag, k, std::move(keys)}};797}798case Fragment::OLDER:799case Fragment::AFTER:800return {{frag, provider.ConsumeIntegralInRange<uint32_t>(1, 0x7FFFFFF)}};801case Fragment::SHA256:802return {{frag, PickValue(provider, TEST_DATA.sha256)}};803case Fragment::HASH256:804return {{frag, PickValue(provider, TEST_DATA.hash256)}};805case Fragment::RIPEMD160:806return {{frag, PickValue(provider, TEST_DATA.ripemd160)}};807case Fragment::HASH160:808return {{frag, PickValue(provider, TEST_DATA.hash160)}};809case Fragment::JUST_0:810case Fragment::JUST_1:811case Fragment::WRAP_A:812case Fragment::WRAP_S:813case Fragment::WRAP_C:814case Fragment::WRAP_D:815case Fragment::WRAP_V:816case Fragment::WRAP_J:817case Fragment::WRAP_N:818case Fragment::AND_V:819case Fragment::AND_B:820case Fragment::OR_B:821case Fragment::OR_C:822case Fragment::OR_D:823case Fragment::OR_I:824case Fragment::ANDOR:825return {{subt, frag}};826case Fragment::THRESH: {827uint32_t children;828if (subt.size() < 2) {829children = subt.size();830} else {831// If we hit a thresh with 2 subnodes, artificially extend it to any number832// (2 or larger) by replicating the type of the last subnode.833children = provider.ConsumeIntegralInRange<uint32_t>(2, MAX_OPS_PER_SCRIPT / 2);834}835auto k = provider.ConsumeIntegralInRange<uint32_t>(1, children);836std::vector<Type> subs = subt;837while (subs.size() < children) subs.push_back(subs.back());838return {{std::move(subs), frag, k}};839}840}842assert(false);843}845/**846* Generate a Miniscript node based on the fuzzer's input.847*848* - ConsumeNode is a function object taking a Type, and returning an std::optional<NodeInfo>.849* - root_type is the required type properties of the constructed Node.850* - strict_valid sets whether ConsumeNode is expected to guarantee a NodeInfo that results in851* a Node whose Type() matches the type fed to ConsumeNode.852*/853template <typename F>854std::optional<Node> GenNode(MsCtx script_ctx, F ConsumeNode, Type root_type, bool strict_valid = false)855{856/** A stack of miniscript Nodes being built up. */857std::vector<Node> stack;858/** The queue of instructions. */859std::vector<std::pair<Type, std::optional<NodeInfo>>> todo{{root_type, {}}};860/** Predict the number of (static) script ops. */861uint32_t ops{0};862/** Predict the total script size (every unexplored subnode is counted as one, as every leaf is863* at least one script byte). */864uint32_t scriptsize{1};866while (!todo.empty()) {867// The expected type we have to construct.868auto type_needed = todo.back().first;869if (!todo.back().second) {870// Fragment/children have not been decided yet. Decide them.871auto node_info = ConsumeNode(type_needed);872if (!node_info) return {};873// Update predicted resource limits. Since every leaf Miniscript node is at least one874// byte long, we move one byte from each child to their parent. A similar technique is875// used in the miniscript::internal::Parse function to prevent runaway string parsing.876scriptsize += miniscript::internal::ComputeScriptLen(node_info->fragment, ""_mst, node_info->subtypes.size(), node_info->k, node_info->subtypes.size(),877node_info->keys.size(), script_ctx) - 1;878if (scriptsize > MAX_STANDARD_P2WSH_SCRIPT_SIZE) return {};879switch (node_info->fragment) {880case Fragment::JUST_0:881case Fragment::JUST_1:882break;883case Fragment::PK_K:884break;885case Fragment::PK_H:886ops += 3;887break;888case Fragment::OLDER:889case Fragment::AFTER:890ops += 1;891break;892case Fragment::RIPEMD160:893case Fragment::SHA256:894case Fragment::HASH160:895case Fragment::HASH256:896ops += 4;897break;898case Fragment::ANDOR:899ops += 3;900break;901case Fragment::AND_V:902break;903case Fragment::AND_B:904case Fragment::OR_B:905ops += 1;906break;907case Fragment::OR_C:908ops += 2;909break;910case Fragment::OR_D:911ops += 3;912break;913case Fragment::OR_I:914ops += 3;915break;916case Fragment::THRESH:917ops += node_info->subtypes.size();918break;919case Fragment::MULTI:920ops += 1;921break;922case Fragment::MULTI_A:923ops += node_info->keys.size() + 1;924break;925case Fragment::WRAP_A:926ops += 2;927break;928case Fragment::WRAP_S:929ops += 1;930break;931case Fragment::WRAP_C:932ops += 1;933break;934case Fragment::WRAP_D:935ops += 3;936break;937case Fragment::WRAP_V:938// We don't account for OP_VERIFY here; that will be corrected for when the actual939// node is constructed below.940break;941case Fragment::WRAP_J:942ops += 4;943break;944case Fragment::WRAP_N:945ops += 1;946break;947}948if (ops > MAX_OPS_PER_SCRIPT) return {};949auto subtypes = node_info->subtypes;950todo.back().second = std::move(node_info);951todo.reserve(todo.size() + subtypes.size());952// As elements on the todo stack are processed back to front, construct953// them in reverse order (so that the first subnode is generated first).954for (size_t i = 0; i < subtypes.size(); ++i) {955todo.emplace_back(*(subtypes.rbegin() + i), std::nullopt);956}957} else {958// The back of todo has fragment and number of children decided, and959// those children have been constructed at the back of stack. Pop960// that entry off todo, and use it to construct a new Node on961// stack.962NodeInfo& info = *todo.back().second;963// Gather children from the back of stack.964std::vector<Node> sub;965sub.reserve(info.subtypes.size());966for (size_t i = 0; i < info.subtypes.size(); ++i) {967sub.push_back(std::move(*(stack.end() - info.subtypes.size() + i)));968}969stack.erase(stack.end() - info.subtypes.size(), stack.end());970// Construct new Node.971Node node{[&] {972if (info.keys.empty()) {973return Node{miniscript::internal::NoDupCheck{}, script_ctx, info.fragment, std::move(sub), std::move(info.hash), info.k};974}975assert(sub.empty());976assert(info.hash.empty());977return Node{miniscript::internal::NoDupCheck{}, script_ctx, info.fragment, std::move(info.keys), info.k};978}()};979// Verify acceptability.980if ((node.GetType() & "KVWB"_mst) == ""_mst) {981assert(!strict_valid);982return {};983}984if (!(type_needed == ""_mst)) {985assert(node.GetType() << type_needed);986}987if (!node.IsValid()) return {};988// Update resource predictions.989if (node.Fragment() == Fragment::WRAP_V && node.Subs()[0].GetType() << "x"_mst) {990ops += 1;991scriptsize += 1;992}993if (!miniscript::IsTapscript(script_ctx) && ops > MAX_OPS_PER_SCRIPT) return {};994if (scriptsize > miniscript::internal::MaxScriptSize(script_ctx)) {995return {};996}997// Move it to the stack.998stack.push_back(std::move(node));999todo.pop_back();1000}
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.