@btc-vision/bitcoin 6.3.5 → 6.4.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/.mocharc.json +13 -0
- package/browser/address.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +3 -3
- package/browser/networks.d.ts +1 -0
- package/browser/psbt/psbtutils.d.ts +1 -1
- package/build/address.d.ts +1 -1
- package/build/address.js +12 -5
- package/build/block.js +2 -2
- package/build/bufferutils.js +5 -5
- package/build/networks.d.ts +1 -0
- package/build/networks.js +11 -0
- package/build/psbt/psbtutils.js +2 -2
- package/build/psbt.js +3 -7
- package/package.json +26 -26
- package/src/address.ts +20 -6
- package/src/block.ts +233 -233
- package/src/bufferutils.ts +188 -180
- package/src/index.ts +86 -86
- package/src/networks.ts +12 -0
- package/src/psbt/bip371.ts +441 -441
- package/src/psbt/psbtutils.ts +4 -3
- package/src/psbt.ts +2187 -2187
- package/test/address.spec.ts +155 -177
- package/test/bitcoin.core.spec.ts +212 -234
- package/test/block.spec.ts +171 -194
- package/test/bufferutils.spec.ts +450 -513
- package/test/crypto.spec.ts +49 -55
- package/test/fixtures/address.json +3 -3
- package/test/integration/addresses.spec.ts +142 -154
- package/test/integration/bip32.spec.ts +130 -151
- package/test/integration/blocks.spec.ts +28 -28
- package/test/integration/cltv.spec.ts +241 -283
- package/test/integration/csv.spec.ts +452 -527
- package/test/integration/payments.spec.ts +110 -135
- package/test/integration/taproot.spec.ts +663 -707
- package/test/integration/transactions.spec.ts +668 -769
- package/test/payments.spec.ts +114 -125
- package/test/payments.utils.ts +165 -208
- package/test/psbt.spec.ts +1285 -1414
- package/test/script.spec.ts +186 -210
- package/test/script_number.spec.ts +26 -29
- package/test/script_signature.spec.ts +66 -66
- package/test/transaction.spec.ts +337 -387
- package/test/ts-node-register.js +7 -5
- package/test/tsconfig.json +4 -1
- package/test/types.spec.ts +53 -58
- package/.nyc_output/6368a5b2-daa5-4821-8ed0-b742d6fc7eab.json +0 -1
- package/.nyc_output/processinfo/6368a5b2-daa5-4821-8ed0-b742d6fc7eab.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/test/address.spec.js +0 -124
- package/test/bitcoin.core.spec.js +0 -170
- package/test/block.spec.js +0 -141
- package/test/bufferutils.spec.js +0 -427
- package/test/crypto.spec.js +0 -41
- package/test/integration/_regtest.js +0 -7
- package/test/integration/addresses.spec.js +0 -116
- package/test/integration/bip32.spec.js +0 -85
- package/test/integration/blocks.spec.js +0 -26
- package/test/integration/cltv.spec.js +0 -199
- package/test/integration/csv.spec.js +0 -362
- package/test/integration/payments.spec.js +0 -98
- package/test/integration/taproot.spec.js +0 -532
- package/test/integration/transactions.spec.js +0 -561
- package/test/payments.spec.js +0 -97
- package/test/payments.utils.js +0 -190
- package/test/psbt.spec.js +0 -1044
- package/test/script.spec.js +0 -151
- package/test/script_number.spec.js +0 -24
- package/test/script_signature.spec.js +0 -52
- package/test/transaction.spec.js +0 -269
- package/test/types.spec.js +0 -46
|
@@ -1,527 +1,452 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { PsbtInput } from 'bip174/src/lib/interfaces';
|
|
3
|
-
import ECPairFactory from 'ecpair';
|
|
4
|
-
import * as ecc from 'tiny-secp256k1';
|
|
5
|
-
import { before, describe, it } from 'mocha';
|
|
6
|
-
import * as bitcoin from '
|
|
7
|
-
import { regtestUtils } from './_regtest';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
redeem: {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
//
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
),
|
|
351
|
-
bitcoin.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
tx.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
): {
|
|
454
|
-
finalScriptSig: Buffer | undefined;
|
|
455
|
-
finalScriptWitness: Buffer | undefined;
|
|
456
|
-
} {
|
|
457
|
-
// Step 1: Check to make sure the meaningful script matches what you expect.
|
|
458
|
-
const decompiled = bitcoin.script.decompile(script);
|
|
459
|
-
// Checking if first OP is OP_IF... should do better check in production!
|
|
460
|
-
// You may even want to check the public keys in the script against a
|
|
461
|
-
// whitelist depending on the circumstances!!!
|
|
462
|
-
// You also want to check the contents of the input to see if you have enough
|
|
463
|
-
// info to actually construct the scriptSig and Witnesses.
|
|
464
|
-
if (!decompiled || decompiled[0] !== bitcoin.opcodes.OP_IF) {
|
|
465
|
-
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Step 2: Create final scripts
|
|
469
|
-
let payment: bitcoin.Payment = {
|
|
470
|
-
network: regtest,
|
|
471
|
-
output: script,
|
|
472
|
-
// This logic should be more strict and make sure the pubkeys in the
|
|
473
|
-
// meaningful script are the ones signing in the PSBT etc.
|
|
474
|
-
input: bitcoin.script.compile([
|
|
475
|
-
input.partialSig![0].signature,
|
|
476
|
-
bitcoin.opcodes.OP_TRUE,
|
|
477
|
-
]),
|
|
478
|
-
};
|
|
479
|
-
if (isP2WSH && isSegwit)
|
|
480
|
-
payment = bitcoin.payments.p2wsh({
|
|
481
|
-
network: regtest,
|
|
482
|
-
redeem: payment,
|
|
483
|
-
});
|
|
484
|
-
if (isP2SH)
|
|
485
|
-
payment = bitcoin.payments.p2sh({
|
|
486
|
-
network: regtest,
|
|
487
|
-
redeem: payment,
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
|
|
491
|
-
let buffer = Buffer.allocUnsafe(0);
|
|
492
|
-
|
|
493
|
-
function writeSlice(slice: Buffer): void {
|
|
494
|
-
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function writeVarInt(i: number): void {
|
|
498
|
-
const currentLen = buffer.length;
|
|
499
|
-
const varintLen = varuint.encodingLength(i);
|
|
500
|
-
|
|
501
|
-
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
502
|
-
varuint.encode(i, buffer, currentLen);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function writeVarSlice(slice: Buffer): void {
|
|
506
|
-
writeVarInt(slice.length);
|
|
507
|
-
writeSlice(slice);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
function writeVector(vector: Buffer[]): void {
|
|
511
|
-
writeVarInt(vector.length);
|
|
512
|
-
vector.forEach(writeVarSlice);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
writeVector(witness);
|
|
516
|
-
|
|
517
|
-
return buffer;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
return {
|
|
521
|
-
finalScriptSig: payment.input,
|
|
522
|
-
finalScriptWitness:
|
|
523
|
-
payment.witness && payment.witness.length > 0
|
|
524
|
-
? witnessStackToScriptWitness(payment.witness)
|
|
525
|
-
: undefined,
|
|
526
|
-
};
|
|
527
|
-
}
|
|
1
|
+
import assert from 'assert';
|
|
2
|
+
import { PsbtInput } from 'bip174/src/lib/interfaces.js';
|
|
3
|
+
import { ECPairFactory } from 'ecpair';
|
|
4
|
+
import * as ecc from 'tiny-secp256k1';
|
|
5
|
+
import { before, describe, it } from 'mocha';
|
|
6
|
+
import * as bitcoin from '../../src/index.js';
|
|
7
|
+
import { regtestUtils } from './_regtest.js';
|
|
8
|
+
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
import bip68 from 'bip68';
|
|
11
|
+
import * as varuint from 'varuint-bitcoin';
|
|
12
|
+
|
|
13
|
+
const ECPair = ECPairFactory(ecc);
|
|
14
|
+
const regtest = regtestUtils.network;
|
|
15
|
+
|
|
16
|
+
function toOutputScript(address: string): Buffer {
|
|
17
|
+
return bitcoin.address.toOutputScript(address, regtest);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function idToHash(txid: string): Buffer {
|
|
21
|
+
return Buffer.from(txid, 'hex').reverse();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const alice = ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest);
|
|
25
|
+
const bob = ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest);
|
|
26
|
+
const charles = ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest);
|
|
27
|
+
const dave = ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest);
|
|
28
|
+
|
|
29
|
+
describe('bitcoinjs-lib (transactions w/ CSV)', () => {
|
|
30
|
+
// force update MTP
|
|
31
|
+
before(async () => {
|
|
32
|
+
await regtestUtils.mine(11);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const hashType = bitcoin.Transaction.SIGHASH_ALL;
|
|
36
|
+
|
|
37
|
+
interface KeyPair {
|
|
38
|
+
publicKey: Buffer;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// IF MTP (from when confirmed) > seconds, _alice can redeem
|
|
42
|
+
function csvCheckSigOutput(_alice: KeyPair, _bob: KeyPair, sequence: number): Buffer {
|
|
43
|
+
return bitcoin.script.fromASM(
|
|
44
|
+
`
|
|
45
|
+
OP_IF
|
|
46
|
+
${bitcoin.script.number.encode(sequence).toString('hex')}
|
|
47
|
+
OP_CHECKSEQUENCEVERIFY
|
|
48
|
+
OP_DROP
|
|
49
|
+
OP_ELSE
|
|
50
|
+
${_bob.publicKey.toString('hex')}
|
|
51
|
+
OP_CHECKSIGVERIFY
|
|
52
|
+
OP_ENDIF
|
|
53
|
+
${_alice.publicKey.toString('hex')}
|
|
54
|
+
OP_CHECKSIG
|
|
55
|
+
`
|
|
56
|
+
.trim()
|
|
57
|
+
.replace(/\s+/g, ' '),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2 of 3 multisig of _bob, _charles, _dave,
|
|
62
|
+
// but after sequence1 time, _alice can allow the multisig to become 1 of 3.
|
|
63
|
+
// but after sequence2 time, _alice can sign for the output all by themself.
|
|
64
|
+
|
|
65
|
+
// Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example
|
|
66
|
+
|
|
67
|
+
// Note: bitcoinjs-lib will not offer specific support for problems with
|
|
68
|
+
// advanced script usages such as below. Use at your own risk.
|
|
69
|
+
function complexCsvOutput(
|
|
70
|
+
_alice: KeyPair,
|
|
71
|
+
_bob: KeyPair,
|
|
72
|
+
_charles: KeyPair,
|
|
73
|
+
_dave: KeyPair,
|
|
74
|
+
sequence1: number,
|
|
75
|
+
sequence2: number,
|
|
76
|
+
): Buffer {
|
|
77
|
+
return bitcoin.script.fromASM(
|
|
78
|
+
`
|
|
79
|
+
OP_IF
|
|
80
|
+
OP_IF
|
|
81
|
+
OP_2
|
|
82
|
+
OP_ELSE
|
|
83
|
+
${bitcoin.script.number.encode(sequence1).toString('hex')}
|
|
84
|
+
OP_CHECKSEQUENCEVERIFY
|
|
85
|
+
OP_DROP
|
|
86
|
+
${_alice.publicKey.toString('hex')}
|
|
87
|
+
OP_CHECKSIGVERIFY
|
|
88
|
+
OP_1
|
|
89
|
+
OP_ENDIF
|
|
90
|
+
${_bob.publicKey.toString('hex')}
|
|
91
|
+
${_charles.publicKey.toString('hex')}
|
|
92
|
+
${_dave.publicKey.toString('hex')}
|
|
93
|
+
OP_3
|
|
94
|
+
OP_CHECKMULTISIG
|
|
95
|
+
OP_ELSE
|
|
96
|
+
${bitcoin.script.number.encode(sequence2).toString('hex')}
|
|
97
|
+
OP_CHECKSEQUENCEVERIFY
|
|
98
|
+
OP_DROP
|
|
99
|
+
${_alice.publicKey.toString('hex')}
|
|
100
|
+
OP_CHECKSIG
|
|
101
|
+
OP_ENDIF
|
|
102
|
+
`
|
|
103
|
+
.trim()
|
|
104
|
+
.replace(/\s+/g, ' '),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// expiry will pass, {Alice's signature} OP_TRUE
|
|
109
|
+
it(
|
|
110
|
+
'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' +
|
|
111
|
+
'the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)',
|
|
112
|
+
async () => {
|
|
113
|
+
// 5 blocks from now
|
|
114
|
+
const sequence = bip68.encode({ blocks: 5 });
|
|
115
|
+
const p2sh = bitcoin.payments.p2sh({
|
|
116
|
+
redeem: {
|
|
117
|
+
output: csvCheckSigOutput(alice, bob, sequence),
|
|
118
|
+
},
|
|
119
|
+
network: regtest,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// fund the P2SH(CSV) address
|
|
123
|
+
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
124
|
+
const utx = await regtestUtils.fetch(unspent.txId);
|
|
125
|
+
// for non segwit inputs, you must pass the full transaction buffer
|
|
126
|
+
const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex');
|
|
127
|
+
|
|
128
|
+
// This is an example of using the finalizeInput second parameter to
|
|
129
|
+
// define how you finalize the inputs, allowing for any type of script.
|
|
130
|
+
const tx = new bitcoin.Psbt({ network: regtest })
|
|
131
|
+
.setVersion(2)
|
|
132
|
+
.addInput({
|
|
133
|
+
hash: unspent.txId,
|
|
134
|
+
index: unspent.vout,
|
|
135
|
+
sequence,
|
|
136
|
+
redeemScript: p2sh.redeem!.output!,
|
|
137
|
+
nonWitnessUtxo,
|
|
138
|
+
})
|
|
139
|
+
.addOutput({
|
|
140
|
+
address: regtestUtils.RANDOM_ADDRESS,
|
|
141
|
+
value: 7e4,
|
|
142
|
+
})
|
|
143
|
+
.signInput(0, alice)
|
|
144
|
+
.finalizeInput(0, csvGetFinalScripts) // See csvGetFinalScripts below
|
|
145
|
+
.extractTransaction();
|
|
146
|
+
|
|
147
|
+
// TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently
|
|
148
|
+
// ...
|
|
149
|
+
// into the future!
|
|
150
|
+
await regtestUtils.mine(10);
|
|
151
|
+
|
|
152
|
+
await regtestUtils.broadcast(tx.toHex());
|
|
153
|
+
|
|
154
|
+
await regtestUtils.verify({
|
|
155
|
+
txId: tx.getId(),
|
|
156
|
+
address: regtestUtils.RANDOM_ADDRESS,
|
|
157
|
+
vout: 0,
|
|
158
|
+
value: 7e4,
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// expiry in the future, {Alice's signature} OP_TRUE
|
|
164
|
+
it(
|
|
165
|
+
'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' +
|
|
166
|
+
'attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)',
|
|
167
|
+
async () => {
|
|
168
|
+
// two hours after confirmation
|
|
169
|
+
const sequence = bip68.encode({ seconds: 7168 });
|
|
170
|
+
const p2sh = bitcoin.payments.p2sh({
|
|
171
|
+
network: regtest,
|
|
172
|
+
redeem: {
|
|
173
|
+
output: csvCheckSigOutput(alice, bob, sequence),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// fund the P2SH(CSV) address
|
|
178
|
+
const unspent = await regtestUtils.faucet(p2sh.address!, 2e4);
|
|
179
|
+
|
|
180
|
+
const tx = new bitcoin.Transaction();
|
|
181
|
+
tx.version = 2;
|
|
182
|
+
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence);
|
|
183
|
+
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4);
|
|
184
|
+
|
|
185
|
+
// {Alice's signature} OP_TRUE
|
|
186
|
+
const signatureHash = tx.hashForSignature(0, p2sh.redeem!.output!, hashType);
|
|
187
|
+
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
188
|
+
network: regtest,
|
|
189
|
+
redeem: {
|
|
190
|
+
network: regtest,
|
|
191
|
+
output: p2sh.redeem!.output,
|
|
192
|
+
input: bitcoin.script.compile([
|
|
193
|
+
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
194
|
+
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
|
195
|
+
bitcoin.opcodes.OP_TRUE,
|
|
196
|
+
]),
|
|
197
|
+
},
|
|
198
|
+
}).input;
|
|
199
|
+
tx.setInputScript(0, redeemScriptSig!);
|
|
200
|
+
|
|
201
|
+
await regtestUtils.broadcast(tx.toHex()).catch((err: unknown) => {
|
|
202
|
+
assert.throws(() => {
|
|
203
|
+
if (err) throw err;
|
|
204
|
+
}, /Error: non-BIP68-final/);
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Check first combination of complex CSV, 2 of 3
|
|
210
|
+
it(
|
|
211
|
+
'can create (and broadcast via 3PBP) a Transaction where Bob and Charles ' +
|
|
212
|
+
'can send (complex CHECKSEQUENCEVERIFY)',
|
|
213
|
+
async () => {
|
|
214
|
+
// 2 blocks from now
|
|
215
|
+
const sequence1 = bip68.encode({ blocks: 2 });
|
|
216
|
+
// 5 blocks from now
|
|
217
|
+
const sequence2 = bip68.encode({ blocks: 5 });
|
|
218
|
+
const p2sh = bitcoin.payments.p2sh({
|
|
219
|
+
redeem: {
|
|
220
|
+
output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2),
|
|
221
|
+
},
|
|
222
|
+
network: regtest,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// fund the P2SH(CCSV) address
|
|
226
|
+
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
227
|
+
|
|
228
|
+
const tx = new bitcoin.Transaction();
|
|
229
|
+
tx.version = 2;
|
|
230
|
+
tx.addInput(idToHash(unspent.txId), unspent.vout);
|
|
231
|
+
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
232
|
+
|
|
233
|
+
// OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE
|
|
234
|
+
const signatureHash = tx.hashForSignature(0, p2sh.redeem!.output!, hashType);
|
|
235
|
+
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
236
|
+
network: regtest,
|
|
237
|
+
redeem: {
|
|
238
|
+
network: regtest,
|
|
239
|
+
output: p2sh.redeem!.output,
|
|
240
|
+
input: bitcoin.script.compile([
|
|
241
|
+
bitcoin.opcodes.OP_0,
|
|
242
|
+
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
|
243
|
+
bitcoin.script.signature.encode(charles.sign(signatureHash), hashType),
|
|
244
|
+
bitcoin.opcodes.OP_TRUE,
|
|
245
|
+
bitcoin.opcodes.OP_TRUE,
|
|
246
|
+
]),
|
|
247
|
+
},
|
|
248
|
+
}).input;
|
|
249
|
+
tx.setInputScript(0, redeemScriptSig!);
|
|
250
|
+
|
|
251
|
+
await regtestUtils.broadcast(tx.toHex());
|
|
252
|
+
|
|
253
|
+
await regtestUtils.verify({
|
|
254
|
+
txId: tx.getId(),
|
|
255
|
+
address: regtestUtils.RANDOM_ADDRESS,
|
|
256
|
+
vout: 0,
|
|
257
|
+
value: 7e4,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks
|
|
263
|
+
it(
|
|
264
|
+
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
|
|
265
|
+
'and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)',
|
|
266
|
+
async () => {
|
|
267
|
+
// 2 blocks from now
|
|
268
|
+
const sequence1 = bip68.encode({ blocks: 2 });
|
|
269
|
+
// 5 blocks from now
|
|
270
|
+
const sequence2 = bip68.encode({ blocks: 5 });
|
|
271
|
+
const p2sh = bitcoin.payments.p2sh({
|
|
272
|
+
redeem: {
|
|
273
|
+
output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2),
|
|
274
|
+
},
|
|
275
|
+
network: regtest,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// fund the P2SH(CCSV) address
|
|
279
|
+
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
280
|
+
|
|
281
|
+
const tx = new bitcoin.Transaction();
|
|
282
|
+
tx.version = 2;
|
|
283
|
+
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input
|
|
284
|
+
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
285
|
+
|
|
286
|
+
// OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE
|
|
287
|
+
const signatureHash = tx.hashForSignature(0, p2sh.redeem!.output!, hashType);
|
|
288
|
+
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
289
|
+
network: regtest,
|
|
290
|
+
redeem: {
|
|
291
|
+
network: regtest,
|
|
292
|
+
output: p2sh.redeem!.output,
|
|
293
|
+
input: bitcoin.script.compile([
|
|
294
|
+
bitcoin.opcodes.OP_0,
|
|
295
|
+
bitcoin.script.signature.encode(bob.sign(signatureHash), hashType),
|
|
296
|
+
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
297
|
+
bitcoin.opcodes.OP_0,
|
|
298
|
+
bitcoin.opcodes.OP_TRUE,
|
|
299
|
+
]),
|
|
300
|
+
},
|
|
301
|
+
}).input;
|
|
302
|
+
tx.setInputScript(0, redeemScriptSig!);
|
|
303
|
+
|
|
304
|
+
// Wait 2 blocks
|
|
305
|
+
await regtestUtils.mine(2);
|
|
306
|
+
|
|
307
|
+
await regtestUtils.broadcast(tx.toHex());
|
|
308
|
+
|
|
309
|
+
await regtestUtils.verify({
|
|
310
|
+
txId: tx.getId(),
|
|
311
|
+
address: regtestUtils.RANDOM_ADDRESS,
|
|
312
|
+
vout: 0,
|
|
313
|
+
value: 7e4,
|
|
314
|
+
});
|
|
315
|
+
},
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Check first combination of complex CSV, mediator after 5 blocks
|
|
319
|
+
it(
|
|
320
|
+
'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' +
|
|
321
|
+
'can send after 5 blocks (complex CHECKSEQUENCEVERIFY)',
|
|
322
|
+
async () => {
|
|
323
|
+
// 2 blocks from now
|
|
324
|
+
const sequence1 = bip68.encode({ blocks: 2 });
|
|
325
|
+
// 5 blocks from now
|
|
326
|
+
const sequence2 = bip68.encode({ blocks: 5 });
|
|
327
|
+
const p2sh = bitcoin.payments.p2sh({
|
|
328
|
+
redeem: {
|
|
329
|
+
output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2),
|
|
330
|
+
},
|
|
331
|
+
network: regtest,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// fund the P2SH(CCSV) address
|
|
335
|
+
const unspent = await regtestUtils.faucet(p2sh.address!, 1e5);
|
|
336
|
+
|
|
337
|
+
const tx = new bitcoin.Transaction();
|
|
338
|
+
tx.version = 2;
|
|
339
|
+
tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input
|
|
340
|
+
tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4);
|
|
341
|
+
|
|
342
|
+
// {Alice mediator sig} OP_FALSE
|
|
343
|
+
const signatureHash = tx.hashForSignature(0, p2sh.redeem!.output!, hashType);
|
|
344
|
+
const redeemScriptSig = bitcoin.payments.p2sh({
|
|
345
|
+
network: regtest,
|
|
346
|
+
redeem: {
|
|
347
|
+
network: regtest,
|
|
348
|
+
output: p2sh.redeem!.output,
|
|
349
|
+
input: bitcoin.script.compile([
|
|
350
|
+
bitcoin.script.signature.encode(alice.sign(signatureHash), hashType),
|
|
351
|
+
bitcoin.opcodes.OP_0,
|
|
352
|
+
]),
|
|
353
|
+
},
|
|
354
|
+
}).input;
|
|
355
|
+
tx.setInputScript(0, redeemScriptSig!);
|
|
356
|
+
|
|
357
|
+
// Wait 5 blocks
|
|
358
|
+
await regtestUtils.mine(5);
|
|
359
|
+
|
|
360
|
+
await regtestUtils.broadcast(tx.toHex());
|
|
361
|
+
|
|
362
|
+
await regtestUtils.verify({
|
|
363
|
+
txId: tx.getId(),
|
|
364
|
+
address: regtestUtils.RANDOM_ADDRESS,
|
|
365
|
+
vout: 0,
|
|
366
|
+
value: 7e4,
|
|
367
|
+
});
|
|
368
|
+
},
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// This function is used to finalize a CSV transaction using PSBT.
|
|
373
|
+
// See first test above.
|
|
374
|
+
function csvGetFinalScripts(
|
|
375
|
+
inputIndex: number,
|
|
376
|
+
input: PsbtInput,
|
|
377
|
+
script: Buffer,
|
|
378
|
+
isSegwit: boolean,
|
|
379
|
+
isP2SH: boolean,
|
|
380
|
+
isP2WSH: boolean,
|
|
381
|
+
): {
|
|
382
|
+
finalScriptSig: Buffer | undefined;
|
|
383
|
+
finalScriptWitness: Buffer | undefined;
|
|
384
|
+
} {
|
|
385
|
+
// Step 1: Check to make sure the meaningful script matches what you expect.
|
|
386
|
+
const decompiled = bitcoin.script.decompile(script);
|
|
387
|
+
// Checking if first OP is OP_IF... should do better check in production!
|
|
388
|
+
// You may even want to check the public keys in the script against a
|
|
389
|
+
// whitelist depending on the circumstances!!!
|
|
390
|
+
// You also want to check the contents of the input to see if you have enough
|
|
391
|
+
// info to actually construct the scriptSig and Witnesses.
|
|
392
|
+
if (!decompiled || decompiled[0] !== bitcoin.opcodes.OP_IF) {
|
|
393
|
+
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Step 2: Create final scripts
|
|
397
|
+
let payment: bitcoin.Payment = {
|
|
398
|
+
network: regtest,
|
|
399
|
+
output: script,
|
|
400
|
+
// This logic should be more strict and make sure the pubkeys in the
|
|
401
|
+
// meaningful script are the ones signing in the PSBT etc.
|
|
402
|
+
input: bitcoin.script.compile([input.partialSig![0].signature, bitcoin.opcodes.OP_TRUE]),
|
|
403
|
+
};
|
|
404
|
+
if (isP2WSH && isSegwit)
|
|
405
|
+
payment = bitcoin.payments.p2wsh({
|
|
406
|
+
network: regtest,
|
|
407
|
+
redeem: payment,
|
|
408
|
+
});
|
|
409
|
+
if (isP2SH)
|
|
410
|
+
payment = bitcoin.payments.p2sh({
|
|
411
|
+
network: regtest,
|
|
412
|
+
redeem: payment,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
|
|
416
|
+
let buffer = Buffer.allocUnsafe(0);
|
|
417
|
+
|
|
418
|
+
function writeSlice(slice: Buffer): void {
|
|
419
|
+
buffer = Buffer.concat([buffer, Buffer.from(slice)]);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function writeVarInt(i: number): void {
|
|
423
|
+
const currentLen = buffer.length;
|
|
424
|
+
const varintLen = varuint.encodingLength(i);
|
|
425
|
+
|
|
426
|
+
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
427
|
+
varuint.encode(i, buffer, currentLen);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function writeVarSlice(slice: Buffer): void {
|
|
431
|
+
writeVarInt(slice.length);
|
|
432
|
+
writeSlice(slice);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function writeVector(vector: Buffer[]): void {
|
|
436
|
+
writeVarInt(vector.length);
|
|
437
|
+
vector.forEach(writeVarSlice);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
writeVector(witness);
|
|
441
|
+
|
|
442
|
+
return buffer;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
finalScriptSig: payment.input,
|
|
447
|
+
finalScriptWitness:
|
|
448
|
+
payment.witness && payment.witness.length > 0
|
|
449
|
+
? witnessStackToScriptWitness(payment.witness)
|
|
450
|
+
: undefined,
|
|
451
|
+
};
|
|
452
|
+
}
|