* Parse a key path, being passed a split list of elements (the first element is ignored because it is always the key). * * @param[in] split BIP32 path string, using either ' or h for hardened derivation * @param[out] out Vector of parsed key paths * @param[out] apostrophe only updated if hardened derivation is found * @param[out] error parsing error message * @param[in] allow_multipath Allow
| 1849 | * @returns false if parsing failed |
| 1850 | **/ |
| 1851 | [[nodiscard]] bool ParseKeyPath(const std::vector<std::span<const char>>& split, std::vector<KeyPath>& out, bool& apostrophe, std::string& error, bool allow_multipath, bool& has_hardened) |
| 1852 | { |
| 1853 | KeyPath path; |
| 1854 | struct MultipathSubstitutes { |
| 1855 | size_t placeholder_index; |
| 1856 | std::vector<uint32_t> values; |
| 1857 | }; |
| 1858 | std::optional<MultipathSubstitutes> substitutes; |
| 1859 | has_hardened = false; |
| 1860 | |
| 1861 | for (size_t i = 1; i < split.size(); ++i) { |
| 1862 | const std::span<const char>& elem = split[i]; |
| 1863 | |
| 1864 | // Check if element contains multipath specifier |
| 1865 | if (!elem.empty() && elem.front() == '<' && elem.back() == '>') { |
| 1866 | if (!allow_multipath) { |
| 1867 | error = strprintf("Key path value '%s' specifies multipath in a section where multipath is not allowed", std::string(elem.begin(), elem.end())); |
| 1868 | return false; |
| 1869 | } |
| 1870 | if (substitutes) { |
| 1871 | error = "Multiple multipath key path specifiers found"; |
| 1872 | return false; |
| 1873 | } |
| 1874 | |
| 1875 | // Parse each possible value |
| 1876 | std::vector<std::span<const char>> nums = Split(std::span(elem.begin()+1, elem.end()-1), ";"); |
| 1877 | if (nums.size() < 2) { |
| 1878 | error = "Multipath key path specifiers must have at least two items"; |
| 1879 | return false; |
| 1880 | } |
| 1881 | |
| 1882 | substitutes.emplace(); |
| 1883 | std::unordered_set<uint32_t> seen_substitutes; |
| 1884 | for (const auto& num : nums) { |
| 1885 | const auto& op_num = ParseKeyPathNum(num, apostrophe, error, has_hardened); |
| 1886 | if (!op_num) return false; |
| 1887 | auto [_, inserted] = seen_substitutes.insert(*op_num); |
| 1888 | if (!inserted) { |
| 1889 | error = strprintf("Duplicated key path value %u in multipath specifier", *op_num); |
| 1890 | return false; |
| 1891 | } |
| 1892 | substitutes->values.emplace_back(*op_num); |
| 1893 | } |
| 1894 | |
| 1895 | path.emplace_back(); // Placeholder for multipath segment |
| 1896 | substitutes->placeholder_index = path.size() - 1; |
| 1897 | } else { |
| 1898 | const auto& op_num = ParseKeyPathNum(elem, apostrophe, error, has_hardened); |
| 1899 | if (!op_num) return false; |
| 1900 | path.emplace_back(*op_num); |
| 1901 | } |
| 1902 | } |
| 1903 | |
| 1904 | if (!substitutes) { |
| 1905 | out.emplace_back(std::move(path)); |
| 1906 | } else { |
| 1907 | // Replace the multipath placeholder with each value while generating paths |
| 1908 | for (uint32_t substitute : substitutes->values) { |