Skip to content

BIP 89: Chain Code Delegation for Private Collaborative Custody#2004

Merged
murchandamus merged 12 commits intobitcoin:masterfrom
jurvis:master
Feb 4, 2026
Merged

BIP 89: Chain Code Delegation for Private Collaborative Custody#2004
murchandamus merged 12 commits intobitcoin:masterfrom
jurvis:master

Conversation

@jurvis
Copy link
Contributor

@jurvis jurvis commented Oct 15, 2025

We propose a new BIP for Chain Code Delegation, a collaborative custody technique that involves privileged participants (delegatee) withholding BIP32 chain codes at key setup time from a delegator, and sharing only enough information for non‑privileged participants to provide their signature.

For non-blinded signing, the delegatee derives a per‑spend scalar tweak t from the (withheld) chain code, the delegator computes the child key (x+t, P+tG), and produces a standard signature over the transaction’s sighash. For blind signing, the nonce and challenge are blinded so the delegator returns a blind Schnorr signature that the counterparty unblinds; thanks to Schnorr’s linearity, the same tweak is incorporated without revealing the final message or linkable details (optionally with predicate proofs for policy).

This enables participants like collaborative custodians to co‑sign when needed, while avoiding the broad visibility that comes with holding an xpub.

More background and discussions can be found: https://delvingbitcoin.org/t/chain-code-delegation-private-access-control-for-bitcoin-keys/1837.

This is joint work with @jesseposner. Feedback appreciated!

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks complete, labelling as needing number assignment.

@jurvis
Copy link
Contributor Author

jurvis commented Nov 30, 2025

@arminsabouri @jonatack thank you for taking the time to review! I've gone ahead and addressed your comments in
9a47c29

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assigned 89.

Please update the file names and BIP draft headers, including "Created: 2025-12-03" for the date of assignment, and add an entry to the README.

@jonatack jonatack changed the title BIP: Chain Code Delegation for Private Collaborative Custody BIP 89: Chain Code Delegation for Private Collaborative Custody Dec 3, 2025
@jurvis jurvis force-pushed the master branch 3 times, most recently from af0ec04 to c5aac26 Compare December 3, 2025 22:44
@jurvis
Copy link
Contributor Author

jurvis commented Dec 3, 2025

thanks @jonatack! i think i got everything!

@jonatack
Copy link
Member

jonatack commented Dec 6, 2025

Thanks for updating.

Final verdict: 95 % LLM-generated or LLM-heavy, with ~5 % human origination of the core idea and final review.

@jurvis can you help me out, please: Is this accurate? I'm trying to adapt our process and understanding.

@jurvis
Copy link
Contributor Author

jurvis commented Dec 6, 2025

hi @jonatack, happy to help. does the scan run through the sample code as well? the sample code contains a lot of boilerplate that we leveraged LLMs to help us with, and may be the reason why it registers high.

However, the contents of the mediawiki itself should be ~90% original. We leaned on LLM use in the mediawiki mostly to ensure that our formatting was aligned with existing conventions (heavily referencing BIP 340, BIP 352, BIP 32, and BIP 3), and to convert it from Markdown, which we had originally used to write our draft.

For example, we wrote out our original one of the algorithms in the following format originally:

Signing
======
When the counterparty requests that the collaborative custodian sign a transaction, it derives the BIP32 scalar tweak from the xpub (i.e. the value `parse_256(I_L)` from BIP32) and provides it to the custodian:

# Inputs:
#   chain_code : 32-byte chain code (hidden from custodian)
#   P_par      : custodian’s parent public key (compressed)
#   i          : child index for spending

I   = HMAC-SHA512(key = chain_code,
                 data = serP(P_par) || ser32(i))
I_L = I[0:32]            # left half
t_i = parse256(I_L)      # scalar tweak mod n
# (I_R would be the child chain code—discarded here.)

# Counterparty → Custodian: send t_i

Which we got ultimately turned into this, to align with how BIP 32 reads:

=== Tweak Calculation ===
To produce CCD tweak data, a delegatee computes a per-participant scalar that aggregates the non-hardened derivation tweaks along the remaining path. Let the extended key retained by the delegatee be P at depth d, and let the target index vector be I = (i<sub>d+1</sub>, …, i<sub>n</sub>) with each i<sub>k</sub> < 2<sup>31</sup>.

