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.
1// Copyright (c) 2023-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <test/fuzz/util/descriptor.h>7#include <key.h>8#include <key_io.h>9#include <pubkey.h>10#include <span.h>11#include <util/strencodings.h>13#include <ranges>14#include <stack>15#include <vector>17void MockedDescriptorConverter::Init()18{19// The data to use as a private key or a seed for an xprv.20std::array<std::byte, 32> key_data{std::byte{1}};21// Generate keys of all kinds and store them in the keys array.22for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {23key_data[31] = std::byte(i);25// If this is a "raw" key, generate a normal privkey. Otherwise generate26// an extended one.27if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {28CKey privkey;29privkey.Set(key_data.begin(), key_data.end(), !IdIsUnCompPubKey(i));30if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {31CPubKey pubkey{privkey.GetPubKey()};32keys_str[i] = HexStr(pubkey);33} else if (IdIsXOnlyPubKey(i)) {34const XOnlyPubKey pubkey{privkey.GetPubKey()};35keys_str[i] = HexStr(pubkey);36} else {37keys_str[i] = EncodeSecret(privkey);38}39} else {40CExtKey ext_privkey;41ext_privkey.SetSeed(key_data);42if (IdIsXprv(i)) {43keys_str[i] = EncodeExtKey(ext_privkey);44} else {45const CExtPubKey ext_pubkey{ext_privkey.Neuter()};46keys_str[i] = EncodeExtPubKey(ext_pubkey);47}48}49}50}52std::optional<uint8_t> MockedDescriptorConverter::IdxFromHex(std::string_view hex_characters) const {53if (hex_characters.size() != 2) return {};54auto idx = ParseHex(hex_characters);55if (idx.size() != 1) return {};56return idx[0];57}59std::optional<std::string> MockedDescriptorConverter::GetDescriptor(std::string_view mocked_desc) const {60// The smallest fragment would be "pk(%00)"61if (mocked_desc.size() < 7) return {};63// The actual descriptor string to be returned.64std::string desc;65desc.reserve(mocked_desc.size());67// Replace all occurrences of '%' followed by two hex characters with the corresponding key.68for (size_t i = 0; i < mocked_desc.size();) {69if (mocked_desc[i] == '%') {70if (i + 3 >= mocked_desc.size()) return {};71if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {72desc += keys_str[*idx];73i += 3;74} else {75return {};76}77} else {78desc += mocked_desc[i++];79}80}82return desc;83}85bool HasDeepDerivPath(std::span<const uint8_t> buff, const int max_depth)86{87auto depth{0};88for (const auto& ch: buff) {89if (ch == ',') {90// A comma is always present between two key expressions, so we use that as a delimiter.91depth = 0;92} else if (ch == '/') {93if (++depth > max_depth) return true;94}95}96return false;97}99bool HasTooManySubFrag(std::span<const uint8_t> buff, const int max_subs, const size_t max_nested_subs)100{101// We use a stack because there may be many nested sub-frags.102std::stack<int> counts;103for (const auto& ch: buff) {104// The fuzzer may generate an input with a ton of parentheses. Rule out pathological cases.105if (counts.size() > max_nested_subs) return true;107if (ch == '(') {108// A new fragment was opened, create a new sub-count for it and start as one since any fragment with109// parentheses has at least one sub.110counts.push(1);111} else if (ch == ',' && !counts.empty()) {112// When encountering a comma, account for an additional sub in the last opened fragment. If it exceeds the113// limit, bail.114if (++counts.top() > max_subs) return true;115} else if (ch == ')' && !counts.empty()) {116// Fragment closed! Drop its sub count and resume to counting the number of subs for its parent.117counts.pop();118}119}120return false;121}123bool HasTooManyWrappers(std::span<const uint8_t> buff, const int max_wrappers)124{125// The number of nested wrappers. Nested wrappers are always characters which follow each other so we don't have to126// use a stack as we do above when counting the number of sub-fragments.127std::optional<int> count;129// We want to detect nested wrappers. A wrapper is a character prepended to a fragment, separated by a colon. There130// may be more than one wrapper, in which case the colon is not repeated. For instance `jjjjj:pk()`. To count131// wrappers we iterate in reverse and use the colon to detect the end of a wrapper expression and count how many132// characters there are since the beginning of the expression. We stop counting when we encounter a character133// indicating the beginning of a new expression.134for (const auto ch: buff | std::views::reverse) {135// A colon, start counting.136if (ch == ':') {137// The colon itself is not a wrapper so we start at 0.138count = 0;139} else if (count) {140// If we are counting wrappers, stop when we crossed the beginning of the wrapper expression. Otherwise keep141// counting and bail if we reached the limit.142// A wrapper may only ever occur as the first sub of a descriptor/miniscript expression ('('), as the143// first Taproot leaf in a pair ('{') or as the nth sub in each case (',').144if (ch == ',' || ch == '(' || ch == '{') {145count.reset();146} else if (++*count > max_wrappers) {147return true;148}149}150}152return false;153}155bool HasTooLargeLeafSize(std::span<const uint8_t> buff, const uint32_t max_leaf_size)156{157uint32_t leaf_len{0};158for (auto c : buff) {159if (c == '(' || c == ')' || c == ',' || c == '{' || c == '}') {160// Possibly start a fresh leaf, or a fresh function name (with161// wrappers), or terminate a prior leaf.162leaf_len = 0;163} else {164// Just treat everything else as a leaf. This will also reject long165// function names, but this should be fine if the max_leaf_size is166// set large enough.167if (++leaf_len > max_leaf_size) {168return true;169}170}171}172return false;173}
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.