@bitcoinerlab/descriptors 1.1.1 → 2.0.0

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/dist/signers.js CHANGED
@@ -26,37 +26,77 @@ const ledgerSignaturesForInputIndex = (index, ledgerSignatures) => ledgerSignatu
26
26
  pubkey: partialSignature.pubkey,
27
27
  signature: partialSignature.signature
28
28
  }));
29
- async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }) {
29
+ /**
30
+ * To be removed in v3.0 and replaced by a version that does not accept
31
+ * descriptor
32
+ * @hidden
33
+ */
34
+ async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState, ledgerManager }) {
35
+ if (!descriptor && !ledgerManager)
36
+ throw new Error(`ledgerManager not provided`);
37
+ if (descriptor && ledgerManager)
38
+ throw new Error(`Invalid usage: don't pass descriptor`);
39
+ if (ledgerManager && (ledgerClient || ledgerState))
40
+ throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
41
+ const output = descriptor;
42
+ if (ledgerManager)
43
+ ({ ledgerClient, ledgerState } = ledgerManager);
44
+ if (!ledgerClient)
45
+ throw new Error(`ledgerManager not provided`);
46
+ if (!ledgerState)
47
+ throw new Error(`ledgerManager not provided`);
30
48
  const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
31
49
  if (!(ledgerClient instanceof AppClient))
32
50
  throw new Error(`Error: pass a valid ledgerClient`);
33
- const result = await (0, ledger_1.descriptorToLedgerFormat)({
34
- descriptor,
35
- ledgerClient,
36
- ledgerState
37
- });
38
- if (!result)
39
- throw new Error(`Error: descriptor does not have a ledger input`);
40
- const { ledgerTemplate, keyRoots } = result;
41
51
  let ledgerSignatures;
42
- const standardPolicy = await (0, ledger_1.ledgerPolicyFromStandard)({
43
- descriptor,
44
- ledgerClient,
45
- ledgerState
46
- });
47
- if (standardPolicy) {
48
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
52
+ if (ledgerManager) {
53
+ const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
54
+ psbt,
55
+ index,
56
+ ledgerManager
57
+ });
58
+ if (!policy)
59
+ throw new Error(`Error: the ledger cannot sign this pstb input`);
60
+ if (policy.policyName && policy.policyHmac && policy.policyId) {
61
+ //non-standard policy
62
+ const walletPolicy = new WalletPolicy(policy.policyName, policy.ledgerTemplate, policy.keyRoots);
63
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
64
+ }
65
+ else {
66
+ //standard policy
67
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(policy.ledgerTemplate, policy.keyRoots[0]), null);
68
+ }
49
69
  }
