@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
package/dist/miniscript.js
CHANGED
|
@@ -43,6 +43,7 @@ const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
|
43
43
|
const keyExpressions_1 = require("./keyExpressions");
|
|
44
44
|
const RE = __importStar(require("./re"));
|
|
45
45
|
const miniscript_1 = require("@bitcoinerlab/miniscript");
|
|
46
|
+
const uint8array_tools_1 = require("uint8array-tools");
|
|
46
47
|
/**
|
|
47
48
|
* Expand a miniscript to a generalized form using variables instead of key
|
|
48
49
|
* expressions. Variables will be of this form: @0, @1, ...
|
|
@@ -51,24 +52,55 @@ const miniscript_1 = require("@bitcoinerlab/miniscript");
|
|
|
51
52
|
* Also compute pubkeys from descriptors to use them later.
|
|
52
53
|
*/
|
|
53
54
|
function expandMiniscript({ miniscript, isSegwit, isTaproot, network = bitcoinjs_lib_1.networks.bitcoin, ECPair, BIP32 }) {
|
|
54
|
-
if (isTaproot)
|
|
55
|
-
throw new Error('Taproot miniscript not yet supported.');
|
|
56
55
|
const reKeyExp = isTaproot
|
|
57
56
|
? RE.reTaprootKeyExp
|
|
58
57
|
: isSegwit
|
|
59
58
|
? RE.reSegwitKeyExp
|
|
60
59
|
: RE.reNonSegwitKeyExp;
|
|
61
60
|
const expansionMap = {};
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
let keyIndex = 0;
|
|
62
|
+
const keyExpressionRegex = new RegExp(String.raw `^${reKeyExp}$`);
|
|
63
|
+
const replaceKeyExpression = (keyExpression) => {
|
|
64
|
+
const trimmed = keyExpression.trim();
|
|
65
|
+
if (!trimmed)
|
|
66
|
+
throw new Error(`Error: expected a keyExpression but got ${keyExpression}`);
|
|
67
|
+
if (!keyExpressionRegex.test(trimmed))
|
|
68
|
+
throw new Error(`Error: expected a keyExpression but got ${trimmed}`);
|
|
69
|
+
const key = `@${keyIndex}`;
|
|
70
|
+
keyIndex += 1;
|
|
64
71
|
expansionMap[key] = (0, keyExpressions_1.parseKeyExpression)({
|
|
65
|
-
keyExpression,
|
|
72
|
+
keyExpression: trimmed,
|
|
66
73
|
isSegwit,
|
|
74
|
+
isTaproot,
|
|
67
75
|
network,
|
|
68
76
|
ECPair,
|
|
69
77
|
BIP32
|
|
70
78
|
});
|
|
71
79
|
return key;
|
|
80
|
+
};
|
|
81
|
+
// These are the Miniscript fragments where key arguments are expected.
|
|
82
|
+
// We only replace key expressions inside these fragments to avoid confusing
|
|
83
|
+
// hash preimages (e.g. sha256(03...)) with keys.
|
|
84
|
+
//
|
|
85
|
+
// Note: sorted script expressions (`sortedmulti`, `sortedmulti_a`) are
|
|
86
|
+
// intentionally excluded here. They are descriptor-level expressions (not
|
|
87
|
+
// Miniscript fragments) and are handled in descriptor/tap-tree code paths
|
|
88
|
+
// before Miniscript compilation.
|
|
89
|
+
const keyBearingFragmentRegex = /\b(pk|pkh|multi_a|multi)\(([^()]*)\)/g;
|
|
90
|
+
const expandedMiniscript = miniscript.replace(keyBearingFragmentRegex, (_, name, inner) => {
|
|
91
|
+
if (name === 'pk' || name === 'pkh')
|
|
92
|
+
return `${name}(${replaceKeyExpression(inner)})`;
|
|
93
|
+
//now do *multi* which has arguments:
|
|
94
|
+
const parts = inner.split(',').map(part => part.trim());
|
|
95
|
+
if (parts.length < 2)
|
|
96
|
+
throw new Error(`Error: invalid miniscript ${miniscript} (missing keys)`);
|
|
97
|
+
const k = parts[0] ?? '';
|
|
98
|
+
if (!k)
|
|
99
|
+
throw new Error(`Error: invalid miniscript ${miniscript} (missing threshold)`);
|
|
100
|
+
const replacedKeys = parts
|
|
101
|
+
.slice(1)
|
|
102
|
+
.map(keyExpression => replaceKeyExpression(keyExpression));
|
|
103
|
+
return `${name}(${[k, ...replacedKeys].join(',')})`;
|
|
72
104
|
});
|
|
73
105
|
//Do some assertions. Miniscript must not have duplicate keys, also all
|
|
74
106
|
//keyExpressions must produce a valid pubkey (unless it's ranged and we want
|
|
@@ -78,7 +110,7 @@ function expandMiniscript({ miniscript, isSegwit, isTaproot, network = bitcoinjs
|
|
|
78
110
|
.map(keyInfo => {
|
|
79
111
|
if (!keyInfo.pubkey)
|
|
80
112
|
throw new Error(`Error: keyExpression ${keyInfo.keyExpression} does not have a pubkey`);
|
|
81
|
-
return keyInfo.pubkey
|
|
113
|
+
return (0, uint8array_tools_1.toHex)(keyInfo.pubkey);
|
|
82
114
|
});
|
|
83
115
|
if (new Set(pubkeysHex).size !== pubkeysHex.length) {
|
|
84
116
|
throw new Error(`Error: miniscript ${miniscript} is not sane: contains duplicate public keys.`);
|
|
@@ -100,8 +132,8 @@ function substituteAsm({ expandedAsm, expansionMap }) {
|
|
|
100
132
|
throw new Error(`Error: invalid expansionMap for ${key}`);
|
|
101
133
|
}
|
|
102
134
|
return accAsm
|
|
103
|
-
.replaceAll(`<${key}>`, `<${
|
|
104
|
-
.replaceAll(`<HASH160(${key})>`, `<${bitcoinjs_lib_1.crypto.hash160(pubkey)
|
|
135
|
+
.replaceAll(`<${key}>`, `<${(0, uint8array_tools_1.toHex)(pubkey)}>`)
|
|
136
|
+
.replaceAll(`<HASH160(${key})>`, `<${(0, uint8array_tools_1.toHex)(bitcoinjs_lib_1.crypto.hash160(pubkey))}>`);
|
|
105
137
|
}, expandedAsm);
|
|
106
138
|
//Now clean it and prepare it so that fromASM can be called:
|
|
107
139
|
asm = asm
|
|
@@ -119,8 +151,8 @@ function substituteAsm({ expandedAsm, expansionMap }) {
|
|
|
119
151
|
.replace(/[<>]/g, '');
|
|
120
152
|
return asm;
|
|
121
153
|
}
|
|
122
|
-
function miniscript2Script({ expandedMiniscript, expansionMap }) {
|
|
123
|
-
const compiled = (0, miniscript_1.compileMiniscript)(expandedMiniscript);
|
|
154
|
+
function miniscript2Script({ expandedMiniscript, expansionMap, tapscript = false }) {
|
|
155
|
+
const compiled = (0, miniscript_1.compileMiniscript)(expandedMiniscript, { tapscript });
|
|
124
156
|
if (compiled.issane !== true) {
|
|
125
157
|
throw new Error(`Error: Miniscript ${expandedMiniscript} is not sane`);
|
|
126
158
|
}
|
|
@@ -137,12 +169,24 @@ function miniscript2Script({ expandedMiniscript, expansionMap }) {
|
|
|
137
169
|
* Pass timeConstraints to search for the first solution with this nLockTime and
|
|
138
170
|
* nSequence. Throw if no solution is possible using these constraints.
|
|
139
171
|
*
|
|
172
|
+
* Time constraints are used to keep the chosen satisfaction stable between the
|
|
173
|
+
* planning pass (fake signatures) and the signing pass (real signatures).
|
|
174
|
+
* We run the satisfier once with fake signatures to discover the implied
|
|
175
|
+
* nLockTime/nSequence without requiring user signatures. If real signatures
|
|
176
|
+
* had the same length, the satisfier would typically pick the same
|
|
177
|
+
* minimal-weight solution again. But ECDSA signature sizes can vary (71–73
|
|
178
|
+
* bytes), which may change which solution is considered "smallest".
|
|
179
|
+
*
|
|
180
|
+
* Passing the previously derived timeConstraints in the second pass forces the
|
|
181
|
+
* same solution to be selected, ensuring locktime/sequence do not change
|
|
182
|
+
* between planning and finalization.
|
|
183
|
+
*
|
|
140
184
|
* Don't pass timeConstraints (this is the default) if you want to get the
|
|
141
185
|
* smallest size solution altogether.
|
|
142
186
|
*
|
|
143
187
|
* If a solution is not found this function throws.
|
|
144
188
|
*/
|
|
145
|
-
function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [], preimages = [], timeConstraints }) {
|
|
189
|
+
function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [], preimages = [], timeConstraints, tapscript = false }) {
|
|
146
190
|
//convert 'sha256(6c...33)' to: { ['<sha256_preimage(6c...33)>']: '10...5f'}
|
|
147
191
|
const preimageMap = {};
|
|
148
192
|
preimages.forEach(preimage => {
|
|
@@ -153,15 +197,18 @@ function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [],
|
|
|
153
197
|
//get the keyExpressions: @0, @1 from the keys in expansionMap
|
|
154
198
|
const expandedSignatureMap = {};
|
|
155
199
|
signatures.forEach(signature => {
|
|
156
|
-
const pubkeyHex = signature.pubkey
|
|
157
|
-
const keyExpression = Object.keys(expansionMap).find(k => expansionMap[k]?.pubkey
|
|
200
|
+
const pubkeyHex = (0, uint8array_tools_1.toHex)(signature.pubkey);
|
|
201
|
+
const keyExpression = Object.keys(expansionMap).find(k => expansionMap[k]?.pubkey && (0, uint8array_tools_1.toHex)(expansionMap[k].pubkey) === pubkeyHex);
|
|
158
202
|
expandedSignatureMap['<sig(' + keyExpression + ')>'] =
|
|
159
|
-
'<' + signature.signature
|
|
203
|
+
'<' + (0, uint8array_tools_1.toHex)(signature.signature) + '>';
|
|
160
204
|
});
|
|
161
205
|
const expandedKnownsMap = { ...preimageMap, ...expandedSignatureMap };
|
|
162
206
|
const knowns = Object.keys(expandedKnownsMap);
|
|
163
207
|
//satisfier verifies again internally whether expandedKnownsMap with given knowns is sane
|
|
164
|
-
const { nonMalleableSats } = (0, miniscript_1.satisfier)(expandedMiniscript, {
|
|
208
|
+
const { nonMalleableSats } = (0, miniscript_1.satisfier)(expandedMiniscript, {
|
|
209
|
+
knowns,
|
|
210
|
+
tapscript
|
|
211
|
+
});
|
|
165
212
|
if (!Array.isArray(nonMalleableSats) || !nonMalleableSats[0])
|
|
166
213
|
throw new Error(`Error: unresolvable miniscript ${expandedMiniscript}`);
|
|
167
214
|
let sat;
|
|
@@ -218,7 +265,7 @@ function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [],
|
|
|
218
265
|
* However, the `0` number is an edge case that we specially handle with this
|
|
219
266
|
* function.
|
|
220
267
|
*
|
|
221
|
-
* bitcoinjs-lib's `bscript.number.encode(0)` produces an empty
|
|
268
|
+
* bitcoinjs-lib's `bscript.number.encode(0)` produces an empty array.
|
|
222
269
|
* This is what the Bitcoin interpreter does and it is what `script.number.encode` was
|
|
223
270
|
* implemented to do.
|
|
224
271
|
*
|
|
@@ -254,5 +301,5 @@ function numberEncodeAsm(number) {
|
|
|
254
301
|
return 'OP_0';
|
|
255
302
|
}
|
|
256
303
|
else
|
|
257
|
-
return bitcoinjs_lib_1.script.number.encode(number)
|
|
304
|
+
return (0, uint8array_tools_1.toHex)(bitcoinjs_lib_1.script.number.encode(number));
|
|
258
305
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves all multipath tuple segments (for example `/<0;1>/*`) in lockstep
|
|
3
|
+
* using the provided `change` value.
|
|
4
|
+
*
|
|
5
|
+
* - `/**` is first canonicalized to `/<0;1>/*`.
|
|
6
|
+
* - All tuples in the descriptor must have the same cardinality.
|
|
7
|
+
* - Tuple values must be strictly increasing decimal numbers.
|
|
8
|
+
* - `change` must match one of the values in each tuple.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveMultipathDescriptor({ descriptor, change }: {
|
|
11
|
+
descriptor: string;
|
|
12
|
+
change?: number;
|
|
13
|
+
}): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveMultipathDescriptor = resolveMultipathDescriptor;
|
|
4
|
+
/**
|
|
5
|
+
* Replaces the receive/change shorthand `/**` with its canonical multipath
|
|
6
|
+
* representation `/<0;1>/*`.
|
|
7
|
+
*/
|
|
8
|
+
function expandReceiveChangeShorthand(descriptor) {
|
|
9
|
+
return descriptor.replace(/\/\*\*/g, '/<0;1>/*');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parses a multipath tuple body from `<...>`.
|
|
13
|
+
*
|
|
14
|
+
* This implementation intentionally accepts only decimal numbers (no hardened
|
|
15
|
+
* suffixes) and enforces strict left-to-right increase as a safety feature to
|
|
16
|
+
* catch likely human errors such as `<1;0>`.
|
|
17
|
+
*/
|
|
18
|
+
function parseMultipathTuple(tupleBody) {
|
|
19
|
+
const parts = tupleBody.split(';');
|
|
20
|
+
if (parts.length < 2)
|
|
21
|
+
throw new Error(`Error: multipath tuple must contain at least 2 values, got <${tupleBody}>`);
|
|
22
|
+
const values = parts.map(part => {
|
|
23
|
+
if (!/^(0|[1-9]\d*)$/.test(part))
|
|
24
|
+
throw new Error(`Error: multipath tuple values must be decimal numbers, got <${tupleBody}>`);
|
|
25
|
+
const value = Number(part);
|
|
26
|
+
if (!Number.isSafeInteger(value))
|
|
27
|
+
throw new Error(`Error: multipath tuple value overflow, got <${tupleBody}>`);
|
|
28
|
+
return value;
|
|
29
|
+
});
|
|
30
|
+
for (let i = 1; i < values.length; i++) {
|
|
31
|
+
const prev = values[i - 1];
|
|
32
|
+
const current = values[i];
|
|
33
|
+
if (prev === undefined || current === undefined)
|
|
34
|
+
throw new Error(`Error: invalid multipath tuple <${tupleBody}>`);
|
|
35
|
+
if (current <= prev)
|
|
36
|
+
throw new Error(`Error: multipath tuple values must be strictly increasing from left to right, got <${tupleBody}>`);
|
|
37
|
+
}
|
|
38
|
+
return values;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolves all multipath tuple segments (for example `/<0;1>/*`) in lockstep
|
|
42
|
+
* using the provided `change` value.
|
|
43
|
+
*
|
|
44
|
+
* - `/**` is first canonicalized to `/<0;1>/*`.
|
|
45
|
+
* - All tuples in the descriptor must have the same cardinality.
|
|
46
|
+
* - Tuple values must be strictly increasing decimal numbers.
|
|
47
|
+
* - `change` must match one of the values in each tuple.
|
|
48
|
+
*/
|
|
49
|
+
function resolveMultipathDescriptor({ descriptor, change }) {
|
|
50
|
+
const canonicalDescriptor = expandReceiveChangeShorthand(descriptor);
|
|
51
|
+
const tupleMatches = Array.from(canonicalDescriptor.matchAll(/\/<([^<>]+)>/g), match => ({
|
|
52
|
+
token: match[0],
|
|
53
|
+
tupleBody: match[1],
|
|
54
|
+
values: parseMultipathTuple(match[1])
|
|
55
|
+
}));
|
|
56
|
+
if (tupleMatches.length === 0)
|
|
57
|
+
return canonicalDescriptor;
|
|
58
|
+
if (change === undefined)
|
|
59
|
+
throw new Error(`Error: change was not provided for multipath descriptor`);
|
|
60
|
+
if (!Number.isInteger(change) || change < 0)
|
|
61
|
+
throw new Error(`Error: invalid change ${change}`);
|
|
62
|
+
const tupleSize = tupleMatches[0]?.values.length;
|
|
63
|
+
if (!tupleSize)
|
|
64
|
+
throw new Error(`Error: invalid multipath tuple`);
|
|
65
|
+
for (const tupleMatch of tupleMatches) {
|
|
66
|
+
if (tupleMatch.values.length !== tupleSize)
|
|
67
|
+
throw new Error(`Error: all multipath tuples must have the same number of options`);
|
|
68
|
+
}
|
|
69
|
+
let resolvedDescriptor = canonicalDescriptor;
|
|
70
|
+
for (const tupleMatch of tupleMatches) {
|
|
71
|
+
if (!tupleMatch.values.includes(change))
|
|
72
|
+
throw new Error(`Error: change ${change} not found in multipath tuple <${tupleMatch.tupleBody}>`);
|
|
73
|
+
resolvedDescriptor = resolvedDescriptor.replaceAll(tupleMatch.token, `/${change}`);
|
|
74
|
+
}
|
|
75
|
+
return resolvedDescriptor;
|
|
76
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isBitcoinMainnet = isBitcoinMainnet;
|
|
4
|
+
exports.coinTypeFromNetwork = coinTypeFromNetwork;
|
|
5
|
+
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
6
|
+
function isBitcoinMainnet(network) {
|
|
7
|
+
return (network.bech32 === bitcoinjs_lib_1.networks.bitcoin.bech32 &&
|
|
8
|
+
network.bip32.public === bitcoinjs_lib_1.networks.bitcoin.bip32.public &&
|
|
9
|
+
network.bip32.private === bitcoinjs_lib_1.networks.bitcoin.bip32.private &&
|
|
10
|
+
network.pubKeyHash === bitcoinjs_lib_1.networks.bitcoin.pubKeyHash &&
|
|
11
|
+
network.scriptHash === bitcoinjs_lib_1.networks.bitcoin.scriptHash &&
|
|
12
|
+
network.wif === bitcoinjs_lib_1.networks.bitcoin.wif);
|
|
13
|
+
}
|
|
14
|
+
function coinTypeFromNetwork(network) {
|
|
15
|
+
return isBitcoinMainnet(network) ? 0 : 1;
|
|
16
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitTopLevelComma = splitTopLevelComma;
|
|
4
|
+
function splitTopLevelComma({ expression, onError }) {
|
|
5
|
+
let braceDepth = 0;
|
|
6
|
+
let parenDepth = 0;
|
|
7
|
+
let commaIndex = -1;
|
|
8
|
+
for (let i = 0; i < expression.length; i++) {
|
|
9
|
+
const char = expression[i];
|
|
10
|
+
if (!char)
|
|
11
|
+
continue;
|
|
12
|
+
if (char === '{') {
|
|
13
|
+
braceDepth++;
|
|
14
|
+
}
|
|
15
|
+
else if (char === '}') {
|
|
16
|
+
if (braceDepth === 0)
|
|
17
|
+
throw onError(expression);
|
|
18
|
+
braceDepth--;
|
|
19
|
+
}
|
|
20
|
+
else if (char === '(') {
|
|
21
|
+
//Track miniscript argument lists so we don't split on commas inside them.
|
|
22
|
+
parenDepth++;
|
|
23
|
+
}
|
|
24
|
+
else if (char === ')') {
|
|
25
|
+
if (parenDepth === 0)
|
|
26
|
+
throw onError(expression);
|
|
27
|
+
parenDepth--;
|
|
28
|
+
}
|
|
29
|
+
else if (char === ',') {
|
|
30
|
+
if (braceDepth === 0 && parenDepth === 0) {
|
|
31
|
+
if (commaIndex !== -1)
|
|
32
|
+
throw onError(expression);
|
|
33
|
+
commaIndex = i;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (braceDepth !== 0 || parenDepth !== 0)
|
|
38
|
+
throw onError(expression);
|
|
39
|
+
if (commaIndex === -1)
|
|
40
|
+
return null;
|
|
41
|
+
const left = expression.slice(0, commaIndex).trim();
|
|
42
|
+
const right = expression.slice(commaIndex + 1).trim();
|
|
43
|
+
if (!left || !right)
|
|
44
|
+
throw onError(expression);
|
|
45
|
+
return { left, right };
|
|
46
|
+
}
|
package/dist/psbt.d.ts
CHANGED
|
@@ -1,40 +1,44 @@
|
|
|
1
|
-
import type { PsbtInput } from 'bip174
|
|
1
|
+
import type { PsbtInput, TapBip32Derivation, TapLeafScript } from 'bip174';
|
|
2
2
|
import type { KeyInfo } from './types';
|
|
3
3
|
import { Network, Psbt } from 'bitcoinjs-lib';
|
|
4
4
|
/**
|
|
5
5
|
* This function must do two things:
|
|
6
6
|
* 1. Check if the `input` can be finalized. If it can not be finalized, throw.
|
|
7
7
|
* ie. `Can not finalize input #${inputIndex}`
|
|
8
|
-
* 2. Create
|
|
8
|
+
* 2. Create finalScriptSig and finalScriptWitness.
|
|
9
9
|
*/
|
|
10
10
|
type FinalScriptsFunc = (inputIndex: number, // Which input is it?
|
|
11
11
|
input: PsbtInput, // The PSBT input contents
|
|
12
|
-
script:
|
|
12
|
+
script: Uint8Array, // The "meaningful" locking script (redeemScript for P2SH etc.)
|
|
13
13
|
isSegwit: boolean, // Is it segwit?
|
|
14
14
|
isP2SH: boolean, // Is it P2SH?
|
|
15
15
|
isP2WSH: boolean) => {
|
|
16
|
-
finalScriptSig:
|
|
17
|
-
finalScriptWitness:
|
|
16
|
+
finalScriptSig: Uint8Array | undefined;
|
|
17
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
18
18
|
};
|
|
19
|
-
export declare function finalScriptsFuncFactory(scriptSatisfaction:
|
|
19
|
+
export declare function finalScriptsFuncFactory(scriptSatisfaction: Uint8Array, network: Network): FinalScriptsFunc;
|
|
20
20
|
/**
|
|
21
|
-
* Important: Read comments on
|
|
21
|
+
* Important: Read comments on Output.updatePsbtAsInput regarding not passing txHex
|
|
22
22
|
*/
|
|
23
|
-
export declare function
|
|
23
|
+
export declare function addPsbtInput({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, tapLeafScript, tapBip32Derivation, witnessScript, redeemScript, rbf }: {
|
|
24
24
|
psbt: Psbt;
|
|
25
25
|
vout: number;
|
|
26
26
|
txHex?: string;
|
|
27
27
|
txId?: string;
|
|
28
|
-
value?:
|
|
28
|
+
value?: bigint;
|
|
29
29
|
sequence: number | undefined;
|
|
30
30
|
locktime: number | undefined;
|
|
31
31
|
keysInfo: KeyInfo[];
|
|
32
|
-
scriptPubKey:
|
|
32
|
+
scriptPubKey: Uint8Array;
|
|
33
33
|
isSegwit: boolean;
|
|
34
34
|
/** for taproot **/
|
|
35
|
-
tapInternalKey?:
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
tapInternalKey?: Uint8Array | undefined;
|
|
36
|
+
/** for taproot script-path **/
|
|
37
|
+
tapLeafScript?: TapLeafScript[] | undefined;
|
|
38
|
+
/** for taproot **/
|
|
39
|
+
tapBip32Derivation?: TapBip32Derivation[] | undefined;
|
|
40
|
+
witnessScript: Uint8Array | undefined;
|
|
41
|
+
redeemScript: Uint8Array | undefined;
|
|
38
42
|
rbf: boolean;
|
|
39
43
|
}): number;
|
|
40
44
|
export {};
|
package/dist/psbt.js
CHANGED
|
@@ -3,43 +3,23 @@
|
|
|
3
3
|
// Distributed under the MIT software license
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.finalScriptsFuncFactory = finalScriptsFuncFactory;
|
|
6
|
-
exports.
|
|
6
|
+
exports.addPsbtInput = addPsbtInput;
|
|
7
7
|
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const uint8array_tools_1 = require("uint8array-tools");
|
|
9
|
+
const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
|
|
10
|
+
function reverseBytes(buffer) {
|
|
10
11
|
if (buffer.length < 1)
|
|
11
12
|
return buffer;
|
|
12
|
-
|
|
13
|
+
const copy = Uint8Array.from(buffer);
|
|
14
|
+
let j = copy.length - 1;
|
|
13
15
|
let tmp = 0;
|
|
14
|
-
for (let i = 0; i <
|
|
15
|
-
tmp =
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
for (let i = 0; i < copy.length / 2; i++) {
|
|
17
|
+
tmp = copy[i];
|
|
18
|
+
copy[i] = copy[j];
|
|
19
|
+
copy[j] = tmp;
|
|
18
20
|
j--;
|
|
19
21
|
}
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
function witnessStackToScriptWitness(witness) {
|
|
23
|
-
let buffer = Buffer.allocUnsafe(0);
|
|
24
|
-
function writeSlice(slice) {
|
|
25
|
-
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
26
|
-
}
|
|
27
|
-
function writeVarInt(i) {
|
|
28
|
-
const currentLen = buffer.length;
|
|
29
|
-
const varintLen = (0, varuint_bitcoin_1.encodingLength)(i);
|
|
30
|
-
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
31
|
-
(0, varuint_bitcoin_1.encode)(i, buffer, currentLen);
|
|
32
|
-
}
|
|
33
|
-
function writeVarSlice(slice) {
|
|
34
|
-
writeVarInt(slice.length);
|
|
35
|
-
writeSlice(slice);
|
|
36
|
-
}
|
|
37
|
-
function writeVector(vector) {
|
|
38
|
-
writeVarInt(vector.length);
|
|
39
|
-
vector.forEach(writeVarSlice);
|
|
40
|
-
}
|
|
41
|
-
writeVector(witness);
|
|
42
|
-
return buffer;
|
|
22
|
+
return copy;
|
|
43
23
|
}
|
|
44
24
|
function finalScriptsFuncFactory(scriptSatisfaction, network) {
|
|
45
25
|
const finalScriptsFunc = (_index, _input, lockingScript /*witnessScript or redeemScript*/, isSegwit, isP2SH, _isP2WSH) => {
|
|
@@ -53,7 +33,7 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
|
|
|
53
33
|
});
|
|
54
34
|
if (!payment.witness)
|
|
55
35
|
throw new Error(`Error: p2wsh failed producing a witness`);
|
|
56
|
-
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
36
|
+
finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(payment.witness);
|
|
57
37
|
}
|
|
58
38
|
//p2sh-p2wsh
|
|
59
39
|
else if (isSegwit && isP2SH) {
|
|
@@ -66,7 +46,7 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
|
|
|
66
46
|
});
|
|
67
47
|
if (!payment.witness)
|
|
68
48
|
throw new Error(`Error: p2sh-p2wsh failed producing a witness`);
|
|
69
|
-
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
49
|
+
finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(payment.witness);
|
|
70
50
|
finalScriptSig = payment.input;
|
|
71
51
|
}
|
|
72
52
|
//p2sh
|
|
@@ -84,9 +64,14 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
|
|
|
84
64
|
return finalScriptsFunc;
|
|
85
65
|
}
|
|
86
66
|
/**
|
|
87
|
-
* Important: Read comments on
|
|
67
|
+
* Important: Read comments on Output.updatePsbtAsInput regarding not passing txHex
|
|
88
68
|
*/
|
|
89
|
-
function
|
|
69
|
+
function addPsbtInput({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, tapLeafScript, tapBip32Derivation, witnessScript, redeemScript, rbf }) {
|
|
70
|
+
if (value !== undefined && typeof value !== 'bigint')
|
|
71
|
+
throw new Error(`Error: value must be a bigint`);
|
|
72
|
+
if (value !== undefined && value < 0n)
|
|
73
|
+
throw new Error(`Error: value must be >= 0n`);
|
|
74
|
+
let normalizedValue = value;
|
|
90
75
|
//Some data-sanity checks:
|
|
91
76
|
if (sequence !== undefined && rbf && sequence > 0xfffffffd)
|
|
92
77
|
throw new Error(`Error: incompatible sequence and rbf settings`);
|
|
@@ -104,7 +89,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
104
89
|
const outputScript = out.script;
|
|
105
90
|
if (!outputScript)
|
|
106
91
|
throw new Error(`Error: could not extract outputScript for txHex ${txHex} and vout ${vout}`);
|
|
107
|
-
if (
|
|
92
|
+
if ((0, uint8array_tools_1.compare)(outputScript, scriptPubKey) !== 0)
|
|
108
93
|
throw new Error(`Error: txHex ${txHex} for vout ${vout} does not correspond to scriptPubKey ${scriptPubKey}`);
|
|
109
94
|
if (txId !== undefined) {
|
|
110
95
|
if (tx.getId() !== txId)
|
|
@@ -113,15 +98,15 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
113
98
|
else {
|
|
114
99
|
txId = tx.getId();
|
|
115
100
|
}
|
|
116
|
-
if (
|
|
117
|
-
if (out.value !==
|
|
101
|
+
if (normalizedValue !== undefined) {
|
|
102
|
+
if (out.value !== normalizedValue)
|
|
118
103
|
throw new Error(`Error: value for ${txHex} and vout ${vout} does not correspond to ${value}`);
|
|
119
104
|
}
|
|
120
105
|
else {
|
|
121
|
-
|
|
106
|
+
normalizedValue = out.value;
|
|
122
107
|
}
|
|
123
108
|
}
|
|
124
|
-
if (txId === undefined ||
|
|
109
|
+
if (txId === undefined || normalizedValue === undefined)
|
|
125
110
|
throw new Error(`Error: txHex+vout required. Alternatively, but ONLY for Segwit inputs, txId+value can also be passed.`);
|
|
126
111
|
if (locktime) {
|
|
127
112
|
if (psbt.locktime && psbt.locktime !== locktime)
|
|
@@ -149,7 +134,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
149
134
|
}
|
|
150
135
|
}
|
|
151
136
|
const input = {
|
|
152
|
-
hash:
|
|
137
|
+
hash: reverseBytes((0, uint8array_tools_1.fromHex)(txId)),
|
|
153
138
|
index: vout
|
|
154
139
|
};
|
|
155
140
|
if (txHex !== undefined) {
|
|
@@ -157,7 +142,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
157
142
|
}
|
|
158
143
|
if (tapInternalKey) {
|
|
159
144
|
//Taproot
|
|
160
|
-
const
|
|
145
|
+
const fallbackTapBip32Derivation = keysInfo
|
|
161
146
|
.filter((keyInfo) => keyInfo.pubkey && keyInfo.masterFingerprint && keyInfo.path)
|
|
162
147
|
.map((keyInfo) => {
|
|
163
148
|
const pubkey = keyInfo.pubkey;
|
|
@@ -167,16 +152,15 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
167
152
|
masterFingerprint: keyInfo.masterFingerprint,
|
|
168
153
|
pubkey,
|
|
169
154
|
path: keyInfo.path,
|
|
170
|
-
leafHashes: []
|
|
155
|
+
leafHashes: []
|
|
171
156
|
};
|
|
172
157
|
});
|
|
173
|
-
|
|
174
|
-
|
|
158
|
+
const resolvedTapBip32Derivation = tapBip32Derivation || fallbackTapBip32Derivation;
|
|
159
|
+
if (resolvedTapBip32Derivation.length)
|
|
160
|
+
input.tapBip32Derivation = resolvedTapBip32Derivation;
|
|
175
161
|
input.tapInternalKey = tapInternalKey;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (tapBip32Derivation.length > 1)
|
|
179
|
-
throw new Error('Only single key taproot inputs are currently supported');
|
|
162
|
+
if (tapLeafScript && tapLeafScript.length > 0)
|
|
163
|
+
input.tapLeafScript = tapLeafScript;
|
|
180
164
|
}
|
|
181
165
|
else {
|
|
182
166
|
const bip32Derivation = keysInfo
|
|
@@ -196,7 +180,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
196
180
|
}
|
|
197
181
|
if (isSegwit && txHex !== undefined) {
|
|
198
182
|
//There's no need to put both witnessUtxo and nonWitnessUtxo
|
|
199
|
-
input.witnessUtxo = { script: scriptPubKey, value };
|
|
183
|
+
input.witnessUtxo = { script: scriptPubKey, value: normalizedValue };
|
|
200
184
|
}
|
|
201
185
|
if (sequence !== undefined)
|
|
202
186
|
input.sequence = sequence;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Network } from 'bitcoinjs-lib';
|
|
2
|
+
export declare const MAX_SCRIPT_ELEMENT_SIZE = 520;
|
|
3
|
+
export declare const MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
|
4
|
+
export declare function assertScriptNonPushOnlyOpsLimit({ script }: {
|
|
5
|
+
script: Uint8Array;
|
|
6
|
+
}): void;
|
|
7
|
+
/**
|
|
8
|
+
* Enforces consensus stack resource limits.
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertConsensusStackResourceLimits({ stackItems, stackLabel, stackItemLabel }: {
|
|
11
|
+
stackItems: Uint8Array[];
|
|
12
|
+
stackLabel?: string;
|
|
13
|
+
stackItemLabel?: string;
|
|
14
|
+
}): void;
|
|
15
|
+
export declare function assertWitnessV0SatisfactionResourceLimits({ stackItems }: {
|
|
16
|
+
stackItems: Uint8Array[];
|
|
17
|
+
}): void;
|
|
18
|
+
export declare function assertTaprootScriptPathSatisfactionResourceLimits({ stackItems }: {
|
|
19
|
+
stackItems: Uint8Array[];
|
|
20
|
+
}): void;
|
|
21
|
+
export declare function assertP2shScriptSigStandardSize({ scriptSatisfaction, redeemScript, network }: {
|
|
22
|
+
scriptSatisfaction: Uint8Array;
|
|
23
|
+
redeemScript: Uint8Array;
|
|
24
|
+
network: Network;
|
|
25
|
+
}): void;
|