There appears to be an issue with the following MuSig descriptor that fails the test:
One of the MuSig portion in the descriptor doesn't contain any private key for which the listdescriptors(true) returns a pk() instead of defaulting to the public keys whereas the listdescriptors() works fine.
desc = descsum_create('tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})')
result = wallet.importdescriptors([{
'desc': desc,
'timestamp': TIME_GENESIS_BLOCK,
}])
result = wallet.listdescriptors(True)
result = wallet.listdescriptors()
Debug Output for result object.
2025-10-03T09:56:03.473944Z TestFramework (INFO): Test descriptor with missing private keys
'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#jd2qx33n'
[{'success': True,
'warnings': ['Range not given, using default keypool range',
'Not all private keys provided. Some wallet functionality may '
'return unexpected errors']}]
{'wallet_name': 'w5',
'descriptors': [{'desc': 'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#53ynphm7',
'timestamp': 1296688602,
'active': False,
'range': [0, 0],
'next': 0,
'next_index': 0}]}
{'wallet_name': 'w5',
'descriptors': [{'desc': 'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tpubD6NzVbkrYhZ4XqNGAWGWSzmxGWFwVjVTjZxh2fioKbVYi7Jx8fdbprVWsdW7mHwqjchBVas8TLZG4Xwuz4RKU4iaCqiCvoSkFCzQptqk5Y1,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#8396h37a',
'timestamp': 1296688602,
'active': False,
'range': [0, 0],
'next': 0,
'next_index': 0}]}
The following patch in MuSigPubkeyProvider seems to fix it.
out doesn't need to be cleared if no priv keys found. For non watch-only wallets, a descriptor without any private keys can't be imported anyway so there can't be a case wherein we need to propagate this check from MuSigPubkeyProvider to TrDescriptor.
It can be assumed that any other portion of the descriptor will have a private key.
I haven't verified this^ assumption for every possible scenario and I suggest adding few more cases in this test wherein a private key is present in different portions of the tr() descriptor with musig(). Few templates for reference from #29675 PR: https://github.com/bitcoin/bitcoin/pull/29675/commits/ac599c4a9cb3b2d424932d3fd91f9eed17426827#diff-c94f9555a2569240d362a00a82764f4714f6c65283e3f96725cf2a1043a296b5R224-R238
- After this PR,
pubkey->ToPrivateString defaults to returning the public key in tmp, so the subsequent else block is redundant now.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 6eafcaa94c..76d4c8bb02 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -726,17 +726,14 @@ public:
std::string tmp;
if (pubkey->ToPrivateString(arg, tmp)) {
any_privkeys = true;
- out += tmp;
- } else {
- out += pubkey->ToString();
}
+ out += tmp;
}
out += ")";
out += FormatHDKeypath(m_path);
if (IsRangedDerivation()) {
out += "/*";
}
- if (!any_privkeys) out.clear();
return any_privkeys;
}
bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const override