<div>
Algorithm ''ComputeBIP32Tweak(P, I)'':
* Inputs:
** ''P'': base public key at depth ''d''
** ''I = (i<sub>d+1</sub>, …, i<sub>n</sub>)'': ordered sequence of non-hardened child indices
* Let ''t = 0'' and ''E = P''.
* For each index ''i'' in ''I'' (from left to right):
** Run the BIP32 non-hardened derivation ''CKDpub'' on ''E'' with child index ''i'', yielding the child extended key ''P<sub>child</sub>'' and its scalar tweak ''δ'' (the parse<sub>256</sub>(''I<sub>L</sub>'') term from BIP32).
** Let ''t = (t + δ) mod n''.
** Let ''E = P<sub>child</sub>''.
* If ''I'' is empty, let ''P′ = P''; otherwise let ''P′ = P<sub>child</sub>'' from the final iteration.
* Return ''(t, P′)''.
</div>

Hope that helps.

@jonatack
Copy link
Member

jonatack commented Dec 6, 2025

hi @jonatack, happy to help. does the scan run through the sample code as well?

Gave it the BIP draft only.

@jurvis
Copy link
Contributor Author

jurvis commented Dec 18, 2025

hi @jonatack just following up to see if there's anything I can do here to move things along. thanks!

@real-or-random
Copy link
Contributor

The inclusion of major parts of secp256k1lab in this form violates the (only) requirement in the MIT license under which secp256k1lab is distributed, see its COPYING file.

@real-or-random
Copy link
Contributor

A related editorial question is whether it's a good idea to include distributions of secp256k1lab in the BIPs repo. This question is relevant not only for this BIP draft but also for #2070 and for the ChillDKG draft BIP (no PR yet).

We had raised that question in the context of ChillDKG over a year ago on the mailing list, specifically hoping to hear the opinion of the BIPs editors, but noone has commented so far: https://groups.google.com/g/bitcoindev/c/HE3HSnGTpoQ/m/Y2VhaMCrCAAJ (This post refers to the library as "secp256k1proto". This was our working title before we switched to "secp256k1lab" when publishing the code in a separate repo.)

Copy link
Member

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave the proposal draft a first read. I find myself somewhat confused as to the terms “Delegator and Delegatee”. The scenario I envisioned was one Delegatee and one or multiple Delegators, but at times it seems that maybe multiple Delegatees are in play. If that’s the case, it could perhaps be clarified what scenarios are covered and how multiple Delegatees would collaborate.
I skipped some of the paragraphs of the Blinded Signing section.

@murchandamus
Copy link
Member

We had raised that question in the context of ChillDKG over a year ago on the mailing list, specifically hoping to hear the opinion of the BIPs editors, but noone has commented so far: https://groups.google.com/g/bitcoindev/c/HE3HSnGTpoQ/m/Y2VhaMCrCAAJ (This post refers to the library as "secp256k1proto". This was our working title before we switched to "secp256k1lab" when publishing the code in a separate repo.)

@real-or-random: TBH, I don’t think I fully understand the trade-offs of the approaches you proposed. I would prefer that any software with on-going development were maintained outside of the BIPs repository, but maybe we could chat some time so that I better understand the situation.

@real-or-random
Copy link
Contributor

We had raised that question in the context of ChillDKG over a year ago on the mailing list, specifically hoping to hear the opinion of the BIPs editors, but noone has commented so far: groups.google.com/g/bitcoindev/c/HE3HSnGTpoQ/m/Y2VhaMCrCAAJ (This post refers to the library as "secp256k1proto". This was our working title before we switched to "secp256k1lab" when publishing the code in a separate repo.)

@real-or-random: TBH, I don’t think I fully understand the trade-offs of the approaches you proposed. I would prefer that any software with on-going development were maintained outside of the BIPs repository, but maybe we could chat some time so that I better understand the situation.

