{"number":89,"title":"Chain Code Delegation","authors":["Jesse Posner","Jurvis Tan"],"status":"Deployed","type":"Specification","layer":"Applications","created":"2025-12-03","license":"BSD-3-Clause","version":"1.0.0","requires":[32,340,341],"replaces":[],"replacedBy":[],"discussion":["https://delvingbitcoin.org/t/chain-code-delegation-private-access-control-for-bitcoin-keys/1837"],"comments":"https://github.com/bitcoin/bips/wiki/Comments:BIP-0089","abstract":"Chain Code Delegation (CCD) is a method for multi-signature wallets in which a privileged participant withholds BIP32 chain codes from one or more non-privileged participants, and supplies per-input scalar tweaks at signing time. This allows non-privileged participants to co-sign transactions without learning wallet-wide derivations, balances, or signing activity from other spending combinations. CCD defines the tweak exchange needed for verification and signing behavior when the signer does not possess a chain code.","eli5":"Describes a technique for multisignature wallets in which one main participant keeps certain key-derivation information to themselves and gives co-signers only what they need for each signature. Co-signers, such as a backup service, can still approve transactions without learning the wallet's full balance or history, improving privacy.","categories":["wallets","signatures","transactions"],"filename":"bip-0089.mediawiki","githubUrl":"https://github.com/bitcoin/bips/blob/master/bip-0089.mediawiki","format":"mediawiki","readingMinutes":20,"inbound":0,"revisions":3,"impact":6,"core":false,"references":[],"lastModified":"2026-09-15","html":"<h2 id=\"abstract\">Abstract</h2>\n<p>Chain Code Delegation (CCD) is a method for multi-signature wallets in which a privileged participant withholds BIP32 chain codes from one or more non-privileged participants, and supplies per-input scalar tweaks at signing time. This allows non-privileged participants to co-sign transactions without learning wallet-wide derivations, balances, or signing activity from other spending combinations. CCD defines the tweak exchange needed for verification and signing behavior when the signer does not possess a chain code.</p>\n<h2 id=\"motivation\">Motivation</h2>\n<p>In multisig deployments, sharing extended public keys (xpubs) or descriptors enables all participants to scan the chain and infer counterparties' activity. CCD limits that visibility by ensuring non-privileged participants only ever hold a non-extended keypair and only receive the minimum per-spend data needed to sign. The procedure keeps policy enforcement feasible for the non-privileged signer while preserving balance privacy, which is particularly useful in collaborative custody arrangements where the wallet owner wants balance privacy from their custodian.</p>\n<h2 id=\"terminology\">Terminology</h2>\n<p>In CCD, the chain code is the object of delegation—not signing authority. A participant who gives up their chain code delegates it to another.</p>\n<ul><li>\nA \"Delegator\" is a participant who delegates their chain code to another party. They hold only a non-extended keypair and receive scalar tweaks from the delegatee when asked to sign.\n</li></ul>\n<ul><li>\nA \"Delegatee\" is a participant who receives and retains a delegated chain code for another participant's public key, and computes derivation tweaks for that participant.\n</li></ul>\n<ul><li>\nA \"Participant\" is any key holder that can co-sign for UTXOs in the wallet (including delegators and delegatees).\n</li></ul>\n<ul><li>\nA \"Non-hardened derivation\" is a BIP32 child derivation where index &lt; 2^31.\n</li></ul>\n<h2 id=\"overview\">Overview</h2>\n<p>CCD operates by having Delegatees deprive Delegators of BIP32 chain codes during setup and later conveying the aggregated scalar tweak computed as the sum of non-hardened derivation tweaks along the remaining path to the child key used by a given input or change output. A Delegator uses the tweak to compute the child keys for verification and signing without being able to derive or recognize keys for other paths.</p>\n<h2 id=\"specification\">Specification</h2>\n<h3 id=\"key-material-and-setup\">Key material and setup</h3>\n<ul><li>\n<strong>Delegator key:</strong> Each delegator generates a standard (non-extended) secp256k1 keypair and provides the public key to the counterparties. A delegator MUST NOT retain or be provided a chain code for this key.\n</li></ul>\n<ul><li>\n<strong>Delegated chain code:</strong> A designated delegatee computes and retains a BIP32 chain code bound to the delegator's public key, forming an xpub that MUST NOT be disclosed to the delegator. The delegatee MAY share this xpub with other delegatees.\n</li></ul>\n<ul><li>\n<strong>Other participants:</strong> Non-delegator participants use conventional extended keys and share the public half as appropriate for the wallet descriptor.\n</li></ul>\n<ul><li>\n<strong>Derivation constraints:</strong> The delegatee holds an extended public key for the delegator. All derivation from this extended key MUST be non-hardened, as hardened derivation requires the private key, which the delegatee does not possess.\n</li></ul>\n<h3 id=\"notation\">Notation</h3>\n<p>The following conventions are used, with constants as defined for <a href=\"https://www.secg.org/sec2-v2.pdf\">secp256k1</a>. We note that adapting this proposal to other elliptic curves is not straightforward and can result in an insecure scheme.</p>\n<ul><li>\nLowercase variables represent integers or byte arrays.\n<ul><li>\nThe constant <em>p</em> refers to the field size, <em>0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F</em>.\n</li></ul>\n<ul><li>\nThe constant <em>n</em> refers to the curve order, <em>0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141</em>.\n</li></ul>\n</li><li>\nUppercase variables refer to points on the curve with equation <em>y<sup>2</sup> = x<sup>3</sup> + 7</em> over the integers modulo <em>p</em>.\n<ul><li>\n<em>is_infinite(P)</em> returns whether <em>P</em> is the point at infinity.\n</li></ul>\n<ul><li>\n<em>x(P)</em> and <em>y(P)</em> are integers in the range <em>0..p-1</em> and refer to the X and Y coordinates of a point <em>P</em> (assuming it is not infinity).\n</li></ul>\n<ul><li>\nThe constant <em>G</em> refers to the base point, for which <em>x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798</em> and <em>y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8</em>.\n</li></ul>\n<ul><li>\nAddition of points refers to the usual <a href=\"https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law\">elliptic curve group operation</a>.\n</li></ul>\n<ul><li>\n<a href=\"https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication\">Multiplication (⋅) of an integer and a point</a> refers to the repeated application of the group operation.\n</li></ul>\n</li><li>\nFunctions and operations:\n<ul><li>\n<em>||</em> refers to byte array concatenation.\n</li></ul>\n<ul><li>\nThe function <em>x[i:j]</em>, where <em>x</em> is a byte array and <em>i, j &ge; 0</em>, returns a <em>(j - i)</em>-byte array with a copy of the <em>i</em>-th byte (inclusive) to the <em>j</em>-th byte (exclusive) of <em>x</em>.\n</li></ul>\n<ul><li>\nThe function <em>bytes(n, x)</em>, where <em>x</em> is an integer, returns the n-byte encoding of <em>x</em>, most significant byte first.\n</li></ul>\n<ul><li>\nThe constant <em>empty_bytestring</em> refers to the empty byte array. It holds that <em>len(empty_bytestring) = 0</em>.\n</li></ul>\n<ul><li>\nThe function <em>xbytes(P)</em>, where <em>P</em> is a point for which <em>not is_infinite(P)</em>, returns <em>bytes(32, x(P))</em>.\n</li></ul>\n<ul><li>\nThe function <em>len(x)</em> where <em>x</em> is a byte array returns the length of the array.\n</li></ul>\n<ul><li>\nThe function <em>has_even_y(P)</em>, where <em>P</em> is a point for which <em>not is_infinite(P)</em>, returns <em>y(P) mod 2 == 0</em>.\n</li></ul>\n<ul><li>\nThe function <em>with_even_y(P)</em>, where <em>P</em> is a point, returns <em>P</em> if <em>is_infinite(P)</em> or <em>has_even_y(P)</em>. Otherwise,  <em>with_even_y(P)</em> returns <em>-P</em>.\n</li></ul>\n<ul><li>\nThe function <em>cbytes(P)</em>, where <em>P</em> is a point for which <em>not is_infinite(P)</em>, returns <em>a || xbytes(P)</em> where <em>a</em> is a byte that is <em>2</em> if <em>has_even_y(P)</em> and <em>3</em> otherwise.\n</li></ul>\n<ul><li>\nThe function <em>int(x)</em>, where <em>x</em> is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is <em>x</em>.\n</li></ul>\n<ul><li>\nThe function <em>lift_x(x)</em>, where <em>x</em> is an integer in range <em>0..2<sup>256</sup>-1</em>, returns the point <em>P</em> for which <em>x(P) = x</em><ref>\n</li></ul>\n</li></ul>\n<p>Given a candidate X coordinate <em>x</em> in the range <em>0..p-1</em>, there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then <em>x</em> is not a valid X coordinate either, i.e., no point <em>P</em> exists for which <em>x(P) = x</em>. The valid Y coordinates for a given candidate <em>x</em> are the square roots of <em>c = x<sup>3</sup> + 7 mod p</em> and they can be computed as <em>y = &plusmn;c<sup>(p+1)/4</sup> mod p</em> (see <a href=\"https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus\">Quadratic residue</a>) if they exist, which can be checked by squaring and comparing with <em>c</em>.</ref> and <em>has_even_y(P)</em>, or fails if <em>x</em> is greater than <em>p-1</em> or no such point exists. The function <em>lift_x(x)</em> is equivalent to the following pseudocode:</p>\n<ul><li>\n<ul><li>\n<ul><li>\nFail if <em>x &gt; p-1</em>.\n</li></ul>\n<ul><li>\nLet <em>c = x<sup>3</sup> + 7 mod p</em>.\n</li></ul>\n<ul><li>\nLet <em>y' = c<sup>(p+1)/4</sup> mod p</em>.\n</li></ul>\n<ul><li>\nFail if <em>c &ne; y'<sup>2</sup> mod p</em>.\n</li></ul>\n<ul><li>\nLet <em>y = y' </em> if <em>y' mod 2 = 0</em>, otherwise let <em>y = p - y' </em>.\n</li></ul>\n<ul><li>\nReturn the unique point <em>P</em> such that <em>x(P) = x</em> and <em>y(P) = y</em>.\n</li></ul>\n</li><li>\nThe function <em>cpoint(x)</em>, where <em>x</em> is a 33-byte array (compressed serialization), sets <em>P = lift_x(int(x[1:33]))</em> and fails if that fails. If <em>x[0] = 2</em> it returns <em>P</em> and if <em>x[0] = 3</em> it returns <em>-P</em>. Otherwise, it fails.\n</li></ul>\n<ul><li>\nThe function <em>hash256<sub>tag</sub>(x)</em> where <em>tag</em> is a UTF-8 encoded tag name and <em>x</em> is a byte array returns the 32-byte hash <em>SHA256(SHA256(tag) || SHA256(tag) || x)</em>.\n</li></ul>\n<ul><li>\nThe function <em>hash512<sub>tag</sub>(x)</em> where <em>tag</em> is a UTF-8 encoded tag name and <em>x</em> is a byte array returns the 64-byte hash <em>SHA512(SHA512(tag) || SHA512(tag) || x)</em>.\n</li></ul>\n</li><li>\nOther:\n<ul><li>\nTuples are written by listing the elements within parentheses and separated by commas. For example, <em>(2, 3, 1)</em> is a tuple.\n</li></ul>\n</li></ul>\n<h3 id=\"tweak-calculation\">Tweak Calculation</h3>\n<p>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>.</p>\n<div> Algorithm <em>ComputeBIP32Tweak(P, I)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>P</em>: base public key at depth <em>d</em>\n</li></ul>\n<ul><li>\n<em>I = (i<sub>d+1</sub>, …, i<sub>n</sub>)</em>: ordered sequence of non-hardened child indices\n</li></ul>\n</li><li>\nLet <em>t = 0</em> and <em>E = P</em>.\n</li></ul>\n<ul><li>\nFor each index <em>i</em> in <em>I</em> (from left to right):\n<ul><li>\nRun the BIP32 non-hardened derivation <em>CKDpub</em> on <em>E</em> with child index <em>i</em>, yielding the child extended key <em>P<sub>child</sub></em> and its scalar tweak <em>δ</em> (the parse<sub>256</sub>(<em>I<sub>L</sub></em>) term from BIP32).\n</li></ul>\n<ul><li>\nLet <em>t = (t + δ) mod n</em>.\n</li></ul>\n<ul><li>\nLet <em>E = P<sub>child</sub></em>.\n</li></ul>\n</li><li>\nIf <em>I</em> is empty, let <em>P′ = P</em>; otherwise let <em>P′ = P<sub>child</sub></em> from the final iteration.\n</li></ul>\n<ul><li>\nReturn <em>(t, P′)</em>.\n</li></ul>\n<p></div></p>\n<p>Any attempt to apply a hardened derivation (index ≥ 2<sup>31</sup>) MUST fail. Delegatees MAY discard P′ after extracting t if it is not otherwise required.</p>\n<h3 id=\"delegation-bundle\">Delegation Bundle</h3>\n<p>CCD requires the delegatee to provide per-participant tweaks for inputs and (optionally) change outputs. Tweaks for change outputs are only required if a delegator wants to be able to compute the amount of bitcoin they are spending.</p>\n<p>A delegatee MUST provide each delegator with, for every signing context, a collection of tuples (P<sub>i</sub>, t<sub>i</sub>) where P<sub>i</sub> is the participant's base public key disclosed to the delegator and t<sub>i</sub> is the aggregated tweak returned by <em>ComputeBIP32Tweak</em>. The scalar t<sub>i</sub> MUST be encoded as a 32-byte big-endian integer.</p>\n<p>The transport that carries this bundle is out of scope for this proposal; implementers MAY use PSBT proprietary keys, RPC payloads, or bespoke messages as long as the delegator can authenticate the origin of the data. Delegatees SHOULD attach the witness script (or sufficient script template information) built with the tweaked keys when the delegator is expected to verify the input or enforce spending policy on change outputs.</p>\n<p>Delegators use the supplied CCD tweak bundle during verification (see <em>Delegator input and change verification</em>) and signature generation (see <em>DelegatorSign</em>). The message to be signed is provided separately as part of the standard signing protocol and is not part of the CCD-specific bundle.</p>\n<h3 id=\"signing-modes\">Signing Modes</h3>\n<p>This BIP supports two modes:</p>\n<ul><li>\n<strong>Non‑blinded.</strong> The delegator receives the tweak for the child public key and the message. The delegator learns only about the specific child keys and transactions it signs for; it does not learn the wider address space.\n</li></ul>\n<ul><li>\n<strong>Blinded.</strong> The delegator receives only a blinded challenge and parity bits. The delegator learns nothing about the message or child key for which it produces a signature.\n</li></ul>\n<p>Both modes produce valid BIP340 signatures.</p>\n<h4 id=\"non-blinded-signing\">Non-Blinded Signing</h4>\n<p>For non-blinded signing, the delegator can produce signatures as usual using the tweaked key.</p>\n<h5>Delegator input and change verification (Optional)</h5>\n<p>A delegator MAY validate the data it receives before producing signatures.</p>\n<p>For example, input verification reassures the delegator that every tweaked key they are asked to sign for corresponds to a wallet input they recognise. Change verification lets them establish the net outflow and enforce spending policy.</p>\n<p>Both checks rely on the same delegated tweak bundle described above.</p>\n<h5>Input verification</h5>\n<p>For each input, the delegatee SHOULD disclose the descriptor template, the untweaked participant keys, the input witness script, and the per-participant tweaks. The delegator then applies the following procedure.</p>\n<div> Algorithm <em>InputVerification(D, W, T)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>D</em>: wallet policy or descriptor template expressed in terms of the untweaked participant keys <em>P<sub>i</sub></em>\n</li></ul>\n<ul><li>\n<em>W</em>: witness script disclosed for the input under review\n</li></ul>\n<ul><li>\n<em>T</em>: mapping from each <em>P<sub>i</sub></em> to a 32-byte big-endian tweak scalar <em>t<sub>i</sub></em>\n</li></ul>\n</li><li>\nFor each participant key <em>P<sub>i</sub></em> referenced in <em>D</em>:\n<ul><li>\nRetrieve <em>t<sub>i</sub></em> from <em>T</em>; fail if the entry is missing or malformed.\n</li></ul>\n<ul><li>\nIf the verifier controls the corresponding private key <em>d<sub>i</sub></em>, let <em>d′<sub>i</sub> = (d<sub>i</sub> + t<sub>i</sub>) mod n</em> and <em>P′<sub>i</sub> = d′<sub>i</sub> · G</em>; otherwise let <em>P′<sub>i</sub> = P<sub>i</sub> + t<sub>i</sub> · G</em>.\n</li></ul>\n</li><li>\nLet <em>D′</em> be the descriptor formed by substituting every occurrence of <em>P<sub>i</sub></em> in <em>D</em> with <em>P′<sub>i</sub></em>.\n</li></ul>\n<ul><li>\nDerive the witness script <em>W′</em> from <em>D′</em>.\n</li></ul>\n<ul><li>\nReturn <code>true</code> if <em>W′ = W</em>, otherwise <code>false</code>.\n</li></ul>\n<p></div></p>\n<p>Successful verification of an input confirms that the delegator is signing for a script that belongs to the wallet and that the aggregate tweak values align with the expected policy.</p>\n<h5>Change-output verification</h5>\n<p>When change outputs are disclosed, the delegator can perform an analogous check to ensure the destination script matches their policy template and to calculate outflows. Let D be the descriptor expressed in untweaked keys, W the provided witness script, and T the tweak mapping:</p>\n<div> Algorithm <em>ChangeOutputVerification(D, W, T)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>D</em>: wallet policy or descriptor template expressed in terms of the untweaked participant keys <em>P<sub>i</sub></em>\n</li></ul>\n<ul><li>\n<em>W</em>: witness script disclosed for the change output\n</li></ul>\n<ul><li>\n<em>T</em>: mapping from each <em>P<sub>i</sub></em> to a 32-byte big-endian tweak scalar <em>t<sub>i</sub></em>\n</li></ul>\n</li><li>\nFor each participant key <em>P<sub>i</sub></em> referenced in <em>T</em>:\n<ul><li>\nRetrieve <em>t<sub>i</sub></em> from <em>T</em>; fail if the entry is missing or malformed.\n</li></ul>\n<ul><li>\nIf the verifier controls the corresponding private key <em>d<sub>i</sub></em>, let <em>d′<sub>i</sub> = (d<sub>i</sub> + t<sub>i</sub>) mod n</em> and <em>P′<sub>i</sub> = d′<sub>i</sub> · G</em>; otherwise let <em>P′<sub>i</sub> = P<sub>i</sub> + t<sub>i</sub> · G</em>.\n</li></ul>\n</li><li>\nLet <em>D′</em> be the descriptor formed by substituting every occurrence of <em>P<sub>i</sub></em> in <em>D</em> with <em>P′<sub>i</sub></em>.\n</li></ul>\n<ul><li>\nDerive the witness script <em>W′</em> from <em>D′</em>.\n</li></ul>\n<ul><li>\nReturn <code>true</code> if <em>W′ = W</em>, otherwise <code>false</code>.\n</li></ul>\n<p></div></p>\n<p>Successful verification ensures the change output commits to the tweaked participant keys implied by the CCD tweaks, preserving the intended policy.</p>\n<p>The delegator may perform additional application-specific verification on the transaction (e.g., recipient addresses, amounts, compliance checks) using the message <em>m</em>. In the concurrently secure blinded mode, such policies can be enforced via zero-knowledge proofs that encode predicates about <em>m</em>. Specification of such policies is outside the scope of this BIP.</p>\n<h5>Delegator Signing</h5>\n<p>A delegator that holds only its base secret key <code>x</code> and public key <code>P</code> uses the delegated tweak bundle to derive per-input signing keys. The delegator MAY first call <em>InputVerification</em> and <em>ChangeOutputVerification</em> on any input and change output that provides a tweak in order to confirm outflow or policy requirements before signing.</p>\n<div> Algorithm <em>DelegatorSign(t, x, m)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>t</em>: aggregated tweak for the signing context (scalar mod <em>n</em>)\n</li></ul>\n<ul><li>\n<em>x</em>: delegator base secret key\n</li></ul>\n<ul><li>\n<em>m</em>: message to be signed (for example, a transaction digest under the desired SIGHASH policy)\n</li></ul>\n</li><li>\nLet <em>x′ = (x + t) mod n</em>.\n</li></ul>\n<ul><li>\nUse secret key <em>x′</em> to produce the required signature <em>σ</em> under the indicated policy.\n</li></ul>\n<ul><li>\nReturn <em>σ</em>.\n</li></ul>\n<p></div></p>\n<p>The delegatee is responsible for inserting <em>σ</em> into the surrounding protocol (e.g., a PSBT, transaction witness, or adaptor signature exchange).</p>\n<h4 id=\"blinded-signing\">Blinded Signing</h4>\n<p>The delegator learns neither the message, the challenge, or the public key used in the BIP340 signature, only a blinded challenge e'.</p>\n<p>This blind‑signing protocol specifies how a delegator can produce a blind partial Schnorr signature that a delegatee can unblind into a standard <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki\">BIP340</a> signature under a possibly tweaked X‑only public key. The notation, algorithmic patterns, and test‑vector style are adapted from [BIP‑327 (MuSig2)] and from the <a href=\"https://github.com/siv2r/bip-frost-signing\">FROST Signing BIP</a>. The design follows the “plain” blind Schnorr flow described in Concurrently Secure Blind Schnorr Signatures (<a href=\"https://eprint.iacr.org/2022/1676\">ePrint 2022/1676</a>), but without the concurrency hardening from that work.</p>\n<p>The output signature is a BIP340 Schnorr signature valid under an X‑only key obtained by applying a sequence of plain (e.g. BIP32) and X‑only (e.g. Tapscript) tweaks to the signer’s plain public key. Consequently the protocol is compatible with <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\">BIP341</a>. The delegator learns neither the message, the challenge, or the public key used in the BIP340 signature, only a blinded challenge e'.</p>\n<p>The plain protocol here is <strong>not</strong> concurrently secure. A signer <strong>MUST NOT</strong> run multiple blind signing sessions in parallel or interleave state across sessions. A signer <strong>MUST</strong> refuse any new blind‑nonce requests while a previous blind‑signature request is outstanding, or <strong>MUST</strong> irrevocably discard (and never reuse) any in‑flight blind nonce commitments that have not resulted in a signature, before accepting new ones.</p>\n<p>To obtain concurrency security as in (<a href=\"https://eprint.iacr.org/2022/1676\">ePrint 2022/1676</a>), the delegatee first sends an encryption of (m, a, b) before the signer commits to the blind nonce; later, the delegatee includes a zero‑knowledge proof binding the produced challenge to that encrypted tuple. That proof can additionally encode policy predicates about m (spend limits, velocity controls, etc.). A complete specification of this variant is outside the scope of this BIP.</p>\n<p>The following sections fully specify the non-concurrent blind signing protocol.</p>\n<h5>Overview</h5>\n<ul><li>\n<strong>Round 1 (blind nonce).</strong> The delegator runs <em>BlindNonceGen</em> to produce <em>blindsecnonce</em> and <em>blindpubnonce</em> and sends <em>blindpubnonce</em> to the delegatee.\n</li></ul>\n<ul><li>\n<strong>Round 2 (challenge).</strong> The delegatee runs <em>BlindChallengeGen</em> using the message <em>m</em>, <em>blindpubnonce</em>, the base public key <em>pk</em>, and a list of ordinary and X-only tweaks, to produce a <em>session context</em> (kept locally for unblinding), a <em>blindchallenge</em>, and two booleans <em>pk_parity</em> and <em>nonce_parity</em>. The delegatee sends <em>blindchallenge</em>, <em>pk_parity</em>, and <em>nonce_parity</em> to the signer.\n</li></ul>\n<ul><li>\n<strong>Round 3 (blind signature).</strong> The delegator runs <em>BlindSign</em> with <em>sk</em>, <em>blindchallenge</em>, <em>blindsecnonce</em>, <em>pk_parity</em>, and <em>nonce_parity</em> and returns <em>blindsignature</em>. The delegatee completes by calling <em>UnblindSignature</em> with the stored session context and <em>blindsignature</em> to obtain the final BIP340 signature <em>sig</em>.\n</li></ul>\n<p><em>BlindSign</em> <strong>MUST NOT</strong> be executed twice with the same <em>blindsecnonce</em>. As a defense, implementations <strong>SHOULD</strong> overwrite the first 64 bytes of <em>blindsecnonce</em> with zeros after they have been read by <em>BlindSign</em>.</p>\n<h5>Key Tweaking</h5>\n<h6>Tweak Context</h6>\n<p>The Tweak Context is a data structure consisting of the following elements:</p>\n<ul><li>\nThe point <em>Q</em> representing the potentially tweaked public key: an elliptic curve point\n</li></ul>\n<ul><li>\nThe accumulated tweak <em>tacc</em>: an integer with <em>0 &le; tacc < n</em>\n</li></ul>\n<ul><li>\nThe value <em>gacc</em> : 1 or -1 mod n\n</li></ul>\n<p>We write \"Let <em>(Q, gacc, tacc) = tweak_ctx</em>\" to assign names to the elements of a Tweak Context.</p>\n<div> Algorithm <em>TweakCtxInit(pk)</em>:\n<ul><li>\nInput:\n<ul><li>\nThe base public key pk: a 33-byte array\n</li></ul>\n</li><li>\nLet <em>Q = cpoint(pk)</em>\n</li></ul>\n<ul><li>\nFail if <em>is_infinite(Q)</em>\n</li></ul>\n<ul><li>\nLet <em>gacc = 1</em>\n</li></ul>\n<ul><li>\nLet <em>tacc = 0</em>\n</li></ul>\n<ul><li>\nReturn <em>tweak_ctx = (Q, gacc, tacc)</em>\n</li></ul>\n<p></div></p>\n<div> Algorithm <em>ApplyTweak(tweak_ctx, tweak, is_xonly_t)</em>:\n<ul><li>\nInputs:\n<ul><li>\nThe <em>tweak_ctx</em>: a <a href=\"#tweak-context\">Tweak Context</a> data structure\n</li></ul>\n<ul><li>\nThe <em>tweak</em>: a 32-byte array\n</li></ul>\n<ul><li>\nThe tweak mode <em>is_xonly_t</em>: a boolean\n</li></ul>\n</li><li>\nLet <em>(Q, gacc, tacc) = tweak_ctx</em>\n</li></ul>\n<ul><li>\nIf <em>is_xonly_t</em> and <em>not has_even_y(Q)</em>:\n<ul><li>\nLet <em>g = -1 mod n</em>\n</li></ul>\n</li><li>\nElse:\n<ul><li>\nLet <em>g = 1</em>\n</li></ul>\n</li><li>\nLet <em>t = int(tweak)</em>; fail if <em>t &ge; n</em>\n</li></ul>\n<ul><li>\nLet <em>Q' = g⋅Q + t⋅G</em>\n<ul><li>\nFail if <em>is_infinite(Q')</em>\n</li></ul>\n</li><li>\nLet <em>gacc' = g⋅gacc mod n</em>\n</li></ul>\n<ul><li>\nLet <em>tacc' = t + g⋅tacc mod n</em>\n</li></ul>\n<ul><li>\nReturn <em>tweak_ctx' = (Q', gacc', tacc')</em>\n</li></ul>\n<p></div></p>\n<h5>Blind Nonce Generation</h5>\n<div> Algorithm <em>BlindNonceGen(sk, pk, aggpk, m, extra_in)</em>:\n<ul><li>\nInputs:\n<ul><li>\nThe base secret signing key <em>sk</em>: a 32-byte array (optional argument)\n</li></ul>\n<ul><li>\nThe base public key <em>pk</em>: a 33-byte array (optional argument)\n</li></ul>\n<ul><li>\nThe auxiliary input <em>extra_in</em>: a byte array with <em>0 &le; len(extra_in) &le; 2<sup>32</sup>-1</em> (optional argument)\n</li></ul>\n</li><li>\nLet <em>rand' </em> be a 32-byte array freshly drawn uniformly at random\n</li></ul>\n<ul><li>\nIf the optional argument <em>sk</em> is present:\n<ul><li>\nLet <em>rand</em> be the byte-wise xor of <em>sk</em> and <em>hash256<sub>CCD/aux</sub>(rand')</em><sup class=\"footnote-ref\"><a href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>\n</li></ul>\n</li><li>\nElse:\n<ul><li>\nLet <em>rand = rand' </em>\n</li></ul>\n</li><li>\nIf the optional argument <em>extra_in</em> is not present:\n<ul><li>\nLet <em>extra_in = empty_bytestring</em>\n</li></ul>\n</li><li>\nLet <em>k' = int(hash256<sub>CCD/blindnonce</sub>(rand || bytes(1, len(pk)) || pk || bytes(4, len(extra_in)) || extra_in )) mod n</em>\n</li></ul>\n<ul><li>\nFail if <em>k' = 0</em>\n</li></ul>\n<ul><li>\nLet <em>R' = k'⋅G</em>\n</li></ul>\n<ul><li>\nLet <em>blindpubnonce = cbytes(R')</em>\n</li></ul>\n<ul><li>\nLet <em>blindsecnonce = bytes(32, k' || pk)</em><ref name=\"blindsecnonce\">The algorithms as specified here assume that the <em>blindsecnonce</em> is stored as a 65-byte array using the serialization <em>blindsecnonce = bytes(32, k') || pk</em>. The same format is used in the reference implementation and in the test vectors. However, since the <em>blindsecnonce</em> is not meant to be sent over the wire, compatibility between implementations is not a concern, and this method of storing the <em>blindsecnonce</em> is merely a suggestion.<br />\n</li></ul>\n<p>The <em>blindsecnonce</em> is effectively a local data structure of the signer which comprises the value double <em>(k', pk)</em>, and implementations may choose any suitable method to carry it from <em>BlindNonceGen</em> (first communication round) to <em>BlindSign</em> (third communication round). In particular, implementations may choose to hide the <em>blindsecnonce</em> in internal state without exposing it in an API explicitly, e.g., in an effort to prevent callers from reusing a <em>blindsecnonce</em> accidentally.</ref></p>\n<ul><li>\nReturn <em>(secnonce, pubnonce)</em>\n</li></ul>\n<p></div></p>\n<h5>Session Context</h5>\n<p>The Session Context is a data structure consisting of the following elements:</p>\n<ul><li>\nThe base public key <em>pk</em>: a 33-byte array\n</li></ul>\n<ul><li>\nThe blind factor <em>blindfactor</em>: a 32-byte array\n</li></ul>\n<ul><li>\nThe challenge hash <em>challenge</em>: a 32-byte array\n</li></ul>\n<ul><li>\nThe public nonce <em>pubnonce</em>: a 33-byte array\n</li></ul>\n<ul><li>\nThe number <em>v</em> of tweaks with <em>0 &le; v < 2^32</em>\n</li></ul>\n<ul><li>\nThe tweaks <em>tweak<sub>1..v</sub></em>: <em>v</em> 32-byte arrays\n</li></ul>\n<ul><li>\nThe tweak modes <em>is_xonly_t<sub>1..v</sub></em> : <em>v</em> booleans\n</li></ul>\n<p>We write \"Let <em>(pk, blindfactor, challenge, pubnonce, v, tweak<sub>1..v</sub>, is_xonly_t<sub>1..v</sub>) = session_ctx</em>\" to assign names to the elements of a Session Context.</p>\n<div> Algorithm <em>GetSessionValues(session_ctx)</em>:\n<ul><li>\nLet <em>(pk, blindfactor, challenge, pubnonce, v, tweak<sub>1..v</sub>, is_xonly_t<sub>1..v</sub>) = session_ctx</em>\n</li></ul>\n<ul><li>\nLet <em>tweak_ctx<sub>0</sub> = TweakCtxInit(pk)</em>; fail if that fails\n</li></ul>\n<ul><li>\nFor <em>i = 1 .. v</em>:\n<ul><li>\nLet <em>tweak_ctx<sub>i</sub> = ApplyTweak(tweak_ctx<sub>i-1</sub>, tweak<sub>i</sub>, is_xonly_t<sub>i</sub>)</em>; fail if that fails\n</li></ul>\n</li><li>\nLet <em>(Q, gacc, tacc) = tweak_ctx<sub>v</sub></em>\n</li></ul>\n<ul><li>\nLet <em>a = int(blindfactor)</em>; fail if <em>a ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>b = int(blindfactor)</em>; fail if <em>b ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>e = int(challenge)</em>; fail if <em>e ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>R = cpoint(pubnonce)</em>; fail if that fails\n</li></ul>\n<ul><li>\nReturn <em>(Q, gacc, tacc, a, e, R)</em>\n</li></ul>\n<p></div></p>\n<h5>Blind Challenge Generation</h5>\n<div> Algorithm <em>BlindChallengeGen(m, blindpubnonce, pk, tweak<sub>1..v</sub>, is_xonly<sub>1..v</sub>, extra_in)</em>:\n<ul><li>\nInputs:\n<ul><li>\nThe message <em>m</em>: a byte array\n</li></ul>\n<ul><li>\nThe blind public nonce <em>blindpubnonce</em>: a 33-byte array\n</li></ul>\n<ul><li>\nThe base public key <em>pk</em>: a 33-byte array\n</li></ul>\n<ul><li>\nThe tweaks <em>tweak<sub>1..v</sub></em>: <em>v</em> 32-byte arrays\n</li></ul>\n<ul><li>\nThe tweak modes <em>is_xonly<sub>1..v</sub></em>: <em>v</em> booleans \n</li></ul>\n<ul><li>\nThe auxiliary input <em>extra_in</em>: a byte array with <em>0 &le; len(extra_in) &le; 2<sup>32</sup>-1</em> (optional argument)\n</li></ul>\n</li><li>\nIf <em>extra_in</em> is not present:\n<ul><li>\nLet <em>extra_in = empty_bytestring</em>\n</li></ul>\n</li><li>\nLet <em>(Q, gacc, tacc) = TweakCtxInit(pk)</em>\n</li></ul>\n<ul><li>\nFor <em>i = 1 .. v</em>:\n<ul><li>\nLet <em>(Q, gacc, tacc) = ApplyTweak((Q, gacc, tacc), tweak<sub>i</sub>, is_xonly<sub>i</sub>)</em>; fail if that fails \n</li></ul>\n</li><li>\nLet <em>cpk = cbytes(Q)</em>\n</li></ul>\n<ul><li>\nDraw 32 random bytes <em>rand</em>\n</li></ul>\n<ul><li>\nLet <em>z = hash512<sub>CCD/blindfactor</sub>(rand || bytes(1, len(cpk)) || cpk || bytes(1, len(blindpubnonce)) || blindpubnonce || bytes(8, len(m)) || m || bytes(4, len(extra_in)) || extra_in)</em>\n</li></ul>\n<ul><li>\nLet <em>a' = int(z[0:32]) mod n</em>; fail if <em>a' = 0</em>\n</li></ul>\n<ul><li>\nLet <em>b' = int(z[32:64]) mod n</em>; fail if <em>b' = 0</em>\n</li></ul>\n<ul><li>\nLet <em>g = 1</em> if <em>has_even_y(Q)</em>, else <em>g = −1 mod n</em>\n</li></ul>\n<ul><li>\nLet <em>pk_parity = (g⋅gacc mod n == 1)</em>\n</li></ul>\n<ul><li>\nLet <em>X' = cpoint(pk)</em>; let <em>X = X' </em> if <em>pk_parity</em> else <em>−X' </em>\n</li></ul>\n<ul><li>\nLet <em>R' = cpoint(blindpubnonce)</em>\n</li></ul>\n<ul><li>\nLet <em>R = R' + a'⋅G + b'⋅X</em>; fail if <em>is_infinite(R)</em>\n</li></ul>\n<ul><li>\nLet <em>nonce_parity = has_even_y(R)</em>\n</li></ul>\n<ul><li>\nIf <em>nonce_parity</em>:\n<ul><li>\nLet <em>a = a' </em>, <em>b = b' </em>\n</li></ul>\n</li><li>\nElse:\n<ul><li>\nLet <em>a = n − a' </em>, <em>b = n − b' </em>\n</li></ul>\n</li><li>\nLet <em>e = int(hash<sub>BIP0340/challenge</sub>(xbytes(R) || xbytes(Q) || m)) mod n</em>\n</li></ul>\n<ul><li>\nLet <em>e' = (e + b) mod n</em>\n</li></ul>\n<ul><li>\nLet <em>session_ctx = (pk, bytes(32, a), bytes(32, e), cbytes(R), tweak<sub>1..v</sub>, is_xonly<sub>1..v</sub>)</em>\n</li></ul>\n<ul><li>\nReturn <em>(session_ctx, bytes(32, e'), pk_parity, nonce_parity)</em>\n</li></ul>\n<p></div></p>\n<h5>Blind Signing</h5>\n<div> Algorithm <em>BlindSign(sk, blindchallenge, blindsecnonce, pk_parity, nonce_parity)</em>:\n<ul><li>\nInputs:\n<ul><li>\nThe secret key <em>sk</em>: a 32-byte array\n</li></ul>\n<ul><li>\nThe blind challenge <em>blindchallenge</em>: a 32-byte array <em>e' </em>\n</li></ul>\n<ul><li>\nThe secret nonce <em>blindsecnonce</em>: a byte array whose first 32 bytes are <em>k</em> (remaining bytes are implementation-defined)\n</li></ul>\n<ul><li>\n<em>pk_parity</em>: boolean (from <em>BlindChallengeGen</em>)\n</li></ul>\n<ul><li>\n<em>nonce_parity</em>: boolean (from <em>BlindChallengeGen</em>)\n</li></ul>\n</li><li>\nLet <em>d' = int(sk)</em>; fail if <em>d' = 0</em> or <em>d' ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>P = d'⋅G</em>; fail if <em>is_infinite(P)</em>\n</li></ul>\n<ul><li>\nLet <em>d = d' </em> if <em>pk_parity</em> else <em>n − d' </em>\n</li></ul>\n<ul><li>\nLet <em>e' = int(blindchallenge)</em>; fail if <em>e' ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>k' = int(blindsecnonce[0:32])</em>; fail if <em>k' = 0</em> or <em>k' ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>k = k' </em> if <em>nonce_parity</em> else <em>n − k' </em>\n</li></ul>\n<ul><li>\nOverwrite <em>blindsecnonce[0:64]</em> with 64 zero bytes<sup class=\"footnote-ref\"><a href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>\n</li></ul>\n<ul><li>\nLet <em>R' = k'⋅G</em>; fail if <em>is_infinite(R')</em><sup class=\"footnote-ref\"><a href=\"#fn-3\" id=\"fnref-3\">[3]</a></sup>\n</li></ul>\n<ul><li>\nLet <em>s' = (k + e'⋅d) mod n</em>\n</li></ul>\n<ul><li>\nIf <em>VerifyBlindSignature(cbytes(P), cbytes(R'), blindchallenge, bytes(32, s'), pk_parity, nonce_parity)</em> returns failure, abort\n</li></ul>\n<ul><li>\nReturn <em>blindsignature = bytes(32, s')</em>\n</li></ul>\n<p></div></p>\n<div> Algorithm <em>VerifyBlindSignature(pk, blindpubnonce, blindchallenge, blindsignature, pk_parity, nonce_parity)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>pk</em>: a 33-byte compressed public key\n</li></ul>\n<ul><li>\n<em>blindpubnonce</em>: the signer’s 33-byte <em>R' = k'⋅G</em>\n</li></ul>\n<ul><li>\n<em>blindchallenge</em>: 32-byte <em>e' </em>\n</li></ul>\n<ul><li>\n<em>blindsignature</em>: 32-byte <em>s' </em>\n</li></ul>\n<ul><li>\n<em>pk_parity, nonce_parity</em>: booleans\n</li></ul>\n</li><li>\nLet <em>P' ' = cpoint(pk)</em>; let <em>P = P' </em> if <em>pk_parity</em> else <em>−P' </em> ; fail if <em>is_infinite(P)</em>\n</li></ul>\n<ul><li>\nLet <em>R' ' = cpoint(blindpubnonce)</em>; let <em>R = R' </em> if <em>nonce_parity</em> else <em>−R' </em>\n</li></ul>\n<ul><li>\nLet <em>e' = int(blindchallenge)</em>, <em>s' = int(blindsignature)</em>\n</li></ul>\n<ul><li>\nReturn success iff <em>s'⋅G == R + e'⋅P</em>\n</li></ul>\n<p></div></p>\n<h5>Unblinding</h5>\n<div> Algorithm <em>UnblindSignature(session_ctx, blindsignature)</em>:\n<ul><li>\nInputs:\n<ul><li>\n<em>session_ctx</em>: as defined above\n</li></ul>\n<ul><li>\n<em>blindsignature</em>: the 32-byte <em>s' </em> returned by the signer\n</li></ul>\n</li><li>\nLet <em>(Q, gacc, tacc, a, e, R) = GetSessionValues(session_ctx)</em>; fail if that fails\n</li></ul>\n<ul><li>\nLet <em>g = 1</em> if <em>has_even_y(Q)</em>, else <em>g = −1 mod n</em>\n</li></ul>\n<ul><li>\nLet <em>s' = int(blindsignature)</em>; fail if <em>s' ≥ n</em>\n</li></ul>\n<ul><li>\nLet <em>s = (s' + a + e⋅g⋅tacc) mod n</em>\n</li></ul>\n<ul><li>\nReturn the BIP340 signature <em>sig = xbytes(R) || bytes(32, s)</em>\n</li></ul>\n<p></div></p>\n<h2 id=\"security-considerations\">Security Considerations</h2>\n<ul><li>\nExposure of any delegated tweak scalar <code>t</code> enables signing only for the specific child key(s) that scalar was derived for, and is typically short-lived if disclosed immediately before spending.\n</li></ul>\n<ul><li>\nDelegatees MUST ensure every delegated path remains non-hardened and that <em>ComputeBIP32Tweak</em> yields the correct tweak <code>t</code>; incorrect scalars could render the delegator incapable of producing a signature.\n</li></ul>\n<ul><li>\nDelegators MUST verify change outputs when tweak data is provided (for example via <em>ChangeOutputVerification</em>) to avoid authorizing unexpected scripts.\n</li></ul>\n<ul><li>\nReusing the same k' (first 32 bytes in blindsecnonce) across two BlindSign calls allows recovery of the base secret key.\n</li></ul>\n<ul><li>\nWhen using blinded signing, opening multiple sessions concurrently against the same signer can allow an attacker to learn the base secret key. If concurrency is required, use the concurrently secure variant (encryption + ZK) instead (not specified in this BIP).\n</li></ul>\n<h2 id=\"test-vectors\">Test Vectors</h2>\n<p>A <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0089/vectors\">collection of JSON test vectors</a> are provided, along with a <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0089/reference.py\">python reference implementation</a>. It uses a vendored copy of the <a href=\"https://github.com/secp256k1lab/secp256k1lab/\">secp256k1lab</a> library (commit <a href=\"https://github.com/secp256k1lab/secp256k1lab/commit/a265da139aea27386085a2a8760f8698e1bda64e\">a265da139aea27386085a2a8760f8698e1bda64e</a>).</p>\n<p>You may also find example code of CCD in action <a href=\"https://github.com/jurvis/chaincode-delegation\">here</a>.</p>\n<h2 id=\"changelog\">Changelog</h2>\n<ul><li>\n<strong>1.0.0</strong> (2026-08-13): Advance to Deployed.\n</li></ul>\n<ul><li>\n<strong>0.1.3</strong> (2026-02-02): Upgrade secp256k1lab and add license file; fix type checker and linter issues; clarify Delegator/Delegatee terminology, derivation constraints, signing modes, and verification scope.\n</li></ul>\n<ul><li>\n<strong>0.1.2</strong> (2025-12-03): Updated to reflect BIP number assignment.\n</li></ul>\n<ul><li>\n<strong>0.1.1</strong> (2025-11-30): Fix acknowledgments spelling, BIP3 formatting, and use \"Chain Code\" with a space throughout.\n</li></ul>\n<ul><li>\n<strong>0.1.0</strong> (2025-10-14): Publication of draft BIP\n</li></ul>\n<h2 id=\"footnotes\">Footnotes</h2>\n<ol class=\"footnotes\"><li id=\"fn-1\">The random data is hashed (with a unique tag) as a precaution against situations where the randomness may be correlated with the secret signing key itself. It is xored with the secret key (rather than combined with it in a hash) to reduce the number of operations exposed to the actual secret key. <a href=\"#fnref-1\" class=\"footnote-back\" aria-label=\"Back to text\">↩</a></li><li id=\"fn-2\"> This helps prevent accidental nonce reuse. A zeroed <em>blindsecnonce</em> MUST cause subsequent <em>BlindSign</em> calls to fail. <a href=\"#fnref-2\" class=\"footnote-back\" aria-label=\"Back to text\">↩</a></li><li id=\"fn-3\"> This check holds except with negligible probability. <a href=\"#fnref-3\" class=\"footnote-back\" aria-label=\"Back to text\">↩</a></li></ol>\n<h2 id=\"acknowledgements\">Acknowledgements</h2>\n<ul><li>\nArik Sosman and Wilmer Paulino for the initial discussions and validation of this idea.  \n</li></ul>\n<ul><li>\nSanket Kajalkar, Jordan Mecom, Gregory Sanders, ZmnSCPxj, Yuval Kogman, and John Cantrell for code and design review.\n</li></ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>This BIP is licensed under the BSD 3-Clause license.</p>","toc":[{"id":"abstract","title":"Abstract","level":2},{"id":"motivation","title":"Motivation","level":2},{"id":"terminology","title":"Terminology","level":2},{"id":"overview","title":"Overview","level":2},{"id":"specification","title":"Specification","level":2},{"id":"key-material-and-setup","title":"Key material and setup","level":3},{"id":"notation","title":"Notation","level":3},{"id":"tweak-calculation","title":"Tweak Calculation","level":3},{"id":"delegation-bundle","title":"Delegation Bundle","level":3},{"id":"signing-modes","title":"Signing Modes","level":3},{"id":"non-blinded-signing","title":"Non-Blinded Signing","level":4},{"id":"blinded-signing","title":"Blinded Signing","level":4},{"id":"security-considerations","title":"Security Considerations","level":2},{"id":"test-vectors","title":"Test Vectors","level":2},{"id":"changelog","title":"Changelog","level":2},{"id":"footnotes","title":"Footnotes","level":2},{"id":"acknowledgements","title":"Acknowledgements","level":2},{"id":"copyright","title":"Copyright","level":2}],"history":{"totalCommits":3,"firstCommit":"2026-02-05T04:58:08+08:00","lastUpdated":"2026-09-15T02:56:36+04:00","events":[{"sha":"3a13d60ff202b94b9e02e7c58d6a7e87a94f3899","date":"2026-09-15T02:56:36+04:00","author":"Kanan","message":"bips 89/114/441: add missing Footnotes and references tag (#2289)","url":"https://github.com/bitcoin/bips/commit/3a13d60ff202b94b9e02e7c58d6a7e87a94f3899","kind":"edit"},{"sha":"b7bf6dda11d0d537bfaf951c36a2cc1d431f7e41","date":"2026-08-12T21:09:25-07:00","author":"Jurvis Tan","message":"docs: advance bip89 to deployed","url":"https://github.com/bitcoin/bips/commit/b7bf6dda11d0d537bfaf951c36a2cc1d431f7e41","kind":"status"},{"sha":"57869d524abb9143ba96aa9f53fab906ec309383","date":"2026-02-05T04:58:08+08:00","author":"Jurvis Tan","message":"BIP 89: Chain Code Delegation for Private Collaborative Custody (#2004)","url":"https://github.com/bitcoin/bips/commit/57869d524abb9143ba96aa9f53fab906ec309383","kind":"edit"}]}}