Skip to content

16.3: Using Miniscript with Descriptors

Miniscript and Policy are great for designing scripts outside of Bitcoin Core that can then be converted into Bitcoin Script and turned into addresses.

Some wallets offer deeper usage of Miniscript, but Bitcoin Core integration currently remains limited: to date you can only use Miniscript with Bitcoin Core to create read-only P2WSH addresses. Doing so requires a combination of two things you've already learned: first you convert from Policy to Miniscript; then you create a descriptor to read into Bitcoin Core. (We call the result a "minidescriptor", but that's not a formal term.)

Create a P2WSH Minidescriptor

Creating a minidescriptor for import into Bitcoin Core is a simple five-step process.

1. Create a Spending Policy

We're going to use our president & vice-president policy from previous chapters:

or(pk(president),thresh(2,pk(vp1),pk(vp2),pk(vp3)))

2. Convert the Policy to Miniscript

Compiling our Policy at bitcoin.sipa.be gives us our Miniscript:

or_d(pk(president),multi(2,vp1,vp2,vp3))

3. Gather the Keys

To enter a minidescriptor into Bitcoin Core requires that you fill in the keys that should be used using base58 format. Since these Miniscripts are currently read-only, that means entering the xpubs,

Various people involved in a Minidescriptor might have created their keys in a variety of ways. Our executives used seedtool and keytool from chapter 10 to create their secrets as follows:

SEEDP=$(seedtool)
KEYP=$(keytool --seed $SEEDP --account-derivation-path m/84h/1h/0h/0h --network testnet account-pub-key-base58)
echo $KEYP

| tpubDFhGpPUEJaipdEeJtXZAUwb3Hz34hRCCqhvNgRwzrnbsGfXhCNEroYbGVHaVAyTAmJK4HXxLGCTXjP9HjwQRnG3oW7hCFY96JZkmo6kkCdt

SEEDVP1=$(seedtool)
KEYVP1=$(keytool --seed $SEEDVP1 --account-derivation-path m/84h/1h/0h/0h --network testnet account-pub-key-base58)
echo $KEYVP1

|tpubDE1YvUwRqg2N2JfU5LKDGuuv3AM2d2CgMYQauAxgoWnGMVaHETz8n2Cdz3VGi9bbfFeY9dRrydeshyQzqPBLPwSEV8hRBZj5JMEqV3rTkc6

SEEDVP2=$(seedtool)
KEYVP2=$(keytool --seed $SEEDVP2 --account-derivation-path m/84h/1h/0h/0h --network testnet account-pub-key-base58)
echo $KEYVP2

| tpubDE8xY8eWp6CZcdeVQFYG7c2H12GeUpN5j5WLpGEYxurygx5HfjkcbN28vjMmTdYHRNvPauKRsKdgwuJ4hiyJ93gnXPadBoiZeUUJsQ2sP37

SEEDVP3=$(seedtool)
KEYVP3=$(keytool --seed $SEEDVP3 --account-derivation-path m/84h/1h/0h/0h --network testnet account-pub-key-base58)
echo $KEYVP3

| tpubDE4kssfB4wqobfE3GWmYFkyV7giscXNG9HbXu2NykHoF1z9Tf4osozgPX4VZ8MnWxytKfeXAa89oZi9NUNauoLYcY51gFghUia34aAuj6sb

4. Place the Keys in the Minidescriptor

We gathered 84h/1h/0h/0h keys. They're intended to support coins that are Segwit (84h), testnet (1h), first account (0h), non-change (0h). To allow for a range of addresses, we then supplement each key with a /* when we place it in our minscript.