Let me try to simplify the discussion: If the BIP editors are okay with integrating a snapshot of secp256klab as a subtree in each bip-xxxx folder of a BIP whose code uses secp256k1lab (as currently proposed in this PR and in #2070), then I'm happy with that. I think it's the best solution.

"Snapshot" means that it is a static thing, and maintenance of secp256k1lab will, of course, happen in its repo. I guess the only reason to ever touch a snapshot subtree in the BIPs repo is to fix a secp256k1 bug that specifically affects the BIP code that uses the library (and even in that case, other ways to resolve the overall issue could be considered).

@murchandamus
Copy link
Member

We had raised that question in the context of ChillDKG over a year ago on the mailing list, specifically hoping to hear the opinion of the BIPs editors, but noone has commented so far: groups.google.com/g/bitcoindev/c/HE3HSnGTpoQ/m/Y2VhaMCrCAAJ (This post refers to the library as "secp256k1proto". This was our working title before we switched to "secp256k1lab" when publishing the code in a separate repo.)

@real-or-random: TBH, I don’t think I fully understand the trade-offs of the approaches you proposed. I would prefer that any software with on-going development were maintained outside of the BIPs repository, but maybe we could chat some time so that I better understand the situation.

Let me try to simplify the discussion: If the BIP editors are okay with integrating a snapshot of secp256klab as a subtree in each bip-xxxx folder of a BIP whose code uses secp256k1lab (as currently proposed in this PR and in #2070), then I'm happy with that. I think it's the best solution.

"Snapshot" means that it is a static thing, and maintenance of secp256k1lab will, of course, happen in its repo. I guess the only reason to ever touch a snapshot subtree in the BIPs repo is to fix a secp256k1 bug that specifically affects the BIP code that uses the library (and even in that case, other ways to resolve the overall issue could be considered).

That seems reasonable to me, as it ensures that a functional reference implementation is retained in the context of the BIPs. The only concern that comes to mind would be that we might get more pull requests by LLM-commit farmers on those, but I guess we’d just have to filter those.

@jesseposner
Copy link
Contributor

@murchandamus Thanks for the detailed review, I will revise accordingly. I agree that delegatee/delgator is confusing, but I'm not sure what the better terminology is.

@real-or-random I'll make sure to include the copyright and permission notice which I believe will satisfy the license requirements for secp256k1lab.

@real-or-random
Copy link
Contributor

@real-or-random I'll make sure to include the copyright and permission notice which I believe will satisfy the license requirements for secp256k1lab.

Yep, thanks, and no offense, by the way. :) I just think it's good practice to keep licenses with the code. It helps everyone later on. I suggest just including the existing COPYING file. And I think it would also be very useful to future readers of the BIP to note somewhere from which commit the files are taken, (e.g., have a sentence in the BIP, in some README file, or added to the top of the COPYING file) such as The contents of this directory are taken from https://github.com/secp256k1lab/secp256k1lab commit id 01234567890abcdef....

@murchandamus murchandamus added the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Jan 8, 2026
@murchandamus
Copy link
Member

Hopefully the last comment on the secp-library discussion in your PR: Let’s continue that conversation in #1855

- Upgrade vendored secp256k1lab to commit a265da1 (adds type annotations)
- Add COPYING file to satisfy MIT license requirements
- Document secp256k1lab commit reference in BIP text
- Fix TweakContext to use Scalar types for gacc/tacc
- Replace HashFunction enum with Callable type alias
- Fix bytearray to bytes conversion in blind_sign
- Move imports to top of file
- Fix boolean comparison style (use 'not' instead of '== False')
- Add proper type annotations and casts for dict handling
- Remove unused imports and type ignore comments
- Add intro explaining delegation naming (chain code is delegated, not
  signing authority)
- Reorder terminology to list Delegator before Delegatee
- Replace "quorum" with clearer "can co-sign for UTXOs" language
- Clarify derivation constraints in terms of delegatee's extended key
- Rename "Delegatee Signing" section to "Signing Modes"
- Fix "delegatee can apply" to "delegator can produce" (line 112)
- Replace undefined "caller" with "delegatee" (line 173)
- Clarify "Change outputs" to "Tweaks for change outputs" (line 98)
- Add note that message is separate from CCD bundle
- Add note on application-specific verification (addresses, amounts)
- Add transition sentence clarifying non-concurrent protocol scope
- Change Type from 'Standards Track' to 'Specification' (valid type)
- Change 'Created' to 'Assigned' (correct field name per BIP format)
- Change 'Post-History' to 'Discussion' (recognized field in buildtable.pl)
@Christewart
Copy link
Contributor

Hi 👋

I'm coming over from reviewing the FROST PR. @siv2r pointed out to me that this proposal (along with MuSig2) share the semantics for tweaking protocol data structures and algorithms ("TweakContext", "ApplyTweak", "TweakCtxInit") .

Do the authors have a strong opinion on the idea of working on a separate BIP that specifies those data structures, and then just reference those algorithms in this BIP - similar to how the FROST proposal delegates definitions to BIP340 to define schnorr signatures in it's writing.

This work could be done yourselves - as the authors on this work have been pretty prolific in this space :-) - or delegated to other volunteers (such as myself, or maybe Sivaram if he has bandwidth) to execute on the idea.

