@@ -1410,37 +1410,96 @@ enum class ParseScriptContext {
1410
1410
P2TR, // !< Inside tr() (either internal key, or BIP342 script leaf)
1411
1411
};
1412
1412
1413
+ std::optional<uint32_t > ParseKeyPathNum (Span<const char > elem, bool & apostrophe, std::string& error)
1414
+ {
1415
+ bool hardened = false ;
1416
+ if (elem.size () > 0 ) {
1417
+ const char last = elem[elem.size () - 1 ];
1418
+ if (last == ' \' ' || last == ' h' ) {
1419
+ elem = elem.first (elem.size () - 1 );
1420
+ hardened = true ;
1421
+ apostrophe = last == ' \' ' ;
1422
+ }
1423
+ }
1424
+ uint32_t p;
1425
+ if (!ParseUInt32 (std::string (elem.begin (), elem.end ()), &p)) {
1426
+ error = strprintf (" Key path value '%s' is not a valid uint32" , std::string (elem.begin (), elem.end ()));
1427
+ return std::nullopt;
1428
+ } else if (p > 0x7FFFFFFFUL ) {
1429
+ error = strprintf (" Key path value %u is out of range" , p);
1430
+ return std::nullopt;
1431
+ }
1432
+
1433
+ return std::make_optional<uint32_t >(p | (((uint32_t )hardened) << 31 ));
1434
+ }
1435
+
1413
1436
/* *
1414
- * Parse a key path, being passed a split list of elements (the first element is ignored).
1437
+ * Parse a key path, being passed a split list of elements (the first element is ignored because it is always the key ).
1415
1438
*
1416
1439
* @param[in] split BIP32 path string, using either ' or h for hardened derivation
1417
- * @param[out] out the key path
1440
+ * @param[out] out Vector of parsed key paths
1418
1441
* @param[out] apostrophe only updated if hardened derivation is found
1419
1442
* @param[out] error parsing error message
1443
+ * @param[in] allow_multipath Allows the parsed path to use the multipath specifier
1420
1444
* @returns false if parsing failed
1421
1445
**/
1422
- [[nodiscard]] bool ParseKeyPath (const std::vector<Span<const char >>& split, KeyPath& out, bool & apostrophe, std::string& error)
1446
+ [[nodiscard]] bool ParseKeyPath (const std::vector<Span<const char >>& split, std::vector< KeyPath> & out, bool & apostrophe, std::string& error, bool allow_multipath )
1423
1447
{
1448
+ KeyPath path;
1449
+ std::optional<size_t > multipath_segment_index;
1450
+ std::vector<uint32_t > multipath_values;
1451
+ std::unordered_set<uint32_t > seen_multipath;
1452
+
1424
1453
for (size_t i = 1 ; i < split.size (); ++i) {
1425
- Span<const char > elem = split[i];
1426
- bool hardened = false ;
1427
- if (elem.size () > 0 ) {
1428
- const char last = elem[elem.size () - 1 ];
1429
- if (last == ' \' ' || last == ' h' ) {
1430
- elem = elem.first (elem.size () - 1 );
1431
- hardened = true ;
1432
- apostrophe = last == ' \' ' ;
1454
+ const Span<const char >& elem = split[i];
1455
+
1456
+ // Check if element contain multipath specifier
1457
+ if (!elem.empty () && elem.front () == ' <' && elem.back () == ' >' ) {
1458
+ if (!allow_multipath) {
1459
+ error = strprintf (" Key path value '%s' specifies multipath in a section where multipath is not allowed" , std::string (elem.begin (), elem.end ()));
1460
+ return false ;
1461
+ }
1462
+ if (multipath_segment_index) {
1463
+ error = " Multiple multipath key path specifiers found" ;
1464
+ return false ;
1465
+ }
1466
+
1467
+ // Parse each possible value
1468
+ std::vector<Span<const char >> nums = Split (Span (elem.begin ()+1 , elem.end ()-1 ), " ;" );
1469
+ if (nums.size () < 2 ) {
1470
+ error = " Multipath key path specifiers must have at least two items" ;
1471
+ return false ;
1472
+ }
1473
+
1474
+ for (const auto & num : nums) {
1475
+ const auto & op_num = ParseKeyPathNum (num, apostrophe, error);
1476
+ if (!op_num) return false ;
1477
+ auto [_, inserted] = seen_multipath.insert (*op_num);
1478
+ if (!inserted) {
1479
+ error = strprintf (" Duplicated key path value %u in multipath specifier" , *op_num);
1480
+ return false ;
1481
+ }
1482
+ multipath_values.emplace_back (*op_num);
1433
1483
}
1484
+
1485
+ path.emplace_back (); // Placeholder for multipath segment
1486
+ multipath_segment_index = path.size ()-1 ;
1487
+ } else {
1488
+ const auto & op_num = ParseKeyPathNum (elem, apostrophe, error);
1489
+ if (!op_num) return false ;
1490
+ path.emplace_back (*op_num);
1434
1491
}
1435
- uint32_t p;
1436
- if (!ParseUInt32 (std::string (elem.begin (), elem.end ()), &p)) {
1437
- error = strprintf (" Key path value '%s' is not a valid uint32" , std::string (elem.begin (), elem.end ()));
1438
- return false ;
1439
- } else if (p > 0x7FFFFFFFUL ) {
1440
- error = strprintf (" Key path value %u is out of range" , p);
1441
- return false ;
1492
+ }
1493
+
1494
+ if (!multipath_segment_index) {
1495
+ out.emplace_back (std::move (path));
1496
+ } else {
1497
+ // Replace the multipath placeholder with each value while generating paths
1498
+ for (size_t i = 0 ; i < multipath_values.size (); i++) {
1499
+ KeyPath branch_path = path;
1500
+ branch_path[*multipath_segment_index] = multipath_values[i];
1501
+ out.emplace_back (std::move (branch_path));
1442
1502
}
1443
- out.push_back (p | (((uint32_t )hardened) << 31 ));
1444
1503
}
1445
1504
return true ;
1446
1505
}
@@ -1503,7 +1562,7 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
1503
1562
error = strprintf (" key '%s' is not valid" , str);
1504
1563
return {};
1505
1564
}
1506
- KeyPath path ;
1565
+ std::vector< KeyPath> paths ;
1507
1566
DeriveType type = DeriveType::NO;
1508
1567
if (split.back () == Span{" *" }.first (1 )) {
1509
1568
split.pop_back ();
@@ -1513,12 +1572,14 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkeyInner(uint32_t key_exp_i
1513
1572
split.pop_back ();
1514
1573
type = DeriveType::HARDENED;
1515
1574
}
1516
- if (!ParseKeyPath (split, path , apostrophe, error)) return {};
1575
+ if (!ParseKeyPath (split, paths , apostrophe, error, /* allow_multipath= */ true )) return {};
1517
1576
if (extkey.key .IsValid ()) {
1518
1577
extpubkey = extkey.Neuter ();
1519
1578
out.keys .emplace (extpubkey.pubkey .GetID (), extkey.key );
1520
1579
}
1521
- ret.emplace_back (std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move (path), type, apostrophe));
1580
+ for (auto & path : paths) {
1581
+ ret.emplace_back (std::make_unique<BIP32PubkeyProvider>(key_exp_index, extpubkey, std::move (path), type, apostrophe));
1582
+ }
1522
1583
return ret;
1523
1584
}
1524
1585
@@ -1556,7 +1617,9 @@ std::vector<std::unique_ptr<PubkeyProvider>> ParsePubkey(uint32_t key_exp_index,
1556
1617
static_assert (sizeof (info.fingerprint ) == 4 , " Fingerprint must be 4 bytes" );
1557
1618
assert (fpr_bytes.size () == 4 );
1558
1619
std::copy (fpr_bytes.begin (), fpr_bytes.end (), info.fingerprint );
1559
- if (!ParseKeyPath (slash_split, info.path , apostrophe, error)) return {};
1620
+ std::vector<KeyPath> path;
1621
+ if (!ParseKeyPath (slash_split, path, apostrophe, error, /* allow_multipath=*/ false )) return {};
1622
+ info.path = path.at (0 );
1560
1623
auto providers = ParsePubkeyInner (key_exp_index, origin_split[1 ], ctx, out, apostrophe, error);
1561
1624
if (providers.empty ()) return {};
1562
1625
ret.reserve (providers.size ());
0 commit comments