(⚠️ We would have liked to collect 84h/1h/0h keys and then defined them as 0h/*, which would be more normative use, but Bitcoin Core currently flags that as "duplicate keys" if you try, even though they're not.)

EXEC_MINID=$(echo "or_d(pk($KEYP/*),multi(2,$KEYVP1/*,$KEYVP2/*,$KEYVP3/*))")
echo $EXEC_MINID

| or_d(pk(tpubDFhGpPUEJaipdEeJtXZAUwb3Hz34hRCCqhvNgRwzrnbsGfXhCNEroYbGVHaVAyTAmJK4HXxLGCTXjP9HjwQRnG3oW7hCFY96JZkmo6kkCdt/*),multi(2,tpubDE1YvUwRqg2N2JfU5LKDGuuv3AM2d2CgMYQauAxgoWnGMVaHETz8n2Cdz3VGi9bbfFeY9dRrydeshyQzqPBLPwSEV8hRBZj5JMEqV3rTkc6/*,tpubDE8xY8eWp6CZcdeVQFYG7c2H12GeUpN5j5WLpGEYxurygx5HfjkcbN28vjMmTdYHRNvPauKRsKdgwuJ4hiyJ93gnXPadBoiZeUUJsQ2sP37/*,tpubDE4kssfB4wqobfE3GWmYFkyV7giscXNG9HbXu2NykHoF1z9Tf4osozgPX4VZ8MnWxytKfeXAa89oZi9NUNauoLYcY51gFghUia34aAuj6sb/*))
  1. Make a Wish

To turn our Miniscript into a descriptor (a minidescriptor) requires one more thing. It has to be defined with a normal descriptor function.

Minidescriptors are currently only readable in Bitcoin Core as P2WSH addresses (not P2TR addresses). That requires a final step of embedding the Minidescriptor in wsh(...) to create a legal descriptor for Bitcoin Core to read.

EXEC_MINID=$(echo "wsh($EXEC_MINID)")
echo $EXEC_MINID

| wsh(or_d(pk(tpubDFhGpPUEJaipdEeJtXZAUwb3Hz34hRCCqhvNgRwzrnbsGfXhCNEroYbGVHaVAyTAmJK4HXxLGCTXjP9HjwQRnG3oW7hCFY96JZkmo6kkCdt/*),multi(2,tpubDE1YvUwRqg2N2JfU5LKDGuuv3AM2d2CgMYQauAxgoWnGMVaHETz8n2Cdz3VGi9bbfFeY9dRrydeshyQzqPBLPwSEV8hRBZj5JMEqV3rTkc6/*,tpubDE8xY8eWp6CZcdeVQFYG7c2H12GeUpN5j5WLpGEYxurygx5HfjkcbN28vjMmTdYHRNvPauKRsKdgwuJ4hiyJ93gnXPadBoiZeUUJsQ2sP37/*,tpubDE4kssfB4wqobfE3GWmYFkyV7giscXNG9HbXu2NykHoF1z9Tf4osozgPX4VZ8MnWxytKfeXAa89oZi9NUNauoLYcY51gFghUia34aAuj6sb/*)))

Import a P2WSH Minidescriptor

The Minidescriptor that you've created should theoretically be readable anywhere that supports Miniscript descriptors. To incorporate it specifically into Bitcoin Core requires the following additional steps:

6. Add the Checksum

As you'll recall, you always need a checksum for Bitcoin Core descriptors.

EXEC_MINID_CS=$(bitcoin-cli getdescriptorinfo $EXEC_MINID | jq -r '.checksum')
EXEC_MINID=$(echo $EXEC_MINID#$EXEC_MINID_CS)
echo $EXEC_MINID

| wsh(or_d(pk(tpubDFhGpPUEJaipdEeJtXZAUwb3Hz34hRCCqhvNgRwzrnbsGfXhCNEroYbGVHaVAyTAmJK4HXxLGCTXjP9HjwQRnG3oW7hCFY96JZkmo6kkCdt/*),multi(2,tpubDE1YvUwRqg2N2JfU5LKDGuuv3AM2d2CgMYQauAxgoWnGMVaHETz8n2Cdz3VGi9bbfFeY9dRrydeshyQzqPBLPwSEV8hRBZj5JMEqV3rTkc6/*,tpubDE8xY8eWp6CZcdeVQFYG7c2H12GeUpN5j5WLpGEYxurygx5HfjkcbN28vjMmTdYHRNvPauKRsKdgwuJ4hiyJ93gnXPadBoiZeUUJsQ2sP37/*,tpubDE4kssfB4wqobfE3GWmYFkyV7giscXNG9HbXu2NykHoF1z9Tf4osozgPX4VZ8MnWxytKfeXAa89oZi9NUNauoLYcY51gFghUia34aAuj6sb/*)))#fhq2j999

7. Double-check Your Addresses

Before you import your Minidescriptor it's best practice to make sure you have the right one. You can check this by verifying the addresses it creates with the deriveaddresses command.

bitcoin-cli deriveaddresses $EXEC_MINID 2

| [
|   "tb1q5wj3hm4vask5zk2xzgdgw9slcpufml37yjnh0jhwf2c3sguuymhqsxc2gd",
|   "tb1qyxz7dv9859evxy5uutn3ueer978l73fw9ew0r2ajnj2lp5xltzpsrarv0q",
|   "tb1qy2s9l6jkhpnwfeyrsl88jj7e88a9k0dw0c8fyudeqv5lnqapm86q69zuue"
| ]

Presumably you've got another version of this miniscripted account somewhere else, on the wallet you're spending from. You should check the range of addresses you derived with Bitcoin Core against the first few addresses on that other wallet and make sure they are the same.

8. Import Your Minidescriptor

Finally, you're ready to import your minidescriptor. You want to do so into a brand-new wallet:

bitcoin-cli -named createwallet wallet_name="watchmini" disable_private_keys=true blank=true

Like when we imported other watch-only addresses in §7.1, we create the wallet without any private keys.

Now you can import:

bitcoin-cli -rpcwallet=watchmini importdescriptors '''[{ "desc":
"'$EXEC_MINID'", "timestamp": 1790329126 }]'''

| [
|   {
|     "success": true,
|     "warnings": [
|       "Range not given, using default keypool range"
|     ]
|   }
| ]

You now have a watch-only wallet that you can use to monitor a range of addresses that you defined through Miniscript!

Spend Your Funds

Obviously, because this is a watch-only address, you can't spend the funds from Bitcoin Core. We've set it up this way because this is the only limited, trial functionality that Bitcoin Core has for minidescriptor importing at the time of this writing.

As noted above, you should already be using this set of addresses somewhere else, in a wallet that you can actually spend from. But, that wallet may be offline; having a watch-only version will allow you to monitor its funds without exposing the private keys to the dangers of being on the net.

Summary: Using Miniscript with Descriptors

Though you can use Bitcoin Scripts that you converted from Miniscript (or Policy) in Bitcoin Core, currently there's only limited ability to use Miniscript directly.

The one thing you can do is read in a Miniscript descriptor built with public keys to create a read-only wallet. Doing so is a simple combination of the process you've already learned to convert Policy into Miniscript with the process you've already learned to read a descriptor into Bitcoin Core.

What's Next?

Continue "Using Miniscript" with §16.4: Using BDK.