In summary 👍 or 👎

@murchandamus
Copy link
Member

@jurvis, @jesseposner: Just so we don’t cross wires, my understanding is that y’all are still working on addressing review, and you’ll let us know here when you’re caught up. Also, please feel free to resolve any review that you feel has been addressed.

Co-authored-by: Mark "Murch" Erhardt <[email protected]>
@jesseposner
Copy link
Contributor

@murchandamus: All review items have now been addressed. I got an error when I tried to resolve the comments, but I posted the commit hash to show it has been resolved.

@jesseposner
Copy link
Contributor

@Christewart My bandwidth is limited, but I think it's a good idea and happy to support any efforts to make this happen.

Copy link
Member

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just looked over the changes. They seem good to me. I marked the open review comments as resolved that seemed to have been addressed by the new commits.

I’ll assume that this is ready for publication, unless I hear otherwise. :)

@siv2r
Copy link
Contributor

siv2r commented Feb 4, 2026

@Christewart, I've been thinking about this TweakContext BIP idea. The scope would be small: just the data structure (Q, gacc, tacc) and four functions (TweakCtxInit, ApplyTweak, GetXonlyPubkey, GetPlainPubkey). Unlike other small BIP 43 (coordination point), this would be more of a convenience abstraction. I'm honestly not sure if this meets the bar for being "BIPable" and would appreciate any input on that. I have very little experience with what is and isn't BIPable anyway.

For downstream BIPs, the spec text could reference the TweakContext BIP instead of duplicating the algorithms. However, there's a challenge with reference code: if this BIP vendors secp256k1lab (needed for group operations), and downstream BIPs also vendor secp256k1lab for their own crypto, they'd end up with two copies. So downstream BIPs would likely just copy-paste the functions anyway, meaning this would only reduce BIP text duplication, not code duplication.

A better approach might be treating this as an informational BIP (for people who aren't aware of this tweaking methodology) with reference code for demonstration, but without expecting downstream BIPs to import it. Additionally, we could add a GE.apply_tweak(tweak: Scalar, is_xonly: bool) -> GE function to the secp256k1lab library. Then downstream BIPs would use that shared tweaking function, and implement their own TweakContext following the spec. This would provide both specification standardization (via the BIP) and implementation standardization (via secp256k1lab).

I see three options: (A) create the informational BIP + add apply_tweak to secp256k1lab, (B) just add apply_tweak to secp256k1lab without a BIP, or (C) do nothing and accept the duplication. I think at minimum B would be useful; I'm less certain about A. Curious what others think, especially on whether this abstraction is valuable enough for its own BIP.

delegated to other volunteers (such as myself, or maybe Sivaram if he has bandwidth) to execute on the idea.

If A seems like the way to go, I can raise a pull request to secp256k1lab for the apply_tweak method. I'm not sure about writing the BIP myself, but I'm happy to help/collaborate in any way if you're interested :)

P.S. Sorry for spamming this PR thread. Wasn't sure where else to post this comment.

@murchandamus murchandamus merged commit 57869d5 into bitcoin:master Feb 4, 2026
4 checks passed
@murchandamus murchandamus removed the PR Author action required Needs updates, has unaddressed review comments, or is otherwise waiting for PR author label Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants