From 025b945cbfe46aa68be19a56dc818abaf5a24324 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 11 Feb 2026 08:45:51 +0200 Subject: [PATCH 1/3] Initial draft of Descriptor Annotations BIP --- bip-descriptorannotations.mediawiki | 136 ++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 bip-descriptorannotations.mediawiki diff --git a/bip-descriptorannotations.mediawiki b/bip-descriptorannotations.mediawiki new file mode 100644 index 0000000000..a9b8e35155 --- /dev/null +++ b/bip-descriptorannotations.mediawiki @@ -0,0 +1,136 @@ +
+  BIP: ?
+  Layer: Applications
+  Title: Output Script Descriptor Annotations
+  Authors: Craig Raw 
+  Status: Draft
+  Type: Specification
+  Assigned: ?
+  License: BSD-2-Clause
+  Requires: 380
+
+ +==Abstract== + +This document specifies an optional annotation syntax for output script descriptors as defined in [[bip-0380.mediawiki|BIP 380]]. +Annotations are key/value pairs appended to a descriptor expression using URL-like query string delimiters (?, &, =). +They convey operational metadata (such as a blockchain scan start height or gap limit) that aids wallet recovery without altering the scripts produced by the descriptor. + +==Copyright== + +This BIP is licensed under the BSD 2-clause license. + +==Motivation== + +[[bip-0380.mediawiki|BIP 380]] introduced output script descriptors to address the insufficiency of key-only backups: given only private keys, a restored wallet cannot determine which scripts and addresses to derive. +Descriptors solve that problem but do not carry the operational parameters that may practically be needed to recover a wallet completely. +When restoring from a backup, a wallet may need to know where to begin scanning the blockchain, how many consecutive unused addresses to tolerate before stopping (the gap limit), or how many labels to scan for in the case of [[bip-0352.mediawiki|BIP 352]] silent payment wallets. +Without this information, implementations must either scan the chain from a generic starting block or rely on out-of-band conventions, and may still miss funds if parameters like the gap limit are too low. + +Annotations provide a compact way to include such metadata directly in a descriptor string, using characters already present in the BIP 380 checksum character set. + +==Specification== + +===General Form=== + +An annotated descriptor has the form: + +
+SCRIPT?key=value&key=value#CHECKSUM
+
+ +The question mark (?) separates the script expression from the annotations. +Each annotation is a key/value pair joined by =. +Multiple annotations are separated by &. +The checksum is computed over the entire string preceding #, including the annotations. + +===Grammar=== + +An annotation expression (ANNOT) is defined as: + +
+ANNOT   := '?' PAIR ('&' PAIR)*
+PAIR    := KEY '=' VALUE
+KEY     := [a-z]+
+VALUE   := [0-9]+
+
+ +Keys consist of one or more lowercase ASCII letters. +Values are non-negative decimal integers (no leading zeros except for the value 0 itself). + +A descriptor with annotations has the form: + +
+SCRIPT ANNOT? ('#' CHECKSUM)?
+
+ +where ANNOT is optional (denoted ? in the grammar sense), and the checksum remains optional as specified in BIP 380. + +===Character Set and Checksum=== + +The delimiter characters ?, =, and & are already members of the INPUT_CHARSET defined in [[bip-0380.mediawiki|BIP 380]]: + +
+IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~
+
+ +Therefore the existing checksum algorithm requires no modification. +The checksum is computed over the full descriptor string including annotations, exactly as specified in BIP 380. + +===Defined Annotation Keys=== + +Key names are abbreviated to reduce the size of descriptors encoded in QR codes and backup materials. + +{| +! Key !! Value !! Description +|- +| bh || Block height || The block height at or after which the wallet first received funds. Implementations should begin scanning from this height. +|- +| gl || Gap limit || The number of consecutive unused addresses to derive before stopping, for [[bip-0032.mediawiki|BIP 32]] derived-key wallets. +|- +| ml || Max Label || The maximum label index to scan for, for [[bip-0352.mediawiki|BIP 352]] silent payment wallets. +|} + +===Semantics=== + +Annotation values are nominal lower bounds: they represent the minimum values required for correct recovery at the time the descriptor was exported. +Over the lifetime of a wallet these values may increase (e.g. the gap limit may grow as more addresses are used) but should not decrease. + +Implementations that encounter an unknown annotation key MUST ignore it and MUST NOT reject the descriptor. + +==Test Vectors== + +The following annotated descriptors are valid. +Checksums were computed using the BIP 380 reference code. + +* Descriptor with bh and gl: +: wpkh([deadbeef/84h/0h/0h]xpub6CY2xt3mvQejPbLuDSEP6hQ1LXQNWN9JPn3TFEbBjCvNmHFiGHoRBZLEvjpcadLFgRpkUeSHFmHFBMiKLtKUE4ovbMYEKPSEyYby9matfkE/0/*)?bh=800000&gl=30#qaz40pnw + +* The same descriptor without annotations remains valid: +: wpkh([deadbeef/84h/0h/0h]xpub6CY2xt3mvQejPbLuDSEP6hQ1LXQNWN9JPn3TFEbBjCvNmHFiGHoRBZLEvjpcadLFgRpkUeSHFmHFBMiKLtKUE4ovbMYEKPSEyYby9matfkE/0/*)#q5ppmz0l + +* Descriptor with bh only: +: wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)?bh=150000#8z7yy3wl + +* Taproot descriptor with annotations: +: tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)?bh=709632&gl=20#6la5jcn3 + +The following annotated descriptors are invalid: + +* Uppercase key: raw(deadbeef)?Bh=1#... +* Empty value: raw(deadbeef)?bh=#... +* Missing value: raw(deadbeef)?bh#... +* Negative value: raw(deadbeef)?bh=-1#... +* Non-integer value: raw(deadbeef)?bh=abc#... +* Leading zeros: raw(deadbeef)?bh=01#... +* Empty key: raw(deadbeef)?=100#... +* Trailing ampersand: raw(deadbeef)?bh=1&#... + +==Backward Compatibility== + +Annotated descriptors are a strict superset of plain descriptors. +Any valid descriptor without annotations remains valid and unchanged. + +Implementations that do not support annotations cannot parse annotated descriptors directly. +However, an annotated descriptor can be converted to a plain descriptor by stripping everything from the first ? to the # (exclusive) and recomputing the checksum using the BIP 380 algorithm. + From 91a1efb3f342b9933101d1ed2f8e739413db0c2c Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 11 Feb 2026 11:39:55 +0200 Subject: [PATCH 2/3] Improve scope definition, tighten specification and clarify semantics --- bip-0380.mediawiki | 4 ++ bip-descriptorannotations.mediawiki | 59 +++++++++++++---------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/bip-0380.mediawiki b/bip-0380.mediawiki index 3f113f2a60..8d5ccd1991 100644 --- a/bip-0380.mediawiki +++ b/bip-0380.mediawiki @@ -288,6 +288,10 @@ All available expression types are listed in this table. | Tree | TREE | [[bip-0386.mediawiki|386]] +|- +| Annotation +| ANNOT +| [[bip-descriptorannotations.mediawiki|?]] |} ==Appendix B: Index of Script Expressions== diff --git a/bip-descriptorannotations.mediawiki b/bip-descriptorannotations.mediawiki index a9b8e35155..1bca0e6cf2 100644 --- a/bip-descriptorannotations.mediawiki +++ b/bip-descriptorannotations.mediawiki @@ -14,7 +14,7 @@ This document specifies an optional annotation syntax for output script descriptors as defined in [[bip-0380.mediawiki|BIP 380]]. Annotations are key/value pairs appended to a descriptor expression using URL-like query string delimiters (?, &, =). -They convey operational metadata (such as a blockchain scan start height or gap limit) that aids wallet recovery without altering the scripts produced by the descriptor. +They convey operational metadata (such as a blockchain scan start height or gap limit) to aid recovery of funds without altering the scripts produced by the descriptor. ==Copyright== @@ -23,8 +23,8 @@ This BIP is licensed under the BSD 2-clause license. ==Motivation== [[bip-0380.mediawiki|BIP 380]] introduced output script descriptors to address the insufficiency of key-only backups: given only private keys, a restored wallet cannot determine which scripts and addresses to derive. -Descriptors solve that problem but do not carry the operational parameters that may practically be needed to recover a wallet completely. -When restoring from a backup, a wallet may need to know where to begin scanning the blockchain, how many consecutive unused addresses to tolerate before stopping (the gap limit), or how many labels to scan for in the case of [[bip-0352.mediawiki|BIP 352]] silent payment wallets. +Descriptors solve that problem but do not carry the operational parameters that may practically be required to recover the funds in a wallet. +When restoring from a backup, a wallet may need to know where to begin scanning the blockchain, how many consecutive unused addresses to tolerate before stopping (the gap limit), or the maximum label index to scan for in the case of [[bip-0352.mediawiki|BIP 352]] silent payment wallets. Without this information, implementations must either scan the chain from a generic starting block or rely on out-of-band conventions, and may still miss funds if parameters like the gap limit are too low. Annotations provide a compact way to include such metadata directly in a descriptor string, using characters already present in the BIP 380 checksum character set. @@ -44,37 +44,25 @@ Each annotation is a key/value pair joined by =. Multiple annotations are separated by &. The checksum is computed over the entire string preceding #, including the annotations. -===Grammar=== +===Annotation Expressions=== -An annotation expression (ANNOT) is defined as: +An annotation expression (ANNOT) consists of: +* A question mark ? +* Followed by one or more key/value pairs (PAIR), separated by & -
-ANNOT   := '?' PAIR ('&' PAIR)*
-PAIR    := KEY '=' VALUE
-KEY     := [a-z]+
-VALUE   := [0-9]+
-
- -Keys consist of one or more lowercase ASCII letters. -Values are non-negative decimal integers (no leading zeros except for the value 0 itself). +Each key MUST appear at most once in an annotation expression. +Only one ? delimiter is permitted per descriptor. -A descriptor with annotations has the form: - -
-SCRIPT ANNOT? ('#' CHECKSUM)?
-
+Each PAIR consists of: +* A key: one or more lowercase ASCII letters (az) +* An equals sign = +* A value: a non-negative decimal integer (no leading zeros except for the value 0 itself) -where ANNOT is optional (denoted ? in the grammar sense), and the checksum remains optional as specified in BIP 380. +A descriptor with annotations has the form SCRIPT ANNOT, optionally followed by #CHECKSUM as specified in BIP 380. ===Character Set and Checksum=== -The delimiter characters ?, =, and & are already members of the INPUT_CHARSET defined in [[bip-0380.mediawiki|BIP 380]]: - -
-IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~
-
- -Therefore the existing checksum algorithm requires no modification. +The delimiter characters ?, =, and & are already members of the INPUT_CHARSET defined in [[bip-0380.mediawiki|BIP 380]], so the existing checksum algorithm requires no modification. The checksum is computed over the full descriptor string including annotations, exactly as specified in BIP 380. ===Defined Annotation Keys=== @@ -93,8 +81,10 @@ Key names are abbreviated to reduce the size of descriptors encoded in QR codes ===Semantics=== -Annotation values are nominal lower bounds: they represent the minimum values required for correct recovery at the time the descriptor was exported. -Over the lifetime of a wallet these values may increase (e.g. the gap limit may grow as more addresses are used) but should not decrease. +Annotation values are declared lower bounds: they represent the minimum values required to recover all funds at the time the descriptor was exported. +Over the lifetime of a wallet these values may increase (e.g. the gap limit may grow as more addresses are used) but SHOULD NOT decrease. + +The order of annotation pairs is not significant for interpretation, but because the checksum is computed over the literal string, reordering pairs produces a different checksum. Implementations that encounter an unknown annotation key MUST ignore it and MUST NOT reject the descriptor. @@ -104,13 +94,13 @@ The following annotated descriptors are valid. Checksums were computed using the BIP 380 reference code. * Descriptor with bh and gl: -: wpkh([deadbeef/84h/0h/0h]xpub6CY2xt3mvQejPbLuDSEP6hQ1LXQNWN9JPn3TFEbBjCvNmHFiGHoRBZLEvjpcadLFgRpkUeSHFmHFBMiKLtKUE4ovbMYEKPSEyYby9matfkE/0/*)?bh=800000&gl=30#qaz40pnw +: wpkh([deadbeef/84h/0h/0h]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*)?bh=800000&gl=30#v35n46d2 * The same descriptor without annotations remains valid: -: wpkh([deadbeef/84h/0h/0h]xpub6CY2xt3mvQejPbLuDSEP6hQ1LXQNWN9JPn3TFEbBjCvNmHFiGHoRBZLEvjpcadLFgRpkUeSHFmHFBMiKLtKUE4ovbMYEKPSEyYby9matfkE/0/*)#q5ppmz0l +: wpkh([deadbeef/84h/0h/0h]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*)#kf3v6fpx * Descriptor with bh only: -: wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)?bh=150000#8z7yy3wl +: wpkh(0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600)?bh=150000#8kehtsrj * Taproot descriptor with annotations: : tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)?bh=709632&gl=20#6la5jcn3 @@ -125,12 +115,15 @@ The following annotated descriptors are invalid: * Leading zeros: raw(deadbeef)?bh=01#... * Empty key: raw(deadbeef)?=100#... * Trailing ampersand: raw(deadbeef)?bh=1&#... +* No annotation pairs: raw(deadbeef)?#... +* Duplicate key: raw(deadbeef)?bh=1&bh=2#... +* Multiple question marks: raw(deadbeef)?bh=1?gl=2#... ==Backward Compatibility== Annotated descriptors are a strict superset of plain descriptors. Any valid descriptor without annotations remains valid and unchanged. -Implementations that do not support annotations cannot parse annotated descriptors directly. +Existing parser implementations that do not support annotations may not be able to parse annotated descriptors directly. However, an annotated descriptor can be converted to a plain descriptor by stripping everything from the first ? to the # (exclusive) and recomputing the checksum using the BIP 380 algorithm. From 236b83c0574a941bc4961de6cc974e83e6e509e0 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 11 Feb 2026 13:06:17 +0200 Subject: [PATCH 3/3] Add link to mailing list discussion --- bip-descriptorannotations.mediawiki | 1 + 1 file changed, 1 insertion(+) diff --git a/bip-descriptorannotations.mediawiki b/bip-descriptorannotations.mediawiki index 1bca0e6cf2..aaea1b659b 100644 --- a/bip-descriptorannotations.mediawiki +++ b/bip-descriptorannotations.mediawiki @@ -7,6 +7,7 @@ Type: Specification Assigned: ? License: BSD-2-Clause + Discussion: https://groups.google.com/g/bitcoindev/c/ozjr1lF3Rkc Requires: 380