| 1854 | */ |
| 1855 | template <typename Key, typename Ctx> |
| 1856 | inline std::optional<Node<Key>> Parse(std::span<const char> in, const Ctx& ctx) |
| 1857 | { |
| 1858 | using namespace script; |
| 1859 | |
| 1860 | // Account for the minimum script size for all parsed fragments so far. It "borrows" 1 |
| 1861 | // script byte from all leaf nodes, counting it instead whenever a space for a recursive |
| 1862 | // expression is added (through andor, and_*, or_*, thresh). This guarantees that all fragments |
| 1863 | // increment the script_size by at least one, except for: |
| 1864 | // - "0", "1": these leafs are only a single byte, so their subtracted-from increment is 0. |
| 1865 | // This is not an issue however, as "space" for them has to be created by combinators, |
| 1866 | // which do increment script_size. |
| 1867 | // - "v:": the v wrapper adds nothing as in some cases it results in no opcode being added |
| 1868 | // (instead transforming another opcode into its VERIFY form). However, the v: wrapper has |
| 1869 | // to be interleaved with other fragments to be valid, so this is not a concern. |
| 1870 | size_t script_size{1}; |
| 1871 | size_t max_size{internal::MaxScriptSize(ctx.MsContext())}; |
| 1872 | |
| 1873 | // The two integers are used to hold state for thresh() |
| 1874 | std::vector<std::tuple<ParseContext, int64_t, int64_t>> to_parse; |
| 1875 | std::vector<Node<Key>> constructed; |
| 1876 | |
| 1877 | to_parse.emplace_back(ParseContext::WRAPPED_EXPR, -1, -1); |
| 1878 | |
| 1879 | // Parses a multi() or multi_a() from its string representation. Returns false on parsing error. |
| 1880 | const auto parse_multi_exp = [&](std::span<const char>& in, const bool is_multi_a) -> bool { |
| 1881 | const auto max_keys{is_multi_a ? MAX_PUBKEYS_PER_MULTI_A : MAX_PUBKEYS_PER_MULTISIG}; |
| 1882 | const auto required_ctx{is_multi_a ? MiniscriptContext::TAPSCRIPT : MiniscriptContext::P2WSH}; |
| 1883 | if (ctx.MsContext() != required_ctx) return false; |
| 1884 | // Get threshold |
| 1885 | int next_comma = FindNextChar(in, ','); |
| 1886 | if (next_comma < 1) return false; |
| 1887 | const auto k_to_integral{ToIntegral<int64_t>(std::string_view(in.data(), next_comma))}; |
| 1888 | if (!k_to_integral.has_value()) return false; |
| 1889 | const int64_t k{k_to_integral.value()}; |
| 1890 | in = in.subspan(next_comma + 1); |
| 1891 | // Get keys. It is compatible for both compressed and x-only keys. |
| 1892 | std::vector<Key> keys; |
| 1893 | while (next_comma != -1) { |
| 1894 | next_comma = FindNextChar(in, ','); |
| 1895 | int key_length = (next_comma == -1) ? FindNextChar(in, ')') : next_comma; |
| 1896 | if (key_length < 1) return false; |
| 1897 | std::span<const char> sp{in.begin(), in.begin() + key_length}; |
| 1898 | auto key = ctx.FromString(sp); |
| 1899 | if (!key) return false; |
| 1900 | keys.push_back(std::move(*key)); |
| 1901 | in = in.subspan(key_length + 1); |
| 1902 | } |
| 1903 | if (keys.size() < 1 || keys.size() > max_keys) return false; |
| 1904 | if (k < 1 || k > (int64_t)keys.size()) return false; |
| 1905 | if (is_multi_a) { |
| 1906 | // (push + xonly-key + CHECKSIG[ADD]) * n + k + OP_NUMEQUAL(VERIFY), minus one. |
| 1907 | script_size += (1 + 32 + 1) * keys.size() + BuildScript(k).size(); |
| 1908 | constructed.emplace_back(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI_A, std::move(keys), k); |
| 1909 | } else { |
| 1910 | script_size += 2 + (keys.size() > 16) + (k > 16) + 34 * keys.size(); |
| 1911 | constructed.emplace_back(internal::NoDupCheck{}, ctx.MsContext(), Fragment::MULTI, std::move(keys), k); |
| 1912 | } |
| 1913 | return true; |
nothing calls this directly
no test coverage detected