@@ -1463,7 +1463,6 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
1463
1463
1464
1464
const std::string& descriptor = data[" desc" ].get_str ();
1465
1465
const bool active = data.exists (" active" ) ? data[" active" ].get_bool () : false ;
1466
- const bool internal = data.exists (" internal" ) ? data[" internal" ].get_bool () : false ;
1467
1466
const std::string label{LabelFromValue (data[" label" ])};
1468
1467
1469
1468
// Parse descriptor string
@@ -1473,13 +1472,19 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
1473
1472
if (parsed_descs.empty ()) {
1474
1473
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, error);
1475
1474
}
1476
- auto & parsed_desc = parsed_descs.at (0 );
1475
+ std::optional<bool > internal;
1476
+ if (data.exists (" internal" )) {
1477
+ if (parsed_descs.size () > 1 ) {
1478
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Cannot have multipath descriptor while also specifying \' internal\' " );
1479
+ }
1480
+ internal = data[" internal" ].get_bool ();
1481
+ }
1477
1482
1478
1483
// Range check
1479
1484
int64_t range_start = 0 , range_end = 1 , next_index = 0 ;
1480
- if (!parsed_desc ->IsRange () && data.exists (" range" )) {
1485
+ if (!parsed_descs. at ( 0 ) ->IsRange () && data.exists (" range" )) {
1481
1486
throw JSONRPCError (RPC_INVALID_PARAMETER, " Range should not be specified for an un-ranged descriptor" );
1482
- } else if (parsed_desc ->IsRange ()) {
1487
+ } else if (parsed_descs. at ( 0 ) ->IsRange ()) {
1483
1488
if (data.exists (" range" )) {
1484
1489
auto range = ParseDescriptorRange (data[" range" ]);
1485
1490
range_start = range.first ;
@@ -1501,10 +1506,15 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
1501
1506
}
1502
1507
1503
1508
// Active descriptors must be ranged
1504
- if (active && !parsed_desc ->IsRange ()) {
1509
+ if (active && !parsed_descs. at ( 0 ) ->IsRange ()) {
1505
1510
throw JSONRPCError (RPC_INVALID_PARAMETER, " Active descriptors must be ranged" );
1506
1511
}
1507
1512
1513
+ // Multipath descriptors should not have a label
1514
+ if (parsed_descs.size () > 1 && data.exists (" label" )) {
1515
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Multipath descriptors should not have a label" );
1516
+ }
1517
+
1508
1518
// Ranged descriptors should not have a label
1509
1519
if (data.exists (" range" ) && data.exists (" label" )) {
1510
1520
throw JSONRPCError (RPC_INVALID_PARAMETER, " Ranged descriptors should not have a label" );
@@ -1516,7 +1526,7 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
1516
1526
}
1517
1527
1518
1528
// Combo descriptor check
1519
- if (active && !parsed_desc ->IsSingleType ()) {
1529
+ if (active && !parsed_descs. at ( 0 ) ->IsSingleType ()) {
1520
1530
throw JSONRPCError (RPC_WALLET_ERROR, " Combo descriptors cannot be set to active" );
1521
1531
}
1522
1532
@@ -1525,61 +1535,70 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
1525
1535
throw JSONRPCError (RPC_WALLET_ERROR, " Cannot import private keys to a wallet with private keys disabled" );
1526
1536
}
1527
1537
1528
- // Need to ExpandPrivate to check if private keys are available for all pubkeys
1529
- FlatSigningProvider expand_keys;
1530
- std::vector<CScript> scripts;
1531
- if (!parsed_desc->Expand (0 , keys, scripts, expand_keys)) {
1532
- throw JSONRPCError (RPC_WALLET_ERROR, " Cannot expand descriptor. Probably because of hardened derivations without private keys provided" );
1533
- }
1534
- parsed_desc->ExpandPrivate (0 , keys, expand_keys);
1535
-
1536
- // Check if all private keys are provided
1537
- bool have_all_privkeys = !expand_keys.keys .empty ();
1538
- for (const auto & entry : expand_keys.origins ) {
1539
- const CKeyID& key_id = entry.first ;
1540
- CKey key;
1541
- if (!expand_keys.GetKey (key_id, key)) {
1542
- have_all_privkeys = false ;
1543
- break ;
1538
+ for (size_t j = 0 ; j < parsed_descs.size (); ++j) {
1539
+ auto parsed_desc = std::move (parsed_descs[j]);
1540
+ bool desc_internal = internal.has_value () && internal.value ();
1541
+ if (parsed_descs.size () == 2 ) {
1542
+ desc_internal = j == 1 ;
1543
+ } else if (parsed_descs.size () > 2 ) {
1544
+ CHECK_NONFATAL (!desc_internal);
1545
+ }
1546
+ // Need to ExpandPrivate to check if private keys are available for all pubkeys
1547
+ FlatSigningProvider expand_keys;
1548
+ std::vector<CScript> scripts;
1549
+ if (!parsed_desc->Expand (0 , keys, scripts, expand_keys)) {
1550
+ throw JSONRPCError (RPC_WALLET_ERROR, " Cannot expand descriptor. Probably because of hardened derivations without private keys provided" );
1551
+ }
1552
+ parsed_desc->ExpandPrivate (0 , keys, expand_keys);
1553
+
1554
+ // Check if all private keys are provided
1555
+ bool have_all_privkeys = !expand_keys.keys .empty ();
1556
+ for (const auto & entry : expand_keys.origins ) {
1557
+ const CKeyID& key_id = entry.first ;
1558
+ CKey key;
1559
+ if (!expand_keys.GetKey (key_id, key)) {
1560
+ have_all_privkeys = false ;
1561
+ break ;
1562
+ }
1544
1563
}
1545
- }
1546
1564
1547
- // If private keys are enabled, check some things.
1548
- if (!wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1549
- if (keys.keys .empty ()) {
1550
- throw JSONRPCError (RPC_WALLET_ERROR, " Cannot import descriptor without private keys to a wallet with private keys enabled" );
1551
- }
1552
- if (!have_all_privkeys) {
1553
- warnings.push_back (" Not all private keys provided. Some wallet functionality may return unexpected errors" );
1554
- }
1555
- }
1565
+ // If private keys are enabled, check some things.
1566
+ if (!wallet.IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
1567
+ if (keys.keys .empty ()) {
1568
+ throw JSONRPCError (RPC_WALLET_ERROR, " Cannot import descriptor without private keys to a wallet with private keys enabled" );
1569
+ }
1570
+ if (!have_all_privkeys) {
1571
+ warnings.push_back (" Not all private keys provided. Some wallet functionality may return unexpected errors" );
1572
+ }
1573
+ }
1556
1574
1557
- WalletDescriptor w_desc (std::move (parsed_desc), timestamp, range_start, range_end, next_index);
1575
+ WalletDescriptor w_desc (std::move (parsed_desc), timestamp, range_start, range_end, next_index);
1558
1576
1559
- // Check if the wallet already contains the descriptor
1560
- auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan (w_desc);
1561
- if (existing_spk_manager) {
1562
- if (!existing_spk_manager->CanUpdateToWalletDescriptor (w_desc, error)) {
1563
- throw JSONRPCError (RPC_INVALID_PARAMETER, error);
1577
+ // Check if the wallet already contains the descriptor
1578
+ auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan (w_desc);
1579
+ if (existing_spk_manager) {
1580
+ if (!existing_spk_manager->CanUpdateToWalletDescriptor (w_desc, error)) {
1581
+ throw JSONRPCError (RPC_INVALID_PARAMETER, error);
1582
+ }
1564
1583
}
1565
- }
1566
1584
1567
- // Add descriptor to the wallet
1568
- auto spk_manager = wallet.AddWalletDescriptor (w_desc, keys, label, internal );
1569
- if (spk_manager == nullptr ) {
1570
- throw JSONRPCError (RPC_WALLET_ERROR, strprintf (" Could not add descriptor '%s'" , descriptor));
1571
- }
1585
+ // Add descriptor to the wallet
1586
+ auto spk_manager = wallet.AddWalletDescriptor (w_desc, keys, label, desc_internal );
1587
+ if (spk_manager == nullptr ) {
1588
+ throw JSONRPCError (RPC_WALLET_ERROR, strprintf (" Could not add descriptor '%s'" , descriptor));
1589
+ }
1572
1590
1573
- // Set descriptor as active if necessary
1574
- if (active) {
1575
- if (!w_desc.descriptor ->GetOutputType ()) {
1576
- warnings.push_back (" Unknown output type, cannot set descriptor to active." );
1591
+ // Set descriptor as active if necessary
1592
+ if (active) {
1593
+ if (!w_desc.descriptor ->GetOutputType ()) {
1594
+ warnings.push_back (" Unknown output type, cannot set descriptor to active." );
1595
+ } else {
1596
+ wallet.AddActiveScriptPubKeyMan (spk_manager->GetID (), *w_desc.descriptor ->GetOutputType (), desc_internal);
1597
+ }
1577
1598
} else {
1578
- wallet.AddActiveScriptPubKeyMan (spk_manager->GetID (), *w_desc.descriptor ->GetOutputType (), internal);
1579
- }
1580
- } else {
1581
- if (w_desc.descriptor ->GetOutputType ()) {
1582
- wallet.DeactivateScriptPubKeyMan (spk_manager->GetID (), *w_desc.descriptor ->GetOutputType (), internal);
1599
+ if (w_desc.descriptor ->GetOutputType ()) {
1600
+ wallet.DeactivateScriptPubKeyMan (spk_manager->GetID (), *w_desc.descriptor ->GetOutputType (), desc_internal);
1601
+ }
1583
1602
}
1584
1603
}
1585
1604
@@ -1596,6 +1615,7 @@ RPCHelpMan importdescriptors()
1596
1615
{
1597
1616
return RPCHelpMan{" importdescriptors" ,
1598
1617
" \n Import descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n "
1618
+ " When importing descriptors with multipath key expressions, if the multipath specifier contains exactly two elements, the descriptor produced from the second elements will be imported as an internal descriptor.\n "
1599
1619
" \n Note: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n "
1600
1620
" may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n "
1601
1621
" The rescan is significantly faster if block filters are available (using startup option \" -blockfilterindex=1\" ).\n " ,
0 commit comments