50
70
  else {
51
- const policy = await (0, ledger_1.ledgerPolicyFromState)({
52
- descriptor,
71
+ if (!output)
72
+ throw new Error(`outputs not provided`);
73
+ const result = await (0, ledger_1.ledgerPolicyFromOutput)({
74
+ output,
75
+ ledgerClient,
76
+ ledgerState
77
+ });
78
+ if (!result)
79
+ throw new Error(`Error: output does not have a ledger input`);
80
+ const { ledgerTemplate, keyRoots } = result;
81
+ const standardPolicy = await (0, ledger_1.ledgerPolicyFromStandard)({
82
+ output,
53
83
  ledgerClient,
54
84
  ledgerState
55
85
  });
56
- if (!policy || !policy.policyName || !policy.policyHmac)
57
- throw new Error(`Error: the descriptor's policy is not registered`);
58
- const walletPolicy = new WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
59
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
86
+ if (standardPolicy) {
87
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
88
+ }
89
+ else {
90
+ const policy = await (0, ledger_1.ledgerPolicyFromState)({
91
+ output,
92
+ ledgerClient,
93
+ ledgerState
94
+ });
95
+ if (!policy || !policy.policyName || !policy.policyHmac)
96
+ throw new Error(`Error: the descriptor's policy is not registered`);
97
+ const walletPolicy = new WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
98
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
99
+ }
60
100
  }
61
101
  //Add the signatures to the Psbt object using PartialSig format:
62
102
  psbt.updateInput(index, {
@@ -64,30 +104,51 @@ async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerSt
64
104
  });
65
105
  }
66
106
  exports.signInputLedger = signInputLedger;
67
- //signLedger is able to sign several inputs of the same wallet policy since it
68
- //it clusters together wallet policy types before signing
69
- //it throws if it cannot sign any input.
70
- async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
107
+ /**
108
+ * To be removed in v3.0 and replaced by a version that does not accept
109
+ * descriptors
110
+ * @hidden
111
+ */
112
+ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState, ledgerManager }) {
113
+ if (!descriptors && !ledgerManager)
114
+ throw new Error(`ledgerManager not provided`);
115
+ if (descriptors && ledgerManager)
116
+ throw new Error(`Invalid usage: don't pass descriptors`);
117
+ if (ledgerManager && (ledgerClient || ledgerState))
118
+ throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
119
+ const outputs = descriptors;
120
+ if (ledgerManager)
121
+ ({ ledgerClient, ledgerState } = ledgerManager);
122
+ if (!ledgerClient)
123
+ throw new Error(`ledgerManager not provided`);
124
+ if (!ledgerState)
125
+ throw new Error(`ledgerManager not provided`);
71
126
  const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
72
127
  if (!(ledgerClient instanceof AppClient))
73
128
  throw new Error(`Error: pass a valid ledgerClient`);
74
129
  const ledgerPolicies = [];
75
- for (const descriptor of descriptors) {
76
- const policy = (await (0, ledger_1.ledgerPolicyFromState)({
77
- descriptor,
78
- ledgerClient,
79
- ledgerState
80
- })) ||
81
- (await (0, ledger_1.ledgerPolicyFromStandard)({
82
- descriptor,
83
- ledgerClient,
84
- ledgerState
85
- }));
86
- if (policy)
87
- ledgerPolicies.push(policy);
130
+ if (ledgerManager)
131
+ for (let index = 0; index < psbt.data.inputs.length; index++) {
132
+ const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
133
+ psbt,
134
+ index,
135
+ ledgerManager
136
+ });
137
+ if (policy)
138
+ ledgerPolicies.push(policy);
139
+ }
140
+ else {
141
+ if (!outputs)
142
+ throw new Error(`outputs not provided`);
143
+ for (const output of outputs) {
144
+ const policy = (await (0, ledger_1.ledgerPolicyFromState)({ output, ledgerClient, ledgerState })) ||
145
+ (await (0, ledger_1.ledgerPolicyFromStandard)({ output, ledgerClient, ledgerState }));
146
+ if (policy)
147
+ ledgerPolicies.push(policy);
148
+ }
149
+ if (ledgerPolicies.length === 0)
150
+ throw new Error(`Error: there are no inputs which could be signed`);
88
151
  }
89
- if (ledgerPolicies.length === 0)
90
- throw new Error(`Error: there are no inputs which could be signed`);
91
152
  //cluster unique LedgerPolicies
92
153
  const uniquePolicies = [];
