@bitcoinerlab/descriptors 2.3.6 → 3.0.1
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 +173 -77
- package/dist/applyPR2137.js +36 -17
- package/dist/bitcoinjs-lib-internals.d.ts +10 -0
- package/dist/bitcoinjs-lib-internals.js +18 -0
- package/dist/descriptors.d.ts +161 -392
- package/dist/descriptors.js +608 -283
- package/dist/index.d.ts +2 -29
- package/dist/index.js +0 -14
- package/dist/keyExpressions.d.ts +4 -13
- package/dist/keyExpressions.js +15 -18
- package/dist/ledger.d.ts +14 -37
- package/dist/ledger.js +147 -119
- package/dist/miniscript.d.ts +20 -6
- package/dist/miniscript.js +64 -17
- package/dist/multipath.d.ts +13 -0
- package/dist/multipath.js +76 -0
- package/dist/networkUtils.d.ts +3 -0
- package/dist/networkUtils.js +16 -0
- package/dist/parseUtils.d.ts +7 -0
- package/dist/parseUtils.js +46 -0
- package/dist/psbt.d.ts +17 -13
- package/dist/psbt.js +34 -50
- package/dist/resourceLimits.d.ts +25 -0
- package/dist/resourceLimits.js +89 -0
- package/dist/scriptExpressions.d.ts +29 -77
- package/dist/scriptExpressions.js +19 -16
- package/dist/signers.d.ts +1 -21
- package/dist/signers.js +85 -129
- package/dist/tapMiniscript.d.ts +215 -0
- package/dist/tapMiniscript.js +515 -0
- package/dist/tapTree.d.ts +86 -0
- package/dist/tapTree.js +167 -0
- package/dist/types.d.ts +52 -6
- package/package.json +13 -13
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2026 Jose-Luis Landabaso
|
|
3
|
+
// Distributed under the MIT software license
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.MAX_STANDARD_P2WSH_SCRIPT_SIZE = exports.MAX_SCRIPT_ELEMENT_SIZE = void 0;
|
|
6
|
+
exports.assertScriptNonPushOnlyOpsLimit = assertScriptNonPushOnlyOpsLimit;
|
|
7
|
+
exports.assertConsensusStackResourceLimits = assertConsensusStackResourceLimits;
|
|
8
|
+
exports.assertWitnessV0SatisfactionResourceLimits = assertWitnessV0SatisfactionResourceLimits;
|
|
9
|
+
exports.assertTaprootScriptPathSatisfactionResourceLimits = assertTaprootScriptPathSatisfactionResourceLimits;
|
|
10
|
+
exports.assertP2shScriptSigStandardSize = assertP2shScriptSigStandardSize;
|
|
11
|
+
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
12
|
+
const { p2sh } = bitcoinjs_lib_1.payments;
|
|
13
|
+
// See Sipa's Miniscript "Resource limitations":
|
|
14
|
+
// https://bitcoin.sipa.be/miniscript/
|
|
15
|
+
// and Bitcoin Core policy/consensus constants.
|
|
16
|
+
//https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.h
|
|
17
|
+
// Consensus: max number of elements in initial stack (and stack+altstack after
|
|
18
|
+
// each opcode execution).
|
|
19
|
+
const MAX_STACK_SIZE = 1000;
|
|
20
|
+
// Consensus: max size for any stack element is 520 bytes.
|
|
21
|
+
// This is a per-element limit, not a full script-size limit.
|
|
22
|
+
// In legacy P2SH, redeemScript is pushed as a stack element,
|
|
23
|
+
// ( this is why we typically say that the redeemScript cannot be larger than 520 ).
|
|
24
|
+
// But the same per-element rule applies to other stack items as well.
|
|
25
|
+
exports.MAX_SCRIPT_ELEMENT_SIZE = 520;
|
|
26
|
+
// Standardness policy limits.
|
|
27
|
+
// See Sipa's Miniscript "Resource limitations":
|
|
28
|
+
// https://bitcoin.sipa.be/miniscript/
|
|
29
|
+
// and Bitcoin Core policy/consensus constants.
|
|
30
|
+
//https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.h
|
|
31
|
+
exports.MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
|
32
|
+
const MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
|
|
33
|
+
const MAX_OPS_PER_SCRIPT = 201;
|
|
34
|
+
const MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
|
|
35
|
+
const MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
|
|
36
|
+
const MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
|
|
37
|
+
function countNonPushOnlyOPs(script) {
|
|
38
|
+
const chunks = bitcoinjs_lib_1.script.decompile(script);
|
|
39
|
+
if (!chunks)
|
|
40
|
+
throw new Error(`Error: could not decompile ${script}`);
|
|
41
|
+
return bitcoinjs_lib_1.script.countNonPushOnlyOPs(chunks);
|
|
42
|
+
}
|
|
43
|
+
function assertScriptNonPushOnlyOpsLimit({ script }) {
|
|
44
|
+
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
45
|
+
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT)
|
|
46
|
+
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Enforces consensus stack resource limits.
|
|
50
|
+
*/
|
|
51
|
+
function assertConsensusStackResourceLimits({ stackItems, stackLabel = 'stack', stackItemLabel = 'stack item' }) {
|
|
52
|
+
if (stackItems.length > MAX_STACK_SIZE)
|
|
53
|
+
throw new Error(`Error: ${stackLabel} has too many items, ${stackItems.length} is larger than ${MAX_STACK_SIZE}`);
|
|
54
|
+
for (const stackItem of stackItems) {
|
|
55
|
+
if (stackItem.length > exports.MAX_SCRIPT_ELEMENT_SIZE)
|
|
56
|
+
throw new Error(`Error: ${stackItemLabel} is too large, ${stackItem.length} bytes is larger than ${exports.MAX_SCRIPT_ELEMENT_SIZE} bytes`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function assertWitnessV0SatisfactionResourceLimits({ stackItems }) {
|
|
60
|
+
assertConsensusStackResourceLimits({ stackItems });
|
|
61
|
+
if (stackItems.length > MAX_STANDARD_P2WSH_STACK_ITEMS)
|
|
62
|
+
throw new Error(`Error: witness stack has too many items, ${stackItems.length} is larger than ${MAX_STANDARD_P2WSH_STACK_ITEMS}`);
|
|
63
|
+
for (const stackItem of stackItems) {
|
|
64
|
+
if (stackItem.length > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE)
|
|
65
|
+
throw new Error(`Error: witness stack item exceeds standard policy, ${stackItem.length} bytes is larger than ${MAX_STANDARD_P2WSH_STACK_ITEM_SIZE} bytes`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function assertTaprootScriptPathSatisfactionResourceLimits({ stackItems }) {
|
|
69
|
+
assertConsensusStackResourceLimits({
|
|
70
|
+
stackItems,
|
|
71
|
+
stackLabel: 'taproot script-path stack',
|
|
72
|
+
stackItemLabel: 'taproot script-path stack item'
|
|
73
|
+
});
|
|
74
|
+
// Standardness policy for tapscript (leaf version 0xc0): <= 80 bytes.
|
|
75
|
+
for (const stackItem of stackItems) {
|
|
76
|
+
if (stackItem.length > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE)
|
|
77
|
+
throw new Error(`Error: taproot script-path stack item exceeds standard policy, ${stackItem.length} bytes is larger than ${MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE} bytes`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function assertP2shScriptSigStandardSize({ scriptSatisfaction, redeemScript, network }) {
|
|
81
|
+
const scriptSig = p2sh({
|
|
82
|
+
redeem: { input: scriptSatisfaction, output: redeemScript, network },
|
|
83
|
+
network
|
|
84
|
+
}).input;
|
|
85
|
+
if (!scriptSig)
|
|
86
|
+
throw new Error(`Error: could not build scriptSig from satisfaction`);
|
|
87
|
+
if (scriptSig.length > MAX_STANDARD_SCRIPTSIG_SIZE)
|
|
88
|
+
throw new Error(`Error: scriptSig is too large, ${scriptSig.length} bytes is larger than ${MAX_STANDARD_SCRIPTSIG_SIZE} bytes`);
|
|
89
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Network } from 'bitcoinjs-lib';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LedgerManager } from './ledger';
|
|
3
3
|
import type { BIP32Interface } from 'bip32';
|
|
4
4
|
/** @function */
|
|
5
5
|
export declare const pkhBIP32: ({ masterNode, network, keyPath, account, change, index, isPublic }: {
|
|
@@ -62,82 +62,34 @@ export declare const trBIP32: ({ masterNode, network, keyPath, account, change,
|
|
|
62
62
|
isPublic?: boolean;
|
|
63
63
|
}) => string;
|
|
64
64
|
/** @function */
|
|
65
|
-
export declare const pkhLedger: {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}): Promise<string>;
|
|
73
|
-
({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
|
|
74
|
-
ledgerClient: unknown;
|
|
75
|
-
ledgerState: LedgerState;
|
|
76
|
-
/** @default networks.bitcoin */
|
|
77
|
-
network?: Network;
|
|
78
|
-
account: number;
|
|
79
|
-
keyPath?: string;
|
|
80
|
-
change?: number | undefined;
|
|
81
|
-
index?: number | undefined | "*";
|
|
82
|
-
}): Promise<string>;
|
|
83
|
-
};
|
|
65
|
+
export declare const pkhLedger: ({ ledgerManager, account, keyPath, change, index }: {
|
|
66
|
+
ledgerManager: LedgerManager;
|
|
67
|
+
account: number;
|
|
68
|
+
keyPath?: string;
|
|
69
|
+
change?: number | undefined;
|
|
70
|
+
index?: number | undefined | "*";
|
|
71
|
+
}) => Promise<string>;
|
|
84
72
|
/** @function */
|
|
85
|
-
export declare const shWpkhLedger: {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}): Promise<string>;
|
|
93
|
-
({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
|
|
94
|
-
ledgerClient: unknown;
|
|
95
|
-
ledgerState: LedgerState;
|
|
96
|
-
/** @default networks.bitcoin */
|
|
97
|
-
network?: Network;
|
|
98
|
-
account: number;
|
|
99
|
-
keyPath?: string;
|
|
100
|
-
change?: number | undefined;
|
|
101
|
-
index?: number | undefined | "*";
|
|
102
|
-
}): Promise<string>;
|
|
103
|
-
};
|
|
73
|
+
export declare const shWpkhLedger: ({ ledgerManager, account, keyPath, change, index }: {
|
|
74
|
+
ledgerManager: LedgerManager;
|
|
75
|
+
account: number;
|
|
76
|
+
keyPath?: string;
|
|
77
|
+
change?: number | undefined;
|
|
78
|
+
index?: number | undefined | "*";
|
|
79
|
+
}) => Promise<string>;
|
|
104
80
|
/** @function */
|
|
105
|
-
export declare const wpkhLedger: {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}): Promise<string>;
|
|
113
|
-
({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
|
|
114
|
-
ledgerClient: unknown;
|
|
115
|
-
ledgerState: LedgerState;
|
|
116
|
-
/** @default networks.bitcoin */
|
|
117
|
-
network?: Network;
|
|
118
|
-
account: number;
|
|
119
|
-
keyPath?: string;
|
|
120
|
-
change?: number | undefined;
|
|
121
|
-
index?: number | undefined | "*";
|
|
122
|
-
}): Promise<string>;
|
|
123
|
-
};
|
|
81
|
+
export declare const wpkhLedger: ({ ledgerManager, account, keyPath, change, index }: {
|
|
82
|
+
ledgerManager: LedgerManager;
|
|
83
|
+
account: number;
|
|
84
|
+
keyPath?: string;
|
|
85
|
+
change?: number | undefined;
|
|
86
|
+
index?: number | undefined | "*";
|
|
87
|
+
}) => Promise<string>;
|
|
124
88
|
/** @function */
|
|
125
|
-
export declare const trLedger: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}): Promise<string>;
|
|
133
|
-
({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
|
|
134
|
-
ledgerClient: unknown;
|
|
135
|
-
ledgerState: LedgerState;
|
|
136
|
-
/** @default networks.bitcoin */
|
|
137
|
-
network?: Network;
|
|
138
|
-
account: number;
|
|
139
|
-
keyPath?: string;
|
|
140
|
-
change?: number | undefined;
|
|
141
|
-
index?: number | undefined | "*";
|
|
142
|
-
}): Promise<string>;
|
|
143
|
-
};
|
|
89
|
+
export declare const trLedger: ({ ledgerManager, account, keyPath, change, index }: {
|
|
90
|
+
ledgerManager: LedgerManager;
|
|
91
|
+
account: number;
|
|
92
|
+
keyPath?: string;
|
|
93
|
+
change?: number | undefined;
|
|
94
|
+
index?: number | undefined | "*";
|
|
95
|
+
}) => Promise<string>;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.trLedger = exports.wpkhLedger = exports.shWpkhLedger = exports.pkhLedger = exports.trBIP32 = exports.wpkhBIP32 = exports.shWpkhBIP32 = exports.pkhBIP32 = void 0;
|
|
4
4
|
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
5
5
|
const keyExpressions_1 = require("./keyExpressions");
|
|
6
|
+
const networkUtils_1 = require("./networkUtils");
|
|
6
7
|
function assertStandardKeyPath(keyPath) {
|
|
7
8
|
// Regular expression to match "/change/index" or "/change/*" format
|
|
8
9
|
const regex = /^\/[01]\/(\d+|\*)$/;
|
|
@@ -25,7 +26,7 @@ function standardExpressionsBIP32Maker(purpose, scriptTemplate) {
|
|
|
25
26
|
* - `{change:0, index:'*'}`.
|
|
26
27
|
*/
|
|
27
28
|
function standardScriptExpressionBIP32({ masterNode, network = bitcoinjs_lib_1.networks.bitcoin, keyPath, account, change, index, isPublic = true }) {
|
|
28
|
-
const originPath = `/${purpose}'/${
|
|
29
|
+
const originPath = `/${purpose}'/${(0, networkUtils_1.coinTypeFromNetwork)(network)}'/${account}'`;
|
|
29
30
|
if (keyPath !== undefined)
|
|
30
31
|
assertStandardKeyPath(keyPath);
|
|
31
32
|
const keyExpression = (0, keyExpressions_1.keyExpressionBIP32)({
|
|
@@ -49,24 +50,26 @@ exports.wpkhBIP32 = standardExpressionsBIP32Maker(84, 'wpkh(KEYEXPRESSION)');
|
|
|
49
50
|
/** @function */
|
|
50
51
|
exports.trBIP32 = standardExpressionsBIP32Maker(86, 'tr(KEYEXPRESSION)');
|
|
51
52
|
function standardExpressionsLedgerMaker(purpose, scriptTemplate) {
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Computes the standard descriptor based on given parameters.
|
|
55
|
+
*
|
|
56
|
+
* You can define the output location either by:
|
|
57
|
+
* - Providing the full `keyPath` (e.g., "/0/2").
|
|
58
|
+
* OR
|
|
59
|
+
* - Specifying the `change` and `index` values separately (e.g., `{change:0, index:2}`).
|
|
60
|
+
*
|
|
61
|
+
* For ranged indexing, the `index` can be set as a wildcard '*'. For example:
|
|
62
|
+
* - `keyPath="/0/*"`
|
|
63
|
+
* OR
|
|
64
|
+
* - `{change:0, index:'*'}`.
|
|
65
|
+
*/
|
|
66
|
+
async function standardScriptExpressionLedger({ ledgerManager, account, keyPath, change, index }) {
|
|
67
|
+
const { network } = ledgerManager;
|
|
68
|
+
const originPath = `/${purpose}'/${(0, networkUtils_1.coinTypeFromNetwork)(network)}'/${account}'`;
|
|
65
69
|
if (keyPath !== undefined)
|
|
66
70
|
assertStandardKeyPath(keyPath);
|
|
67
71
|
const keyExpression = await (0, keyExpressions_1.keyExpressionLedger)({
|
|
68
|
-
|
|
69
|
-
ledgerState,
|
|
72
|
+
ledgerManager,
|
|
70
73
|
originPath,
|
|
71
74
|
keyPath,
|
|
72
75
|
change,
|
package/dist/signers.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Psbt } from 'bitcoinjs-lib';
|
|
2
2
|
import type { ECPairInterface } from 'ecpair';
|
|
3
3
|
import type { BIP32Interface } from 'bip32';
|
|
4
|
-
import
|
|
5
|
-
import { LedgerState, LedgerManager } from './ledger';
|
|
4
|
+
import { LedgerManager } from './ledger';
|
|
6
5
|
/**
|
|
7
6
|
* Signs a specific input of a PSBT with an ECPair.
|
|
8
7
|
*
|
|
@@ -69,16 +68,6 @@ export declare function signInputLedger({ psbt, index, ledgerManager }: {
|
|
|
69
68
|
index: number;
|
|
70
69
|
ledgerManager: LedgerManager;
|
|
71
70
|
}): Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* @hidden
|
|
74
|
-
*/
|
|
75
|
-
export declare function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }: {
|
|
76
|
-
psbt: Psbt;
|
|
77
|
-
index: number;
|
|
78
|
-
descriptor: DescriptorInstance;
|
|
79
|
-
ledgerClient: unknown;
|
|
80
|
-
ledgerState: LedgerState;
|
|
81
|
-
}): Promise<void>;
|
|
82
71
|
/**
|
|
83
72
|
* Signs the inputs of the `psbt` where the keys are controlled by a Ledger
|
|
84
73
|
* device.
|
|
@@ -93,12 +82,3 @@ export declare function signLedger({ psbt, ledgerManager }: {
|
|
|
93
82
|
psbt: Psbt;
|
|
94
83
|
ledgerManager: LedgerManager;
|
|
95
84
|
}): Promise<void>;
|
|
96
|
-
/**
|
|
97
|
-
* @hidden
|
|
98
|
-
*/
|
|
99
|
-
export declare function signLedger({ psbt, descriptors, ledgerClient, ledgerState }: {
|
|
100
|
-
psbt: Psbt;
|
|
101
|
-
descriptors: DescriptorInstance[];
|
|
102
|
-
ledgerClient: unknown;
|
|
103
|
-
ledgerState: LedgerState;
|
|
104
|
-
}): Promise<void>;
|
package/dist/signers.js
CHANGED
|
@@ -8,8 +8,7 @@ exports.signInputBIP32 = signInputBIP32;
|
|
|
8
8
|
exports.signBIP32 = signBIP32;
|
|
9
9
|
exports.signInputLedger = signInputLedger;
|
|
10
10
|
exports.signLedger = signLedger;
|
|
11
|
-
const
|
|
12
|
-
const bip341_1 = require("bitcoinjs-lib/src/payments/bip341");
|
|
11
|
+
const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
|
|
13
12
|
const ledger_1 = require("./ledger");
|
|
14
13
|
const applyPR2137_1 = require("./applyPR2137");
|
|
15
14
|
function range(n) {
|
|
@@ -39,12 +38,12 @@ function signInputECPair({ psbt, index, ecpair }) {
|
|
|
39
38
|
const input = psbt.data.inputs[index];
|
|
40
39
|
if (!input)
|
|
41
40
|
throw new Error('Invalid index');
|
|
42
|
-
if ((0,
|
|
41
|
+
if ((0, bitcoinjs_lib_internals_1.isTaprootInput)(input)) {
|
|
43
42
|
// If script-path (tapLeafScript present) -> DO NOT TWEAK
|
|
44
43
|
if (input.tapLeafScript && input.tapLeafScript.length > 0)
|
|
45
44
|
psbt.signInput(index, ecpair);
|
|
46
45
|
else {
|
|
47
|
-
const hash = (0,
|
|
46
|
+
const hash = (0, bitcoinjs_lib_internals_1.tapTweakHash)(ecpair.publicKey.slice(1, 33), undefined);
|
|
48
47
|
const tweakedEcpair = ecpair.tweak(hash);
|
|
49
48
|
psbt.signInput(index, tweakedEcpair);
|
|
50
49
|
}
|
|
@@ -101,136 +100,91 @@ function signBIP32({ psbt, masterNode }) {
|
|
|
101
100
|
}
|
|
102
101
|
const ledgerSignaturesForInputIndex = (index, ledgerSignatures) => ledgerSignatures
|
|
103
102
|
.filter(([i]) => i === index)
|
|
104
|
-
.map(([_i, partialSignature]) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
throw new Error(`Error: pass a valid ledgerClient`);
|
|
130
|
-
let ledgerSignatures;
|
|
131
|
-
if (ledgerManager) {
|
|
132
|
-
if (psbt.data.inputs[index]?.tapInternalKey)
|
|
133
|
-
throw new Error('Taproot inputs not yet supported for the Ledger device');
|
|
134
|
-
const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
|
|
135
|
-
psbt,
|
|
136
|
-
index,
|
|
137
|
-
ledgerManager
|
|
138
|
-
});
|
|
139
|
-
if (!policy)
|
|
140
|
-
throw new Error(`Error: the ledger cannot sign this pstb input`);
|
|
141
|
-
if (policy.policyName && policy.policyHmac && policy.policyId) {
|
|
142
|
-
//non-standard policy
|
|
143
|
-
const walletPolicy = new WalletPolicy(policy.policyName, policy.ledgerTemplate, policy.keyRoots);
|
|
144
|
-
ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
|
|
103
|
+
.map(([_i, partialSignature]) => partialSignature);
|
|
104
|
+
function addLedgerSignaturesToInput({ psbt, index, ledgerSignatures }) {
|
|
105
|
+
const input = psbt.data.inputs[index];
|
|
106
|
+
if (!input)
|
|
107
|
+
throw new Error(`Error: input ${index} not available`);
|
|
108
|
+
const signatures = ledgerSignaturesForInputIndex(index, ledgerSignatures);
|
|
109
|
+
if (signatures.length === 0)
|
|
110
|
+
throw new Error(`Error: no ledger signatures found for input ${index}`);
|
|
111
|
+
if ((0, bitcoinjs_lib_internals_1.isTaprootInput)(input)) {
|
|
112
|
+
// Ledger returns per-input signatures as [pubkey, signature, tapleafHash?].
|
|
113
|
+
// For taproot we must map them to PSBT taproot fields (not partialSig):
|
|
114
|
+
// - signatures with tapleafHash -> tapScriptSig[] (script-path)
|
|
115
|
+
// - signature without tapleafHash -> tapKeySig (key-path)
|
|
116
|
+
// A taproot input may contain script-path signatures, key-path signature,
|
|
117
|
+
// or both in edge cases; each must be written to its corresponding field.
|
|
118
|
+
const tapScriptSig = signatures
|
|
119
|
+
.filter((sig) => sig.tapleafHash)
|
|
120
|
+
.map((sig) => ({
|
|
121
|
+
pubkey: sig.pubkey,
|
|
122
|
+
signature: sig.signature,
|
|
123
|
+
leafHash: sig.tapleafHash
|
|
124
|
+
}));
|
|
125
|
+
const tapKeySigs = signatures.filter((sig) => !sig.tapleafHash);
|
|
126
|
+
if (tapScriptSig.length > 0) {
|
|
127
|
+
psbt.updateInput(index, { tapScriptSig });
|
|
145
128
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
129
|
+
if (tapKeySigs.length > 1)
|
|
130
|
+
throw new Error(`Error: expected at most one tapKeySig for input ${index}`);
|
|
131
|
+
const tapKeySig = tapKeySigs[0]?.signature;
|
|
132
|
+
if (tapKeySig) {
|
|
133
|
+
psbt.updateInput(index, { tapKeySig });
|
|
149
134
|
}
|
|
135
|
+
if (tapScriptSig.length === 0 && !tapKeySig)
|
|
136
|
+
throw new Error(`Error: no valid taproot ledger signatures found for input ${index}`);
|
|
150
137
|
}
|
|
151
138
|
else {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
ledgerState
|
|
158
|
-
});
|
|
159
|
-
if (!result)
|
|
160
|
-
throw new Error(`Error: output does not have a ledger input`);
|
|
161
|
-
const { ledgerTemplate, keyRoots } = result;
|
|
162
|
-
const standardPolicy = await (0, ledger_1.ledgerPolicyFromStandard)({
|
|
163
|
-
output,
|
|
164
|
-
ledgerClient,
|
|
165
|
-
ledgerState
|
|
166
|
-
});
|
|
167
|
-
if (standardPolicy) {
|
|
168
|
-
ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
const policy = await (0, ledger_1.ledgerPolicyFromState)({
|
|
172
|
-
output,
|
|
173
|
-
ledgerClient,
|
|
174
|
-
ledgerState
|
|
175
|
-
});
|
|
176
|
-
if (!policy || !policy.policyName || !policy.policyHmac)
|
|
177
|
-
throw new Error(`Error: the descriptor's policy is not registered`);
|
|
178
|
-
const walletPolicy = new WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
|
|
179
|
-
ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
|
|
180
|
-
}
|
|
139
|
+
const partialSig = signatures.map((sig) => ({
|
|
140
|
+
pubkey: sig.pubkey,
|
|
141
|
+
signature: sig.signature
|
|
142
|
+
}));
|
|
143
|
+
psbt.updateInput(index, { partialSig });
|
|
181
144
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
145
|
+
}
|
|
146
|
+
async function signInputLedger({ psbt, index, ledgerManager }) {
|
|
147
|
+
const { ledgerClient } = ledgerManager;
|
|
148
|
+
const { DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
|
|
149
|
+
if (!(ledgerClient instanceof AppClient))
|
|
150
|
+
throw new Error(`Error: pass a valid ledgerClient`);
|
|
151
|
+
const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
|
|
152
|
+
psbt,
|
|
153
|
+
index,
|
|
154
|
+
ledgerManager
|
|
185
155
|
});
|
|
156
|
+
if (!policy)
|
|
157
|
+
throw new Error(`Error: the ledger cannot sign this pstb input`);
|
|
158
|
+
let ledgerSignatures;
|
|
159
|
+
if (policy.policyName && policy.policyHmac && policy.policyId) {
|
|
160
|
+
//non-standard policy
|
|
161
|
+
const walletPolicy = new WalletPolicy(policy.policyName, policy.ledgerTemplate, policy.keyRoots);
|
|
162
|
+
const walletHmac = policy.policyHmac;
|
|
163
|
+
ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), walletPolicy, walletHmac);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
//standard policy
|
|
167
|
+
ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), new DefaultWalletPolicy(policy.ledgerTemplate, policy.keyRoots[0]), null);
|
|
168
|
+
}
|
|
169
|
+
addLedgerSignaturesToInput({ psbt, index, ledgerSignatures });
|
|
186
170
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* @overload
|
|
191
|
-
*/
|
|
192
|
-
async function signLedger({ psbt, descriptors, ledgerClient, ledgerState, ledgerManager }) {
|
|
193
|
-
if (!descriptors && !ledgerManager)
|
|
194
|
-
throw new Error(`ledgerManager not provided`);
|
|
195
|
-
if (descriptors && ledgerManager)
|
|
196
|
-
throw new Error(`Invalid usage: don't pass descriptors`);
|
|
197
|
-
if (ledgerManager && (ledgerClient || ledgerState))
|
|
198
|
-
throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
|
|
199
|
-
const outputs = descriptors;
|
|
200
|
-
if (ledgerManager)
|
|
201
|
-
({ ledgerClient, ledgerState } = ledgerManager);
|
|
202
|
-
if (!ledgerClient)
|
|
203
|
-
throw new Error(`ledgerManager not provided`);
|
|
204
|
-
if (!ledgerState)
|
|
205
|
-
throw new Error(`ledgerManager not provided`);
|
|
206
|
-
const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
|
|
171
|
+
async function signLedger({ psbt, ledgerManager }) {
|
|
172
|
+
const { ledgerClient } = ledgerManager;
|
|
173
|
+
const { DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
|
|
207
174
|
if (!(ledgerClient instanceof AppClient))
|
|
208
175
|
throw new Error(`Error: pass a valid ledgerClient`);
|
|
209
176
|
const ledgerPolicies = [];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
219
|
-
if (policy)
|
|
220
|
-
ledgerPolicies.push(policy);
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
if (!outputs)
|
|
224
|
-
throw new Error(`outputs not provided`);
|
|
225
|
-
for (const output of outputs) {
|
|
226
|
-
const policy = (await (0, ledger_1.ledgerPolicyFromState)({ output, ledgerClient, ledgerState })) ||
|
|
227
|
-
(await (0, ledger_1.ledgerPolicyFromStandard)({ output, ledgerClient, ledgerState }));
|
|
228
|
-
if (policy)
|
|
229
|
-
ledgerPolicies.push(policy);
|
|
230
|
-
}
|
|
231
|
-
if (ledgerPolicies.length === 0)
|
|
232
|
-
throw new Error(`Error: there are no inputs which could be signed`);
|
|
177
|
+
for (let index = 0; index < psbt.data.inputs.length; index++) {
|
|
178
|
+
const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
|
|
179
|
+
psbt,
|
|
180
|
+
index,
|
|
181
|
+
ledgerManager
|
|
182
|
+
});
|
|
183
|
+
if (policy)
|
|
184
|
+
ledgerPolicies.push(policy);
|
|
233
185
|
}
|
|
186
|
+
if (ledgerPolicies.length === 0)
|
|
187
|
+
throw new Error(`Error: there are no inputs which could be signed`);
|
|
234
188
|
//cluster unique LedgerPolicies
|
|
235
189
|
const uniquePolicies = [];
|
|
236
190
|
for (const policy of ledgerPolicies) {
|
|
@@ -244,16 +198,18 @@ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState, ledger
|
|
|
244
198
|
uniquePolicy.policyId) {
|
|
245
199
|
//non-standard policy
|
|
246
200
|
const walletPolicy = new WalletPolicy(uniquePolicy.policyName, uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots);
|
|
247
|
-
|
|
201
|
+
const walletHmac = uniquePolicy.policyHmac;
|
|
202
|
+
ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), walletPolicy, walletHmac);
|
|
248
203
|
}
|
|
249
204
|
else {
|
|
250
205
|
//standard policy
|
|
251
|
-
ledgerSignatures = await ledgerClient.signPsbt(
|
|
206
|
+
ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), new DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
|
|
252
207
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
208
|
+
const signedIndexes = [
|
|
209
|
+
...new Set(ledgerSignatures.map(([index]) => index))
|
|
210
|
+
];
|
|
211
|
+
for (const index of signedIndexes) {
|
|
212
|
+
addLedgerSignaturesToInput({ psbt, index, ledgerSignatures });
|
|
257
213
|
}
|
|
258
214
|
}
|
|
259
215
|
}
|