@kukks/bitcoin-descriptors 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # @kukks/bitcoin-descriptors
2
+
3
+ > **Fork of [`@bitcoinerlab/descriptors`](https://github.com/bitcoinerlab/descriptors)** by [Jose-Luis Landabaso](https://github.com/landabaso).
4
+
5
+ This library parses and creates Bitcoin Miniscript Descriptors and generates Partially Signed Bitcoin Transactions (PSBTs). It provides PSBT finalizers and signers for single-signature and BIP32 wallets.
6
+
7
+ ## Differences from upstream
8
+
9
+ This fork migrates the entire library from `bitcoinjs-lib` to the [`@scure/btc-signer`](https://github.com/nicolo-ribaudo/scure-btc-signer) and [`@noble`](https://github.com/nicolo-ribaudo/noble-curves) ecosystem. Key differences:
10
+
11
+ - **`Buffer` replaced with `Uint8Array`** across the entire public API. All methods that previously returned or accepted `Buffer` now use `Uint8Array`. This is a **breaking change**.
12
+ - **Dependencies replaced**: `bitcoinjs-lib`, `ecpair`, `bip32`, `tiny-secp256k1` are no longer used. The library now depends on `@scure/btc-signer`, `@scure/bip32`, `@noble/curves`, `@noble/hashes`, and `@scure/base`.
13
+ - **PSBT class**: Uses `Transaction` from `@scure/btc-signer` instead of `Psbt` from `bitcoinjs-lib`.
14
+ - **Ledger support removed**: The `ledger` module and all Ledger-related functions (`signLedger`, `keyExpressionLedger`, `pkhLedger`, `shWpkhLedger`, `wpkhLedger`, etc.) have been removed.
15
+ - **`lodash.memoize` removed**: Replaced with an inline memoize helper.
16
+ - **Package renamed** from `@bitcoinerlab/descriptors` to `@kukks/bitcoin-descriptors`.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @kukks/bitcoin-descriptors
22
+ npm install @bitcoinerlab/miniscript
23
+ ```
24
+
25
+ ## Features
26
+
27
+ - Parses and creates [Bitcoin Descriptors](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) (including those based on the [Miniscript language](https://bitcoinerlab.com/modules/miniscript)).
28
+ - Generates Partially Signed Bitcoin Transactions (PSBTs).
29
+ - Provides PSBT finalizers and signers for single-signature and BIP32 wallets.
30
+
31
+ ## Concepts
32
+
33
+ This library has two main capabilities related to Bitcoin descriptors. Firstly, it can generate `addresses` and `scriptPubKeys` from descriptors. These `addresses` and `scriptPubKeys` can be used to receive funds from other parties. Secondly, the library is able to sign transactions and spend unspent outputs described by those same descriptors. In order to do this, the descriptors must first be set into a PSBT.
34
+
35
+ <details>
36
+ <summary>Concepts</summary>
37
+
38
+ ### Descriptors
39
+
40
+ In Bitcoin, a transaction consists of a set of inputs that are spent into a different set of outputs. Each input spends an output in a previous transaction. A Bitcoin descriptor is a string of text that describes the rules and conditions required to spend an output in a transaction.
41
+
42
+ For example, `wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)` is a descriptor that describes a pay-to-witness-public-key-hash (P2WPKH) type of output with the specified public key. If you know the corresponding private key for the transaction for which this descriptor is an output, you can spend it.
43
+
44
+ Descriptors can express much more complex conditions, such as multi-party cooperation, time-locked outputs, and more. These conditions can be expressed using the Bitcoin Miniscript language, which is a way of writing Bitcoin Scripts in a structured and more easily understandable way.
45
+
46
+ ### Partially Signed Bitcoin Transactions (PSBTs)
47
+
48
+ A PSBT (Partially Signed Bitcoin Transaction) is a format for sharing Bitcoin transactions between different parties.
49
+
50
+ PSBTs come in handy when working with descriptors, especially when using scripts, because they allow multiple parties to collaborate in the signing process.
51
+
52
+ </details>
53
+
54
+ ## Usage
55
+
56
+ The library can be split into three main parts:
57
+
58
+ - The `Output` class is the central component for managing descriptors. It facilitates the creation of outputs to receive funds and enables the signing and finalization of PSBTs for spending UTXOs.
59
+ - PSBT signers and finalizers, which are used to manage the signing and finalization of PSBTs.
60
+ - `keyExpressions` and `scriptExpressions`, which provide functions to create key and standard descriptor expressions (strings) from structured data.
61
+
62
+ ### Output class
63
+
64
+ The `Output` class is dynamically created by providing `ECPair` and `BIP32` factory APIs:
65
+
66
+ ```javascript
67
+ import * as descriptors from '@kukks/bitcoin-descriptors';
68
+ const { Output } = descriptors.DescriptorsFactory({ ECPair, BIP32 });
69
+ ```
70
+
71
+ Here, `ECPair` and `BIP32` are implementations of the `ECPairAPI` and `BIP32API` interfaces. These interfaces now use `Uint8Array` instead of `Buffer` for all binary data (public keys, private keys, signatures, etc.).
72
+
73
+ Once set up, you can obtain an instance for an output:
74
+
75
+ ```javascript
76
+ const wpkhOutput = new Output({
77
+ descriptor:
78
+ 'wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)'
79
+ });
80
+ ```
81
+
82
+ For miniscript-based descriptors, the `signersPubKeys` parameter in the constructor becomes particularly important. It specifies the spending path of a previous output with multiple spending paths.
83
+
84
+ The `Output` class offers various helpful methods, including `getAddress()`, `getScriptPubKey()` (returns `Uint8Array`), `expand()`, `updatePsbtAsInput()` and `updatePsbtAsOutput()`.
85
+
86
+ The library supports a wide range of descriptor types, including:
87
+ - Pay-to-Public-Key-Hash (P2PKH): `pkh(KEY)`
88
+ - Pay-to-Witness-Public-Key-Hash (P2WPKH): `wpkh(KEY)`
89
+ - Pay-to-Script-Hash (P2SH): `sh(SCRIPT)`
90
+ - Pay-to-Witness-Script-Hash (P2WSH): `wsh(SCRIPT)`
91
+ - Pay-to-Taproot (P2TR) with single key: `tr(KEY)`
92
+ - Address-based descriptors: `addr(ADDRESS)`, including Taproot addresses
93
+
94
+ #### Working with PSBTs
95
+
96
+ This library uses `Transaction` from `@scure/btc-signer` as the PSBT class:
97
+
98
+ ```javascript
99
+ import { Transaction } from '@scure/btc-signer';
100
+ const psbt = new Transaction({ allowUnknownOutputs: true, disableScriptCheck: true });
101
+ const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout });
102
+ ```
103
+
104
+ Here, `psbt` refers to an instance of the [`@scure/btc-signer` Transaction class](https://github.com/nicolo-ribaudo/scure-btc-signer). The parameter `txHex` denotes a hex string that serializes the previous transaction containing this output. Meanwhile, `vout` is an integer that marks the position of the output within that transaction.
105
+
106
+ The method returns the `inputFinalizer()` function. This finalizer function completes a PSBT input by adding the unlocking script (`scriptWitness` or `scriptSig`) that satisfies the previous output's spending conditions. Complete all necessary signing operations before calling `inputFinalizer()`.
107
+
108
+ To add an output:
109
+
110
+ ```javascript
111
+ const recipientOutput =
112
+ new Output({ descriptor: `addr(bc1qgw6xanldsz959z45y4dszehx4xkuzf7nfhya8x)` });
113
+ recipientOutput.updatePsbtAsOutput({ psbt, value: 10000 });
114
+ ```
115
+
116
+ #### Parsing Descriptors with `expand()`
117
+
118
+ The `expand()` function parses Bitcoin descriptors into their component parts:
119
+
120
+ ```javascript
121
+ const output = new Output({ descriptor: "your-descriptor-here" });
122
+ const result = output.expand();
123
+ ```
124
+
125
+ Or through the factory:
126
+
127
+ ```javascript
128
+ const { expand } = descriptors.DescriptorsFactory({ ECPair, BIP32 });
129
+ const result = expand({
130
+ descriptor: "sh(wsh(andor(pk(0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2),older(8640),pk([d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*))))"
131
+ });
132
+ ```
133
+
134
+ ### Signers and Finalizers
135
+
136
+ This library includes two signers: ECPair (single-signature) and BIP32.
137
+
138
+ ```javascript
139
+ import { signers } from '@kukks/bitcoin-descriptors';
140
+
141
+ // For BIP32
142
+ signers.signBIP32({ psbt, masterNode });
143
+
144
+ // For ECPair
145
+ signers.signECPair({ psbt, ecpair });
146
+ ```
147
+
148
+ #### Finalizing the PSBT
149
+
150
+ 1. For each unspent output, call `updatePsbtAsInput`:
151
+
152
+ ```javascript
153
+ const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout });
154
+ ```
155
+
156
+ 2. After signing, finalize each input:
157
+
158
+ ```javascript
159
+ inputFinalizer({ psbt });
160
+ ```
161
+
162
+ ### Key Expressions and Script Expressions
163
+
164
+ Helper functions for generating descriptor strings:
165
+
166
+ ```javascript
167
+ import { scriptExpressions, keyExpressionBIP32 } from '@kukks/bitcoin-descriptors';
168
+ ```
169
+
170
+ The `scriptExpressions` module includes functions like `pkhBIP32()`, `shWpkhBIP32()`, and `wpkhBIP32()` for generating descriptors for commonly used scripts.
171
+
172
+ The `keyExpressionBIP32` function generates BIP32 key expression strings:
173
+
174
+ ```javascript
175
+ keyExpressionBIP32({
176
+ masterNode, // BIP32Interface
177
+ originPath, // e.g. "/44'/0'/0'"
178
+ change, // 0 (receive) or 1 (change)
179
+ index, // number or '*'
180
+ isPublic // whether to use xpub or xprv
181
+ });
182
+ ```
183
+
184
+ ## Building from source
185
+
186
+ ```bash
187
+ git clone https://github.com/Kukks/descriptors.git
188
+ cd descriptors/
189
+ npm install
190
+ npm run build
191
+ ```
192
+
193
+ ## Testing
194
+
195
+ Before running tests, start a Bitcoin regtest node using the preconfigured Docker image:
196
+
197
+ ```bash
198
+ docker pull bitcoinerlab/tester
199
+ docker run -d -p 8080:8080 -p 60401:60401 -p 3002:3002 bitcoinerlab/tester
200
+ ```
201
+
202
+ Then run:
203
+
204
+ ```bash
205
+ npm run test
206
+ ```
207
+
208
+ ## License
209
+
210
+ This project is licensed under the MIT License.
211
+
212
+ ## Credits
213
+
214
+ Originally developed by [Jose-Luis Landabaso](https://github.com/landabaso) at [bitcoinerlab](https://github.com/bitcoinerlab). This fork is maintained by [Kukks](https://github.com/Kukks).
@@ -0,0 +1,6 @@
1
+ export declare const CHECKSUM_CHARSET: string;
2
+ /**
3
+ * Implements the Bitcoin descriptor's checksum algorithm described in
4
+ * {@link https://github.com/bitcoin/bitcoin/blob/master/src/script/descriptor.cpp}
5
+ */
6
+ export declare const DescriptorChecksum: (span: string) => string;
@@ -0,0 +1,54 @@
1
+ // Converted to Javascript by Jose-Luis Landabaso, 2023 - https://bitcoinerlab.com
2
+ // Source: https://github.com/bitcoin/bitcoin/blob/master/src/script/descriptor.cpp
3
+ // Distributed under the MIT software license
4
+ const PolyMod = (c, val) => {
5
+ const c0 = c >> 35n;
6
+ c = ((c & 0x7ffffffffn) << 5n) ^ val;
7
+ if (c0 & 1n)
8
+ c ^= 0xf5dee51989n;
9
+ if (c0 & 2n)
10
+ c ^= 0xa9fdca3312n;
11
+ if (c0 & 4n)
12
+ c ^= 0x1bab10e32dn;
13
+ if (c0 & 8n)
14
+ c ^= 0x3706b1677an;
15
+ if (c0 & 16n)
16
+ c ^= 0x644d626ffdn;
17
+ return c;
18
+ };
19
+ export const CHECKSUM_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
20
+ /**
21
+ * Implements the Bitcoin descriptor's checksum algorithm described in
22
+ * {@link https://github.com/bitcoin/bitcoin/blob/master/src/script/descriptor.cpp}
23
+ */
24
+ export const DescriptorChecksum = (span) => {
25
+ const INPUT_CHARSET = '0123456789()[],\'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#"\\ ';
26
+ let c = 1n;
27
+ let cls = 0n;
28
+ let clscount = 0n;
29
+ for (const ch of span) {
30
+ const pos = BigInt(INPUT_CHARSET.indexOf(ch));
31
+ if (pos === -1n)
32
+ return '';
33
+ c = PolyMod(c, pos & 31n);
34
+ cls = cls * 3n + (pos >> 5n);
35
+ if (++clscount === 3n) {
36
+ c = PolyMod(c, cls);
37
+ cls = 0n;
38
+ clscount = 0n;
39
+ }
40
+ }
41
+ if (clscount > 0n)
42
+ c = PolyMod(c, cls);
43
+ for (let j = 0; j < 8; ++j)
44
+ c = PolyMod(c, 0n);
45
+ c ^= 1n;
46
+ let ret = '';
47
+ for (let j = 0; j < 8; ++j) {
48
+ const index = (c >> (5n * (7n - BigInt(j)))) & 31n;
49
+ if (index < 0 || index > Number.MAX_SAFE_INTEGER)
50
+ throw new Error(`Error: could not compute checksum, invalid index ${index}`);
51
+ ret += CHECKSUM_CHARSET[Number(index)];
52
+ }
53
+ return ret;
54
+ };
@@ -0,0 +1,113 @@
1
+ import type { P2Ret } from '@scure/btc-signer/payment.js';
2
+ import { Network } from './networks.js';
3
+ import type { ECPairAPI, BIP32API, PartialSig, Preimage, TimeConstraints, Expansion, ExpansionMap, ParseKeyExpression } from './types.js';
4
+ import type { PsbtLike } from './psbt.js';
5
+ /**
6
+ * Constructs the necessary functions and classes for working with descriptors.
7
+ *
8
+ * Notably, it returns the {@link _Internal_.Output | `Output`} class, which
9
+ * provides methods to create, sign, and finalize PSBTs based on descriptor
10
+ * expressions.
11
+ *
12
+ * The Factory also returns utility methods like `expand` (detailed below)
13
+ * and `parseKeyExpression` (see {@link ParseKeyExpression}).
14
+ *
15
+ * Additionally, for convenience, the function returns `BIP32` and `ECPair`.
16
+ * These are compatible interfaces for managing BIP32 keys and
17
+ * public/private key pairs respectively.
18
+ *
19
+ * @param {Object} params - An object with `ECPair` and `BIP32` factories.
20
+ */
21
+ export declare function DescriptorsFactory({ ECPair, BIP32 }: {
22
+ ECPair: ECPairAPI;
23
+ BIP32: BIP32API;
24
+ }): {
25
+ Output: {
26
+ new ({ descriptor, index, checksumRequired, allowMiniscriptInP2SH, network, preimages, signersPubKeys }: {
27
+ descriptor: string;
28
+ index?: number;
29
+ checksumRequired?: boolean;
30
+ allowMiniscriptInP2SH?: boolean;
31
+ network?: Network;
32
+ preimages?: Preimage[];
33
+ signersPubKeys?: Uint8Array[];
34
+ }): {
35
+ readonly "__#private@#payment": P2Ret;
36
+ readonly "__#private@#preimages": Preimage[];
37
+ readonly "__#private@#signersPubKeys": Uint8Array[];
38
+ readonly "__#private@#miniscript"?: string;
39
+ readonly "__#private@#witnessScript"?: Uint8Array;
40
+ readonly "__#private@#redeemScript"?: Uint8Array;
41
+ readonly "__#private@#isSegwit"?: boolean;
42
+ readonly "__#private@#isTaproot"?: boolean;
43
+ readonly "__#private@#expandedExpression"?: string;
44
+ readonly "__#private@#expandedMiniscript"?: string;
45
+ readonly "__#private@#expansionMap"?: ExpansionMap;
46
+ readonly "__#private@#network": Network;
47
+ "__#private@#getTimeConstraints"(): TimeConstraints | undefined;
48
+ getPayment(): P2Ret;
49
+ getAddress(): string;
50
+ getScriptPubKey(): Uint8Array;
51
+ getScriptSatisfaction(signatures: PartialSig[] | "DANGEROUSLY_USE_FAKE_SIGNATURES"): Uint8Array;
52
+ getSequence(): number | undefined;
53
+ getLockTime(): number | undefined;
54
+ getWitnessScript(): Uint8Array | undefined;
55
+ getRedeemScript(): Uint8Array | undefined;
56
+ getNetwork(): Network;
57
+ isSegwit(): boolean | undefined;
58
+ isTaproot(): boolean | undefined;
59
+ guessOutput(): {
60
+ isPKH: boolean;
61
+ isWPKH: boolean;
62
+ isSH: boolean;
63
+ isWSH: boolean;
64
+ isTR: boolean;
65
+ };
66
+ inputWeight(isSegwitTx: boolean, signatures: PartialSig[] | "DANGEROUSLY_USE_FAKE_SIGNATURES"): number;
67
+ outputWeight(): number;
68
+ updatePsbtAsInput({ psbt, txHex, txId, value, vout, rbf }: {
69
+ psbt: PsbtLike;
70
+ txHex?: string;
71
+ txId?: string;
72
+ value?: number;
73
+ vout: number;
74
+ rbf?: boolean;
75
+ }): ({ psbt, validate }: {
76
+ psbt: PsbtLike;
77
+ validate?: boolean | undefined;
78
+ }) => void;
79
+ updatePsbtAsOutput({ psbt, value }: {
80
+ psbt: PsbtLike;
81
+ value: number | bigint;
82
+ }): void;
83
+ "__#private@#assertPsbtInput"({ psbt, index }: {
84
+ psbt: PsbtLike;
85
+ index: number;
86
+ }): void;
87
+ finalizePsbtInput({ index, psbt, validate }: {
88
+ index: number;
89
+ psbt: PsbtLike;
90
+ validate?: boolean | undefined;
91
+ }): void;
92
+ expand(): {
93
+ expansionMap?: ExpansionMap;
94
+ expandedMiniscript?: string;
95
+ miniscript?: string;
96
+ expandedExpression?: string;
97
+ };
98
+ };
99
+ };
100
+ parseKeyExpression: ParseKeyExpression;
101
+ expand: ({ descriptor, index, checksumRequired, network, allowMiniscriptInP2SH }: {
102
+ descriptor: string;
103
+ index?: number;
104
+ checksumRequired?: boolean;
105
+ network?: Network;
106
+ allowMiniscriptInP2SH?: boolean;
107
+ }) => Expansion;
108
+ ECPair: ECPairAPI;
109
+ BIP32: BIP32API;
110
+ };
111
+ type OutputConstructor = ReturnType<typeof DescriptorsFactory>['Output'];
112
+ type OutputInstance = InstanceType<OutputConstructor>;
113
+ export { OutputInstance, OutputConstructor };