Skip to content

Add register command for BIP388 policies - #791

Closed
Sjors wants to merge 4 commits into
bitcoin-core:masterfrom
Sjors:2025/07/policy
Closed

Add register command for BIP388 policies#791
Sjors wants to merge 4 commits into
bitcoin-core:masterfrom
Sjors:2025/07/policy

Conversation

@Sjors

@Sjors Sjors commented Jul 18, 2025

Copy link
Copy Markdown
Member

First step towards #785. See also #647.

This adds a generic register command for devices that support BIP388 wallet policy registration.

The command takes:

  • A policy name.
  • A combined multipath output descriptor using expressions such as /<0;1>/*.

HWI converts the descriptor into a BIP388 descriptor template and key vector, validates the resulting policy, and fails if the descriptor cannot be represented as a valid BIP388 policy.

Example:

hwi --device-type ledger register \
  --name "2-of-2 multisig" \
  --desc "wsh(sortedmulti(2,[00000001/48'/0'/0'/2']xpub.../<0;1>/*,[00000002/48'/0'/0'/2']xpub.../<0;1>/*))"

Ledger returns an opaque registration value containing the device registration and policy name as separately length-prefixed fields. This lets signtx in #792 recover the policy name without a separate --policy-name argument:

{"registration": "00..."}

BitBox02 stores the policy on the device and therefore returns no registration value:

{"registration": null}

The policy conversion tests cover all BIP388 test vectors. Simulator device tests cover registration on both Ledger and BitBox02.

@Sjors

Sjors commented Sep 4, 2025

Copy link
Copy Markdown
Member Author

Rebased after #795 landed.

@Sjors

Sjors commented Jul 4, 2026

Copy link
Copy Markdown
Member Author

I'll give this a rebase after #836.

I need to sync this up with the latest #794 (e2a7288, c3815ba), and consider cherry-picking 562fe54.

@Sjors
Sjors marked this pull request as draft July 4, 2026 09:38
@Sjors
Sjors marked this pull request as ready for review July 31, 2026 08:02
@achow101

Copy link
Copy Markdown
Member

It seems like BitBox02 also has policy registration, so I would prefer if this could also be implemented for that as well. We should not be adding commands for one device only.

I strongly prefer for this to be generic and standardized. As of now, there is no standard for policy registration. Policies can be registered on BitBox02 but that seems to return nothing back to the user as they have enough storage to remember that registration. Conversely, Ledger returns the hmac. Since clearly not every device that can take policies will return the same thing, I think it would be better to say that the result value is some opaque binary result that has no meaning to the user. Rather, the interpretation of that blob occurs on a per-device basis, which users, and HWI for the most part, don't need to care about what that value is. We could also invent some encoding for it that can wrap the ledger's hmac, or signal that there is no value for the bitbox.

I also don't really like strictly following BIP 388 here in taking all of the fields directly from BIP 388 as separate positional arguments. Fundamentally, the policy is just a bastardized descriptor, and it should be straightforward to go from a descriptor to a policy. We have to massage the data into the device specific protocol stuff anyways. So instead of taking a policy, this could take a full descriptor, extract the keys and put in the substitutes, and verify that the rest of the descriptor meets the BIP 388 requirements. Then it can package up all of that to send to the device. This would reduce the number of parameters required and remove the requirement of the caller having to remember the correct key order.

Lastly, in addition to returning the policy registration result, I think the result should also include the descriptor/policy as part of the opaque string. Then the whole policy and proof can be supplied to signtx as a single argument rather than also requiring the user to know specifically which pieces to pass in. Both the policy and proof can be encoded into one big string that is visually meaningless to the user (e.g. base64 encode it) that can be passed in at a later time.

@Sjors

Sjors commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

We have to massage the data into the device specific protocol stuff anyways. So instead of taking a policy, this could take a full descriptor, extract the keys and put in the substitutes, and verify that the rest of the descriptor meets the BIP 388 requirements.

Having HWI convert from descriptors to BIP388 will make things easier on the Bitcoin Core side too, so that's nice.

In #785 (comment) I suggested that we simply store a max 255 byte blob from the device. A null blob means we don't store anything. That should be easy to make device independent.

I think the result should also include the descriptor/policy as part of the opaque string.

Oh but then it needs to be bigger. And the wallet still needs to know what blob to pass for which descriptor, so not sure if this helps at all.

cc @bigspider

@Sjors

Sjors commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

It seems like BitBox02 also has policy registration, so I would prefer if this could also be implemented for that as well.

Last time I checked BitBox didn't support MuSig2, but that's good reason to add BIP388 support independent of MuSig2.

@Sjors

Sjors commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

Pushed the new approach, it's fully vibe coded though, will polish it later if needed.

The first commit adds (multi-path) descriptor -> BIP388 conversion, with BIP test vectors.

These will be shared with #792 so I'll keep that draft.

The next commits add ledger and bitbox02, with only the former returning an hmac.

@bigspider

Copy link
Copy Markdown
Contributor

In #785 (comment) I suggested that we simply store a max 255 byte blob from the device. A null blob means we don't store anything. That should be easy to make device independent.

This is in line with the approach I drafted in my old PoC, and seems likely to remain compatible with all devices.

@Sjors

Sjors commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Fixed the linter, updated PR description.

@Sjors

Sjors commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

I think the result should also include the descriptor/policy as part of the opaque string.

Oh but then it needs to be bigger. And the wallet still needs to know what blob to pass for which descriptor, so not sure if this helps at all.

I changed register to echo the policy name for Ledger. That way #792 doesn't need a --policy-name argument. The signtx command still takes a descriptor argument.

We could also shove the descriptor into the registration blob, but then we're stuck with it forever. I'd rather see this be added to PSBT spec, which seems useful in general. In the mean time a --descriptor argument is easy enough to support, and something we already do for other methods.

We could add a (proprietary) field for authentication, but the Bitcoin Core wallet needs to be aware of this mechanism one way or the other, and having a --registration argument seems simple enough.


I briefly investigated how this works with ColdCard. IIUC it's able to figure out which policy is being used by looking at the PSBT input derivations. But when connected via USB, you can help it by passing the policy name. So for registration we should return 00 <name-length> <ASCII name> (empty hmac + policy name). We could even support offline registration by generating the necessary json file and putting it in a tmp folder.

Sjors and others added 4 commits August 2, 2026 12:59
Convert combined multipath output descriptors into BIP388 descriptor templates and key vectors, and validate that the result follows BIP388. Cover all canonical valid and invalid BIP388 policy vectors.

Co-authored-by: Codex (GPT-5.6 Sol) <noreply@openai.com>
Frame the device registration and policy name as separately length-prefixed fields before hex encoding the opaque value. Decode and validate all fields while allowing later fields to be appended.

Co-authored-by: Codex (GPT-5.6 Sol) <noreply@openai.com>
Accept a policy name and combined multipath output descriptor, convert it to the BIP388 template and key vector, and register it with the device.

Ledger already supports BIP388 wallet policy registration internally. Expose that existing support through the generic command and return its registration value as opaque data.

Co-authored-by: Codex (GPT-5.6 Sol) <noreply@openai.com>
Co-authored-by: Codex (GPT-5.6 Sol) <noreply@openai.com>
@achow101

achow101 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Come on, we already have an entire descriptor parser with classes. Why does this include a wholly separate descriptor parser, that uses regex insanity?

This is too sloppy, I've opened #842 to do this the right way.

@achow101 achow101 closed this Aug 4, 2026
@Sjors

Sjors commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

I said:

it's fully vibe coded though, will polish it later if needed.

Of course much better if you do it :-) I'll review #842.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants