Merge bitcoin/bitcoin#32134: descriptors: Multipath/PR 22838 follow-ups
Some checks are pending
CI / test each commit (push) Waiting to run
CI / macOS 14 native, arm64, no depends, sqlite only, gui (push) Waiting to run
CI / macOS 14 native, arm64, fuzz (push) Waiting to run
CI / Windows native, VS 2022 (push) Waiting to run
CI / Windows native, fuzz, VS 2022 (push) Waiting to run
CI / Linux->Windows cross, no tests (push) Waiting to run
CI / Windows, test cross-built (push) Blocked by required conditions
CI / ASan + LSan + UBSan + integer, no depends, USDT (push) Waiting to run

56f271e9b9 descriptors refactor: Clarify multipath data relationships through local struct (Hodlinator)
7e974f474e descriptors refactor: Use range-for and limit scope of seen_multipath (Hodlinator)
99a92efdd9 descriptors doc: Correct Markdown format + wording (Hodlinator)

Pull request description:

  Follows up on unresolved suggestions from #22838. In order of priority:

  1. Fixes a couple of typos [^1][^2] and indentation to conform to Markdown.
  2. Solves `for`-loop nit [^3] and also limits variable scope.
  3. Clarifies data relationships [^4][^5] by introducing `struct` rather than comments.

  [^1]: https://github.com/bitcoin/bitcoin/pull/22838#discussion_r1713711352
  [^2]: https://github.com/bitcoin/bitcoin/pull/22838#discussion_r1735039600
  [^3]: https://github.com/bitcoin/bitcoin/pull/22838#discussion_r1735041704
  [^4]: https://github.com/bitcoin/bitcoin/pull/22838#discussion_r1715150336
  [^5]: https://github.com/bitcoin/bitcoin/pull/22838#discussion_r1715151078

ACKs for top commit:
  Prabhat1308:
    re-ACK [`56f271e`](56f271e9b9)
  mabu44:
    tACK 56f271e9b9
  l0rinc:
    utACK 56f271e9b9
  rkrux:
    crACK 56f271e9b9

Tree-SHA512: 75777c911640038a3e0ea48601c0f55463a5f8ff5b3462d81e8992d9fc8f988d5a240e2166befa67a2a246696b0863f8e2508524c14697c490d3b229fe048996
This commit is contained in:
Ryan Ofsky 2025-03-31 15:38:04 -04:00
commit 74d9598bfb
No known key found for this signature in database
GPG key ID: 46800E30FC748A66
2 changed files with 19 additions and 15 deletions

View file

@ -288,11 +288,11 @@ For example, a descriptor of the form:
multi(2,xpub.../<0;1;2>/0/*,xpub.../<2;3;4>/*) 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.../0/0/*,xpub.../2/*)
multi(2,xpub.../1/0/*,xpub.../3/*) multi(2,xpub.../1/0/*,xpub.../3/*)
multi(2,xpub.../2/0/*,xpub.../4*) multi(2,xpub.../2/0/*,xpub.../4/*)
When this tuple contains only two elements, wallet implementations can use the When this tuple contains only two elements, wallet implementations can use the
first descriptor for receiving addresses and the second descriptor for change addresses. first descriptor for receiving addresses and the second descriptor for change addresses.

View file

@ -1440,20 +1440,22 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
[[nodiscard]] bool ParseKeyPath(const std::vector<std::span<const char>>& split, std::vector<KeyPath>& out, bool& apostrophe, std::string& error, bool allow_multipath) [[nodiscard]] bool ParseKeyPath(const std::vector<std::span<const char>>& split, std::vector<KeyPath>& out, bool& apostrophe, std::string& error, bool allow_multipath)
{ {
KeyPath path; KeyPath path;
std::optional<size_t> multipath_segment_index; struct MultipathSubstitutes {
std::vector<uint32_t> multipath_values; size_t placeholder_index;
std::unordered_set<uint32_t> seen_multipath; std::vector<uint32_t> values;
};
std::optional<MultipathSubstitutes> substitutes;
for (size_t i = 1; i < split.size(); ++i) { for (size_t i = 1; i < split.size(); ++i) {
const std::span<const char>& elem = split[i]; const std::span<const char>& elem = split[i];
// Check if element contain multipath specifier // Check if element contains multipath specifier
if (!elem.empty() && elem.front() == '<' && elem.back() == '>') { if (!elem.empty() && elem.front() == '<' && elem.back() == '>') {
if (!allow_multipath) { 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())); error = strprintf("Key path value '%s' specifies multipath in a section where multipath is not allowed", std::string(elem.begin(), elem.end()));
return false; return false;
} }
if (multipath_segment_index) { if (substitutes) {
error = "Multiple multipath key path specifiers found"; error = "Multiple multipath key path specifiers found";
return false; return false;
} }
@ -1465,19 +1467,21 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
return false; return false;
} }
substitutes.emplace();
std::unordered_set<uint32_t> seen_substitutes;
for (const auto& num : nums) { for (const auto& num : nums) {
const auto& op_num = ParseKeyPathNum(num, apostrophe, error); const auto& op_num = ParseKeyPathNum(num, apostrophe, error);
if (!op_num) return false; if (!op_num) return false;
auto [_, inserted] = seen_multipath.insert(*op_num); auto [_, inserted] = seen_substitutes.insert(*op_num);
if (!inserted) { if (!inserted) {
error = strprintf("Duplicated key path value %u in multipath specifier", *op_num); error = strprintf("Duplicated key path value %u in multipath specifier", *op_num);
return false; return false;
} }
multipath_values.emplace_back(*op_num); substitutes->values.emplace_back(*op_num);
} }
path.emplace_back(); // Placeholder for multipath segment path.emplace_back(); // Placeholder for multipath segment
multipath_segment_index = path.size()-1; substitutes->placeholder_index = path.size() - 1;
} else { } else {
const auto& op_num = ParseKeyPathNum(elem, apostrophe, error); const auto& op_num = ParseKeyPathNum(elem, apostrophe, error);
if (!op_num) return false; if (!op_num) return false;
@ -1485,13 +1489,13 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
} }
} }
if (!multipath_segment_index) { if (!substitutes) {
out.emplace_back(std::move(path)); out.emplace_back(std::move(path));
} else { } else {
// Replace the multipath placeholder with each value while generating paths // 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; 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)); out.emplace_back(std::move(branch_path));
} }
} }