diff --git a/doc/descriptors.md b/doc/descriptors.md index 32922b52668..625a511bba2 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -288,11 +288,11 @@ For example, a descriptor of the form: multi(2,xpub.../<0;1;2>/0/*,xpub.../<2;3;4>/*) -will expand to the two descriptors +will expand to the 3 descriptors - multi(2,xpub.../0/0/*,xpub.../2/*) - multi(2,xpub.../1/0/*,xpub.../3/*) - multi(2,xpub.../2/0/*,xpub.../4*) + multi(2,xpub.../0/0/*,xpub.../2/*) + multi(2,xpub.../1/0/*,xpub.../3/*) + multi(2,xpub.../2/0/*,xpub.../4/*) When this tuple contains only two elements, wallet implementations can use the first descriptor for receiving addresses and the second descriptor for change addresses. diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 2d23ec9ede6..e132a0ba224 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -1440,20 +1440,22 @@ std::optional ParseKeyPathNum(std::span elem, bool& apostr [[nodiscard]] bool ParseKeyPath(const std::vector>& split, std::vector& out, bool& apostrophe, std::string& error, bool allow_multipath) { KeyPath path; - std::optional multipath_segment_index; - std::vector multipath_values; - std::unordered_set seen_multipath; + struct MultipathSubstitutes { + size_t placeholder_index; + std::vector values; + }; + std::optional substitutes; for (size_t i = 1; i < split.size(); ++i) { const std::span& elem = split[i]; - // Check if element contain multipath specifier + // Check if element contains multipath specifier if (!elem.empty() && elem.front() == '<' && elem.back() == '>') { if (!allow_multipath) { error = strprintf("Key path value '%s' specifies multipath in a section where multipath is not allowed", std::string(elem.begin(), elem.end())); return false; } - if (multipath_segment_index) { + if (substitutes) { error = "Multiple multipath key path specifiers found"; return false; } @@ -1465,19 +1467,21 @@ std::optional ParseKeyPathNum(std::span elem, bool& apostr return false; } + substitutes.emplace(); + std::unordered_set seen_substitutes; for (const auto& num : nums) { const auto& op_num = ParseKeyPathNum(num, apostrophe, error); if (!op_num) return false; - auto [_, inserted] = seen_multipath.insert(*op_num); + auto [_, inserted] = seen_substitutes.insert(*op_num); if (!inserted) { error = strprintf("Duplicated key path value %u in multipath specifier", *op_num); return false; } - multipath_values.emplace_back(*op_num); + substitutes->values.emplace_back(*op_num); } path.emplace_back(); // Placeholder for multipath segment - multipath_segment_index = path.size()-1; + substitutes->placeholder_index = path.size() - 1; } else { const auto& op_num = ParseKeyPathNum(elem, apostrophe, error); if (!op_num) return false; @@ -1485,13 +1489,13 @@ std::optional ParseKeyPathNum(std::span elem, bool& apostr } } - if (!multipath_segment_index) { + if (!substitutes) { out.emplace_back(std::move(path)); } else { // Replace the multipath placeholder with each value while generating paths - for (size_t i = 0; i < multipath_values.size(); i++) { + for (uint32_t substitute : substitutes->values) { KeyPath branch_path = path; - branch_path[*multipath_segment_index] = multipath_values[i]; + branch_path[substitutes->placeholder_index] = substitute; out.emplace_back(std::move(branch_path)); } }