93
154
  for (const policy of ledgerPolicies) {
package/dist/types.d.ts CHANGED
@@ -23,6 +23,9 @@ export type TimeConstraints = {
23
23
  nLockTime: number | undefined;
24
24
  nSequence: number | undefined;
25
25
  };
26
+ /**
27
+ * See {@link _Internal_.ParseKeyExpression | ParseKeyExpression}.
28
+ */
26
29
  export type KeyInfo = {
27
30
  keyExpression: string;
28
31
  pubkey?: Buffer;
@@ -33,6 +36,35 @@ export type KeyInfo = {
33
36
  keyPath?: string;
34
37
  path?: string;
35
38
  };
39
+ /**
40
+ * An `ExpansionMap` contains destructured information of a descritptor expression.
41
+ *
42
+ * For example, this descriptor `sh(wsh(andor(pk(0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2),older(8640),pk([d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*))))` has the following
43
+ * `expandedExpression`: `sh(wsh(andor(pk(@0),older(8640),pk(@1))))`
44
+ *
45
+ * `key`'s are set using this format: `@i`, where `i` is an integer starting from `0` assigned by parsing and retrieving keys from the descriptor from left to right.
46
+ *
47
+ * For the given example, the `ExpansionMap` is:
48
+ *
49
+ * ```javascript
50
+ * {
51
+ * '@0': {
52
+ * keyExpression:
53
+ * '0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2'
54
+ * },
55
+ * '@1': {
56
+ * keyExpression:
57
+ * "[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*",
58
+ * keyPath: '/1/2/3/4/*',
59
+ * originPath: "/49'/0'/0'",
60
+ * path: "m/49'/0'/0'/1/2/3/4/*",
61
+ * // Other relevant properties of the type `KeyInfo`: `pubkey`, `ecpair` & `bip32` interfaces, `masterFingerprint`, etc.
62
+ * }
63
+ * }
64
+ *```
65
+ *
66
+ *
67
+ */
36
68
  export type ExpansionMap = {
37
69
  [key: string]: KeyInfo;
38
70
  };
@@ -56,6 +88,11 @@ export interface TinySecp256k1Interface {
56
88
  xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
57
89
  privateNegate(d: Uint8Array): Uint8Array;
58
90
  }
91
+ /**
92
+ * `DescriptorsFactory` creates and returns the {@link DescriptorsFactory | `expand()`}
93
+ * function that parses a descriptor expression and destructures it
94
+ * into its elemental parts. `Expansion` is the type that `expand()` returns.
95
+ */
59
96
  export type Expansion = {
60
97
  /**
61
98
  * The corresponding [bitcoinjs-lib Payment](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts) for the provided expression, if applicable.
@@ -63,6 +100,7 @@ export type Expansion = {
63
100
  payment?: Payment;
64
101
  /**
65
102
  * The expanded descriptor expression.
103
+ * See {@link ExpansionMap ExpansionMap} for a detailed explanation.
66
104
  */
67
105
  expandedExpression?: string;
68
106
  /**
@@ -71,14 +109,17 @@ export type Expansion = {
71
109
  miniscript?: string;
72
110
  /**
73
111
  * A map of key expressions in the descriptor to their corresponding expanded keys.
112
+ * See {@link ExpansionMap ExpansionMap} for a detailed explanation.
74
113
  */
75
114
  expansionMap?: ExpansionMap;
76
115
  /**
77
- * A boolean indicating whether the descriptor represents a SegWit script.
116
+ * A boolean indicating whether the descriptor uses SegWit.
78
117
  */
79
118
  isSegwit?: boolean;
80
119
  /**
81
120
  * The expanded miniscript, if any.
121
+ * It corresponds to the `expandedExpression` without the top-level script
122
+ * expression.
82
123
  */
83
124
  expandedMiniscript?: string;
84
125
  /**
@@ -90,56 +131,47 @@ export type Expansion = {
90
131
  */
91
132
  witnessScript?: Buffer;
92
133
  /**
93
- * Whether this expression represents a ranged-descriptor.
134
+ * Whether the descriptor is a ranged-descriptor.
94
135
  */
95
136
  isRanged: boolean;
96
137
  /**
97
- * This is the preferred or authoritative representation of the descriptor expression.
138
+ * This is the preferred or authoritative representation of an output
139
+ * descriptor expression.
140
+ * It removes the checksum and, if it is a ranged-descriptor, it
141
+ * particularizes it to its index.
98
142
  */
99
143
  canonicalExpression: string;
100
144
  };
101
145
  /**
102
- * The {@link DescriptorsFactory | `DescriptorsFactory`} function creates and returns an implementation of the `Expand` interface.
103
- * This returned implementation is tailored for the provided `TinySecp256k1Interface`.
104
- */
105
- export interface Expand {
106
- (params: {
107
- /**
108
- * The descriptor expression to be expanded.
109
- */
110
- expression: string;
111
- /**
112
- * The descriptor index, if ranged.
113
- */
114
- index?: number;
115
- /**
116
- * A flag indicating whether the descriptor is required to include a checksum.
117
- * @defaultValue false
118
- */
119
- checksumRequired?: boolean;
120
- /**
121
- * The Bitcoin network to use.
122
- * @defaultValue `networks.bitcoin`
123
- */
124
- network?: Network;
125
- /**
126
- * Flag to allow miniscript in P2SH.
127
- * @defaultValue false
128
- */
129
- allowMiniscriptInP2SH?: boolean;
130
- }): Expansion;
131
- }
132
- /**
133
- * The {@link DescriptorsFactory | `DescriptorsFactory`} function creates and returns an implementation of the `ParseKeyExpression` interface.
134
- * This returned implementation is tailored for the provided `TinySecp256k1Interface`.
146
+ * The {@link DescriptorsFactory | `DescriptorsFactory`} function creates and
147
+ * returns the `parseKeyExpression` function, which is an implementation of this
148
+ * interface.
149
+ *
150
+ * It parses and destructures a key expression string (xpub, xprv, pubkey or
151
+ * wif) into {@link KeyInfo | `KeyInfo`}.
152
+ *
153
+ * For example, given this `keyExpression`: `[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*`, this is the parsed result:
154
+ *
155
+ * ```javascript
156
+ * {
157
+ * keyExpression:
158
+ * "[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*",
159
+ * keyPath: '/1/2/3/4/*',
160
+ * originPath: "/49'/0'/0'",
161
+ * path: "m/49'/0'/0'/1/2/3/4/*",
162
+ * // Other relevant properties of the type `KeyInfo`: `pubkey`, `ecpair` & `bip32` interfaces, `masterFingerprint`, etc.
163
+ * }
164
+ * ```
165
+ *
166
+ * See {@link KeyInfo} for the complete list of elements retrieved by this function.
135
167
  */
136
168
  export interface ParseKeyExpression {
137
169
  (params: {
138
170
  keyExpression: string;
139
171
  /**
140
- * Indicates if this is a SegWit key expression. When set, further checks
141
- * ensure the public key (if present in the expression) is compressed
142
- * (33 bytes).
172
+ * Indicates if this key expression belongs to a a SegWit output. When set,
173
+ * further checks are done to ensure the public key (if present in the
174
+ * expression) is compressed (33 bytes).
143
175
  */
144
176
  isSegwit?: boolean;
145
177
  network?: Network;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@bitcoinerlab/descriptors",
3
3
  "description": "This library parses and creates Bitcoin Miniscript Descriptors and generates Partially Signed Bitcoin Transactions (PSBTs). It provides PSBT finalizers and signers for single-signature, BIP32 and Hardware Wallets.",
4
4
  "homepage": "https://github.com/bitcoinerlab/descriptors",
5
- "version": "1.1.1",
5
+ "version": "2.0.0",
6
6
  "author": "Jose-Luis Landabaso",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -38,10 +38,11 @@
38
38
  "ensureTester": "./node_modules/@bitcoinerlab/configs/scripts/ensureTester.sh",
39
39
  "test:integration:soft": "npm run ensureTester && node test/integration/standardOutputs.js && echo \"\\n\\n\" && node test/integration/miniscript.js",
40
40
  "test:integration:ledger": "npm run ensureTester && node test/integration/ledger.js",
41
+ "test:integration:deprecated": "npm run ensureTester && node test/integration/standardOutputs-deprecated.js && echo \"\\n\\n\" && node test/integration/miniscript-deprecated.js && echo \"\\n\\n\" && node test/integration/ledger-deprecated.js",
41
42
  "test:unit": "jest",
42
43
  "test": "npm run lint && npm run build && npm run test:unit && npm run test:integration:soft",
43
44
  "testledger": "npm run lint && npm run build && npm run test:integration:ledger",
44
- "prepublishOnly": "npm run test && echo \"\\n\\n\" && npm run test:integration:ledger"
45
+ "prepublishOnly": "npm run test && echo \"\\n\\n\" && npm run test:integration:deprecated && npm run test:integration:ledger"
45
46
  },
46
47
  "files": [
47
48
  "dist"