@@ -1612,9 +1612,10 @@ enum class ParseScriptContext {
1612
1612
P2WPKH, // !< Inside wpkh() (no script, pubkey only)
1613
1613
P2WSH, // !< Inside wsh() (script becomes v0 witness script)
1614
1614
P2TR, // !< Inside tr() (either internal key, or BIP342 script leaf)
1615
+ MUSIG, // !< Inside musig() (implies P2TR, cannot have nested musig())
1615
1616
};
1616
1617
1617
- std::optional<uint32_t > ParseKeyPathNum (std::span<const char > elem, bool & apostrophe, std::string& error)
1618
+ std::optional<uint32_t > ParseKeyPathNum (std::span<const char > elem, bool & apostrophe, std::string& error, bool & has_hardened )
1618
1619
{
1619
1620
bool hardened = false ;
1620
1621
if (elem.size () > 0 ) {
@@ -1633,6 +1634,7 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
1633
1634
error = strprintf (" Key path value %u is out of range" , *p);
1634
1635
return std::nullopt;
1635
1636
}
1637
+ has_hardened = has_hardened || hardened;
1636
1638
1637
1639
return std::make_optional<uint32_t >(*p | (((uint32_t )hardened) << 31 ));
1638
1640
}
@@ -1647,14 +1649,15 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
1647
1649
* @param[in] allow_multipath Allows the parsed path to use the multipath specifier
1648
1650
* @returns false if parsing failed
1649
1651
**/
1650
- [[nodiscard]] bool ParseKeyPath (const std::vector<std::span<const char >>& split, std::vector<KeyPath>& out, bool & apostrophe, std::string& error, bool allow_multipath)
1652
+ [[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 )
1651
1653
{
1652
1654
KeyPath path;
1653
1655
struct MultipathSubstitutes {
1654
1656
size_t placeholder_index;
1655
1657
std::vector<uint32_t > values;
1656
1658
};
1657
1659
std::optional<MultipathSubstitutes> substitutes;
1660
+ has_hardened = false ;
1658
1661
1659
1662
for (size_t i = 1 ; i < split.size (); ++i) {
1660
1663
const std::span<const char >& elem = split[i];
@@ -1680,7 +1683,7 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
1680
1683
substitutes.emplace ();
1681
1684
std::unordered_set<uint32_t > seen_substitutes;
1682
1685
for (const auto & num : nums) {
1683
- const auto & op_num = ParseKeyPathNum (num, apostrophe, error);
1686
+ const auto & op_num = ParseKeyPathNum (num, apostrophe, error, has_hardened );
1684
1687
if (!op_num) return false ;
1685
1688
auto [_, inserted] = seen_substitutes.insert (*op_num);
1686
1689
if (!inserted) {
@@ -1693,7 +1696,7 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
1693
1696
path.emplace_back (); // Placeholder for multipath segment
1694
1697
substitutes->placeholder_index = path.size () - 1 ;
1695
1698
} else {
1696
- const auto & op_num = ParseKeyPathNum (elem, apostrophe, error);
1699
+ const auto & op_num = ParseKeyPathNum (elem, apostrophe, error, has_hardened );
1697
1700
if (!op_num) return false ;
1698
1701
path.emplace_back (*op_num);
1699
1702
}
@@ -1712,6 +1715,12 @@ std::optional<uint32_t> ParseKeyPathNum(std::span<const char> elem, bool& apostr
1712
1715
return true ;
1713
1716
}
1714
1717
1718
+ [[nodiscard]] bool ParseKeyPath (const std::vector<std::span<const char >>& split, std::vector<KeyPath>& out, bool & apostrophe, std::string& error, bool allow_multipath)
1719
+ {
1720
+ bool dummy;
1721
+ return ParseKeyPath (split, out, apostrophe, error, allow_multipath, /* has_hardened=*/ dummy);
1722
+ }
1723
+
1715
1724
static DeriveType ParseDeriveType (std::vector<std::span<const char >>& split, bool & apostrophe)
1716
1725
{
1717
1726
DeriveType type = DeriveType::NO;
@@ -1802,9 +1811,154 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
1802
1811
}
1803
1812
1804
1813
/* * Parse a public key including origin information (if enabled). */
1805
- std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey (uint32_t key_exp_index, const std::span<const char >& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
1814
+ // NOLINTNEXTLINE(misc-no-recursion)
1815
+ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey (uint32_t & key_exp_index, const std::span<const char >& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
1806
1816
{
1807
1817
std::vector<std::unique_ptr<PubkeyProvider>> ret;
1818
+
1819
+ using namespace script ;
1820
+
1821
+ // musig cannot be nested inside of an origin
1822
+ std::span<const char > span = sp;
1823
+ if (Const (" musig(" , span, /* skip=*/ false )) {
1824
+ if (ctx != ParseScriptContext::P2TR) {
1825
+ error = " musig() is only allowed in tr() and rawtr()" ;
1826
+ return {};
1827
+ }
1828
+
1829
+ // Split the span on the end parentheses. The end parentheses must
1830
+ // be included in the resulting span so that Expr is happy.
1831
+ auto split = Split (sp, ' )' , /* include_sep=*/ true );
1832
+ if (split.size () > 2 ) {
1833
+ error = " Too many ')' in musig() expression" ;
1834
+ return {};
1835
+ }
1836
+ std::span<const char > expr (split.at (0 ).begin (), split.at (0 ).end ());
1837
+ if (!Func (" musig" , expr)) {
1838
+ error = " Invalid musig() expression" ;
1839
+ return {};
1840
+ }
1841
+
1842
+ // Parse the participant pubkeys
1843
+ bool any_ranged = false ;
1844
+ bool all_bip32 = true ;
1845
+ std::vector<std::vector<std::unique_ptr<PubkeyProvider>>> providers;
1846
+ bool any_key_parsed = true ;
1847
+ size_t max_multipath_len = 0 ;
1848
+ while (expr.size ()) {
1849
+ if (!any_key_parsed && !Const (" ," , expr)) {
1850
+ error = strprintf (" musig(): expected ',', got '%c'" , expr[0 ]);
1851
+ return {};
1852
+ }
1853
+ any_key_parsed = false ;
1854
+ auto arg = Expr (expr);
1855
+ auto pk = ParsePubkey (key_exp_index, arg, ParseScriptContext::MUSIG, out, error);
1856
+ if (pk.empty ()) {
1857
+ error = strprintf (" musig(): %s" , error);
1858
+ return {};
1859
+ }
1860
+
1861
+ any_ranged = any_ranged || pk.at (0 )->IsRange ();
1862
+ all_bip32 = all_bip32 && pk.at (0 )->IsBIP32 ();
1863
+
1864
+ max_multipath_len = std::max (max_multipath_len, pk.size ());
1865
+
1866
+ providers.emplace_back (std::move (pk));
1867
+ key_exp_index++;
1868
+ }
1869
+ if (any_key_parsed) {
1870
+ error = " musig(): Must contain key expressions" ;
1871
+ return {};
1872
+ }
1873
+
1874
+ // Parse any derivation
1875
+ DeriveType deriv_type = DeriveType::NO;
1876
+ std::vector<KeyPath> derivation_multipaths;
1877
+ if (split.size () == 2 && Const (" /" , split.at (1 ), /* skip=*/ false )) {
1878
+ if (!all_bip32) {
1879
+ error = " musig(): derivation requires all participants to be xpubs or xprvs" ;
1880
+ return {};
1881
+ }
1882
+ if (any_ranged) {
1883
+ error = " musig(): Cannot have ranged participant keys if musig() also has derivation" ;
1884
+ return {};
1885
+ }
1886
+ bool dummy = false ;
1887
+ auto deriv_split = Split (split.at (1 ), ' /' );
1888
+ deriv_type = ParseDeriveType (deriv_split, dummy);
1889
+ if (deriv_type == DeriveType::HARDENED) {
1890
+ error = " musig(): Cannot have hardened child derivation" ;
1891
+ return {};
1892
+ }
1893
+ bool has_hardened = false ;
1894
+ if (!ParseKeyPath (deriv_split, derivation_multipaths, dummy, error, /* allow_multipath=*/ true , has_hardened)) {
1895
+ error = " musig(): " + error;
1896
+ return {};
1897
+ }
1898
+ if (has_hardened) {
1899
+ error = " musig(): cannot have hardened derivation steps" ;
1900
+ return {};
1901
+ }
1902
+ } else {
1903
+ derivation_multipaths.emplace_back ();
1904
+ }
1905
+
1906
+ // Makes sure that all providers vectors in providers are the given length, or exactly length 1
1907
+ // Length 1 vectors have the single provider cloned until it matches the given length.
1908
+ const auto & clone_providers = [&providers](size_t length) -> bool {
1909
+ for (auto & multipath_providers : providers) {
1910
+ if (multipath_providers.size () == 1 ) {
1911
+ for (size_t i = 1 ; i < length; ++i) {
1912
+ multipath_providers.emplace_back (multipath_providers.at (0 )->Clone ());
1913
+ }
1914
+ } else if (multipath_providers.size () != length) {
1915
+ return false ;
1916
+ }
1917
+ }
1918
+ return true ;
1919
+ };
1920
+
1921
+ // Emplace the final MuSigPubkeyProvider into ret with the pubkey providers from the specified provider vectors index
1922
+ // and the path from the specified path index
1923
+ const auto & emplace_final_provider = [&ret, &key_exp_index, &deriv_type, &derivation_multipaths, &providers](size_t vec_idx, size_t path_idx) -> void {
1924
+ KeyPath& path = derivation_multipaths.at (path_idx);
1925
+ std::vector<std::unique_ptr<PubkeyProvider>> pubs;
1926
+ pubs.reserve (providers.size ());
1927
+ for (auto & vec : providers) {
1928
+ pubs.emplace_back (std::move (vec.at (vec_idx)));
1929
+ }
1930
+ ret.emplace_back (std::make_unique<MuSigPubkeyProvider>(key_exp_index, std::move (pubs), path, deriv_type));
1931
+ };
1932
+
1933
+ if (max_multipath_len > 1 && derivation_multipaths.size () > 1 ) {
1934
+ error = " musig(): Cannot have multipath participant keys if musig() is also multipath" ;
1935
+ return {};
1936
+ } else if (max_multipath_len > 1 ) {
1937
+ if (!clone_providers (max_multipath_len)) {
1938
+ error = strprintf (" musig(): Multipath derivation paths have mismatched lengths" );
1939
+ return {};
1940
+ }
1941
+ for (size_t i = 0 ; i < max_multipath_len; ++i) {
1942
+ // Final MuSigPubkeyProvider uses participant pubkey providers at each multipath position, and the first (and only) path
1943
+ emplace_final_provider (i, 0 );
1944
+ }
1945
+ } else if (derivation_multipaths.size () > 1 ) {
1946
+ // All key provider vectors should be length 1. Clone them until they have the same length as paths
1947
+ if (!Assume (clone_providers (derivation_multipaths.size ()))) {
1948
+ error = " musig(): Multipath derivation path with multipath participants is disallowed" ; // This error is unreachable due to earlier check
1949
+ return {};
1950
+ }
1951
+ for (size_t i = 0 ; i < derivation_multipaths.size (); ++i) {
1952
+ // Final MuSigPubkeyProvider uses cloned participant pubkey providers, and the multipath derivation paths
1953
+ emplace_final_provider (i, i);
1954
+ }
1955
+ } else {
1956
+ // No multipath derivation, MuSigPubkeyProvider uses the first (and only) participant pubkey providers, and the first (and only) path
1957
+ emplace_final_provider (0 , 0 );
1958
+ }
1959
+ return ret;
1960
+ }
1961
+
1808
1962
auto origin_split = Split (sp, ' ]' );
1809
1963
if (origin_split.size () > 2 ) {
1810
1964
error = " Multiple ']' characters found for a single pubkey" ;
@@ -1915,7 +2069,8 @@ struct KeyParser {
1915
2069
{
1916
2070
assert (m_out);
1917
2071
Key key = m_keys.size ();
1918
- auto pk = ParsePubkey (m_offset + key, {&*begin, &*end}, ParseContext (), *m_out, m_key_parsing_error);
2072
+ uint32_t exp_index = m_offset + key;
2073
+ auto pk = ParsePubkey (exp_index, {&*begin, &*end}, ParseContext (), *m_out, m_key_parsing_error);
1919
2074
if (pk.empty ()) return {};
1920
2075
m_keys.emplace_back (std::move (pk));
1921
2076
return key;
0 commit comments