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.
0 desc = descsum_create('tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})')
1 result = wallet.importdescriptors([{
2 'desc': desc,
3 'timestamp': TIME_GENESIS_BLOCK,
4 }])
5 result = wallet.listdescriptors(True)
6 result = wallet.listdescriptors()
Debug Output for result
object.
02025-10-03T09:56:03.473944Z TestFramework (INFO): Test descriptor with missing private keys
1'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#jd2qx33n'
2[{'success': True,
3 'warnings': ['Range not given, using default keypool range',
4 'Not all private keys provided. Some wallet functionality may '
5 'return unexpected errors']}]
6{'wallet_name': 'w5',
7 'descriptors': [{'desc': 'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(),pk(musig(tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#53ynphm7',
8 'timestamp': 1296688602,
9 'active': False,
10 'range': [0, 0],
11 'next': 0,
12 'next_index': 0}]}
13{'wallet_name': 'w5',
14 'descriptors': [{'desc': 'tr(03dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659,{pk(musig(tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR,tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6)/12/*),pk(musig(tpubD6NzVbkrYhZ4XqNGAWGWSzmxGWFwVjVTjZxh2fioKbVYi7Jx8fdbprVWsdW7mHwqjchBVas8TLZG4Xwuz4RKU4iaCqiCvoSkFCzQptqk5Y1,tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy)/13/*)})#8396h37a',
15 'timestamp': 1296688602,
16 'active': False,
17 'range': [0, 0],
18 'next': 0,
19 '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.
0diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
1index 6eafcaa94c..76d4c8bb02 100644
2--- a/src/script/descriptor.cpp
3+++ b/src/script/descriptor.cpp
4@@ -726,17 +726,14 @@ public:
5 std::string tmp;
6 if (pubkey->ToPrivateString(arg, tmp)) {
7 any_privkeys = true;
8- out += tmp;
9- } else {
10- out += pubkey->ToString();
11 }
12+ out += tmp;
13 }
14 out += ")";
15 out += FormatHDKeypath(m_path);
16 if (IsRangedDerivation()) {
17 out += "/*";
18 }
19- if (!any_privkeys) out.clear();
20 return any_privkeys;
21 }
22 bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache = nullptr) const override