Repository: bitcoin/bitcoin
File: src/script/miniscript.h (C/C++)

Code before:

/** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */
template<typename Key>
void BuildBack(const MiniscriptContext script_ctx, Fragment nt, std::vector<NodeRef<Key>>& constructed, const bool reverse = false)
{
    NodeRef<Key> child = std::move(constructed.back());
    constructed.pop_back();
    if (reverse) {
        constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(child), std::move(constructed.back())));
    } else {
        constructed.back() = MakeNodeRef<Key>(internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(constructed.back()), std::move(child)));
    }
}

/**
 * Parse a miniscript from its textual descriptor form.
 * This does not check whether the script is valid, let alone sane. The caller is expected to use
 * the `IsValidTopLevel()` and `IsSaneTopLevel()` to check for these properties on the node.
 */
template<typename Key, typename Ctx>
inline NodeRef<Key> Parse(std::span<const char> in, const Ctx& ctx)
{
    using namespace script;

    // Account for the minimum script size for all parsed fragments so far. It "borrows" 1
    // script byte from all leaf nodes, counting it instead whenever a space for a recursive
    // expression is added (through andor, and_*, or_*, thresh). This guarantees that all fragments
    // increment the script_size by at least one, except for:
    // - "0", "1": these leafs are only a single byte, so their subtracted-from increment is 0.
    //   This is not an issue however, as "space" for them has to be created by combinators,
    //   which do increment script_size.
    // - "v:": the v wrapper adds nothing as in some cases it results in no opcode being added
    //   (instead transforming another opcode into its VERIFY form). However, the v: wrapper has
    //   to be interleaved with other fragments to be valid, so this is not a concern.
    size_t script_size{1};
    size_t max_size{internal::MaxScriptSize(ctx.MsContext())};

    // The two integers are used to hold state for thresh()
    std::vector<std::tuple<ParseContext, int64_t, int64_t>> to_parse;
    std::vector<NodeRef<Key>> constructed;

    to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1);

    // Parses a multi() or multi_a() from its string representation. Returns false on parsing error.
    const auto parse_multi_exp = [&](std::span<const char>& in, const bool is_multi_a) -> bool {
        const auto max_keys{is_multi_a ? MAX_PUBKEYS_PER_MULTI_A : MAX_PUBKEYS_PER_MULTISIG};
        const auto required_ctx{is_multi_a ? MiniscriptContext::TAPSCRIPT : MiniscriptContext::P2WSH};
        if (ctx.MsContext() != required_ctx) return false;
        // Get threshold
        int next_comma = FindNextChar(in, ',');
        if (next_comma < 1) return false;

Review comment (suggestion):
Mental note: we should really rewrite all of the span parsing logic to use `std::string_view`s instead, so these conversions aren't needed.

Code after:

    /** COMMA expects the next element to be ',' and fails if not. */
    COMMA,
    /** CLOSE_BRACKET expects the next element to be ')' and fails if not. */
    CLOSE_BRACKET,
};

int FindNextChar(std::span<const char> in, char m);

/** Parse a key expression fully contained within a fragment with the name given by 'func' */
template<typename Key, typename Ctx>
std::optional<Key> ParseKey(const std::string& func, std::span<const char>& in, const Ctx& ctx)
{
    std::span<const char> expr = script::Expr(in);
    if (!script::Func(func, expr)) return {};
    return ctx.FromString(expr);
}

/** Parse a hex string fully contained within a fragment with the name given by 'func' */
template<typename Ctx>
std::optional<std::vector<unsigned char>> ParseHexStr(const std::string& func, std::span<const char>& in, const size_t expected_size,
                                                                         const Ctx& ctx)
{
    std::span<const char> expr = script::Expr(in);
    if (!script::Func(func, expr)) return {};
    std::string val = std::string(expr.begin(), expr.end());
    if (!IsHex(val)) return {};
    auto hash = ParseHex(val);
    if (hash.size() != expected_size) return {};
    return hash;
}

/** BuildBack pops the last two elements off `constructed` and wraps them in the specified Fragment */
template<typename Key>
void BuildBack(const MiniscriptContext script_ctx, Fragment nt, std::vector<Node<Key>>& constructed, const bool reverse = false)
{
    Node<Key> child{std::move(constructed.back())};
    constructed.pop_back();
    if (reverse) {
        constructed.back() = Node<Key>{internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(child), std::move(constructed.back()))};
    } else {
        constructed.back() = Node<Key>{internal::NoDupCheck{}, script_ctx, nt, Vector(std::move(constructed.back()), std::move(child))};
    }
}

/**
 * Parse a miniscript from its textual descriptor form.
 * This does not check whether the script is valid, let alone sane. The caller is expected to use
 * the `IsValidTopLevel()` and `IsSaneTopLevel()` to check for these properties on the node.
 */
template <typename Key, typename Ctx>