@scure/btc-signer 0.5.0 → 1.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/README.md +51 -35
- package/index.d.ts +149 -60
- package/index.d.ts.map +1 -1
- package/index.js +41 -8
- package/index.js.map +1 -1
- package/index.ts +51 -21
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# scure-btc-signer
|
|
2
2
|
|
|
3
|
-
Audited minimal library for creating, signing & decoding Bitcoin transactions.
|
|
3
|
+
Audited & minimal library for creating, signing & decoding Bitcoin transactions.
|
|
4
4
|
|
|
5
5
|
- ✍️ Create transactions, inputs, outputs, sign them
|
|
6
6
|
- 📡 No network code: simplified audits and offline usage
|
|
@@ -87,7 +87,7 @@ import { deepStrictEqual, throws } from 'assert';
|
|
|
87
87
|
|
|
88
88
|
### P2PK (Pay To Public Key)
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Legacy script, doesn't have an address. Must be wrapped in P2SH / P2WSH / P2SH-P2WSH. Not recommended.
|
|
91
91
|
|
|
92
92
|
```ts
|
|
93
93
|
const uncompressed = hex.decode(
|
|
@@ -104,7 +104,7 @@ deepStrictEqual(btc.p2pk(uncompressed), {
|
|
|
104
104
|
|
|
105
105
|
### P2PKH (Public Key Hash)
|
|
106
106
|
|
|
107
|
-
Classic
|
|
107
|
+
Classic (pre-SegWit) address.
|
|
108
108
|
|
|
109
109
|
```ts
|
|
110
110
|
const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
|
|
@@ -139,8 +139,7 @@ deepStrictEqual(btc.p2sh(btc.p2wsh(btc.p2pkh(PubKey))), {
|
|
|
139
139
|
|
|
140
140
|
### P2WPKH (Witness Public Key Hash)
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
Cannot be wrapped in P2WSH.
|
|
142
|
+
SegWit V0 version of [P2PKH](#p2pkh-public-key-hash). Basic bech32 address. Can't be wrapped in [P2WSH](#p2wsh-witness-script-hash).
|
|
144
143
|
|
|
145
144
|
```ts
|
|
146
145
|
const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
|
|
@@ -160,10 +159,9 @@ deepStrictEqual(btc.p2sh(btc.p2wpkh(PubKey)), {
|
|
|
160
159
|
|
|
161
160
|
### P2SH (Script Hash)
|
|
162
161
|
|
|
163
|
-
Classic (pre-SegWit) script address. Useful for multisig and other
|
|
164
|
-
Takes full output of other payments, not just script.
|
|
162
|
+
Classic (pre-SegWit) script address. Useful for multisig and other advanced use-cases. Consumes full output of other payments — NOT only script.
|
|
165
163
|
|
|
166
|
-
|
|
164
|
+
Required tx input fields to make it spendable: `redeemScript`
|
|
167
165
|
|
|
168
166
|
```ts
|
|
169
167
|
const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
|
|
@@ -178,9 +176,9 @@ deepStrictEqual(btc.p2sh(btc.p2pkh(PubKey)), {
|
|
|
178
176
|
|
|
179
177
|
### P2WSH (Witness Script Hash)
|
|
180
178
|
|
|
181
|
-
|
|
179
|
+
SegWit V0 version of [P2SH](#p2sh-script-hash).
|
|
182
180
|
|
|
183
|
-
|
|
181
|
+
Required tx input fields to make it spendable: `witnessScript`
|
|
184
182
|
|
|
185
183
|
```ts
|
|
186
184
|
const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
|
|
@@ -196,7 +194,7 @@ deepStrictEqual(btc.p2wsh(btc.p2pkh(PubKey)), {
|
|
|
196
194
|
|
|
197
195
|
Not really script type, but construction of P2WSH inside P2SH.
|
|
198
196
|
|
|
199
|
-
|
|
197
|
+
Required tx input fields to make it spendable: `redeemScript`, `witnessScript`
|
|
200
198
|
|
|
201
199
|
```ts
|
|
202
200
|
const PubKey = hex.decode('030000000000000000000000000000000000000000000000000000000000000001');
|
|
@@ -211,10 +209,9 @@ deepStrictEqual(btc.p2sh(btc.p2wsh(btc.p2pkh(PubKey))), {
|
|
|
211
209
|
|
|
212
210
|
### P2MS (classic multisig)
|
|
213
211
|
|
|
214
|
-
Classic (pre-taproot) M-of-N Multisig
|
|
212
|
+
Classic / segwit (pre-taproot) M-of-N Multisig. Doesn't have an address, must be wrapped in P2SH / P2WSH / P2SH-P2WSH.
|
|
215
213
|
|
|
216
|
-
|
|
217
|
-
Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
|
|
214
|
+
Duplicate public keys are not accepted to reduce mistakes. Use flag `allowSamePubkeys` to override the behavior, for cases like `2-of-[A,A,B,C]`, which can be signed by `A or (B and C)`.
|
|
218
215
|
|
|
219
216
|
```ts
|
|
220
217
|
const PubKeys = [
|
|
@@ -262,12 +259,12 @@ deepStrictEqual(btc.p2wsh(btc.p2ms(2, PubKeys)), btc.sortedMultisig(2, PubKeys,
|
|
|
262
259
|
|
|
263
260
|
TapRoot (SegWit V1) script which replaces both public key and script types from previous versions.
|
|
264
261
|
|
|
265
|
-
|
|
262
|
+
Consumes `p2tr(PubKey?, ScriptTree?)` and works as `PubKey` OR `ScriptTree`, which means
|
|
266
263
|
if you use any spendable PubKey and ScriptTree of multi-sig, owner of private key for PubKey will
|
|
267
264
|
be able to spend output. If PubKey is undefined we use static unspendable PubKey by default, which leaks information about script type. However, any dynamic unspendable keys will require complex interaction
|
|
268
265
|
to sign multi-sig wallets, and there is no BIP/PSBT fields for that yet.
|
|
269
266
|
|
|
270
|
-
|
|
267
|
+
Required tx input fields to make it spendable: `tapInternalKey`, `tapMerkleRoot`, `tapLeafScript`
|
|
271
268
|
|
|
272
269
|
```ts
|
|
273
270
|
const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
|
|
@@ -285,7 +282,7 @@ const clean = (x) => ({ type: x.type, address: x.address, script: hex.encode(x.s
|
|
|
285
282
|
const PubKey2 = hex.decode('0202020202020202020202020202020202020202020202020202020202020202');
|
|
286
283
|
const PubKey3 = hex.decode('1212121212121212121212121212121212121212121212121212121212121212');
|
|
287
284
|
// Nested P2TR, owner of private key for any of PubKeys can spend whole
|
|
288
|
-
//
|
|
285
|
+
// By default P2TR expects binary tree, but btc.p2tr can build it if list of scripts passed.
|
|
289
286
|
// Also, you can include {weight: N} to scripts to create differently balanced tree.
|
|
290
287
|
deepStrictEqual(
|
|
291
288
|
clean(btc.p2tr(undefined, [btc.p2tr_pk(PubKey), btc.p2tr_pk(PubKey2), btc.p2tr_pk(PubKey3)])),
|
|
@@ -312,12 +309,11 @@ deepStrictEqual(
|
|
|
312
309
|
|
|
313
310
|
Taproot N-of-N multisig (`[<PubKeys[0:n-1]> CHECKSIGVERIFY] <PubKeys[n-1]> CHECKSIG`).
|
|
314
311
|
|
|
315
|
-
|
|
312
|
+
First arg is M, if M!=PubKeys.length, it will create a multi-leaf M-of-N taproot script tree.
|
|
316
313
|
This allows one to reveal only `M` PubKeys on spend, without any information about the others.
|
|
317
314
|
This is fast for cases like 15-of-20, but extremely slow for cases like 5-of-20.
|
|
318
315
|
|
|
319
|
-
|
|
320
|
-
Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
|
|
316
|
+
Duplicate public keys are not accepted to reduce mistakes. Use flag `allowSamePubkeys` to override the behavior, for cases like `2-of-[A,A,B,C]`, which can be signed by `A or (B and C)`.
|
|
321
317
|
|
|
322
318
|
```ts
|
|
323
319
|
const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
|
|
@@ -348,10 +344,9 @@ deepStrictEqual(clean(btc.p2tr(undefined, btc.p2tr_ns(2, [PubKey, PubKey2, PubKe
|
|
|
348
344
|
|
|
349
345
|
M-of-N single leaf TapRoot multisig (`<PubKeys[0]> CHECKSIG [<PubKeys[1:n]> CHECKSIGADD] <M> NUMEQUAL`)
|
|
350
346
|
|
|
351
|
-
|
|
352
|
-
Valid use-case: `2-of-[A,A,B,C]`, can be signed by `A or (B and C)`.
|
|
347
|
+
Duplicate public keys are not accepted to reduce mistakes. Use flag `allowSamePubkeys` to override the behavior, for cases like `2-of-[A,A,B,C]`, which can be signed by `A or (B and C)`.
|
|
353
348
|
|
|
354
|
-
**
|
|
349
|
+
**Experimental**, use at your own risk.
|
|
355
350
|
|
|
356
351
|
```ts
|
|
357
352
|
const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
|
|
@@ -375,7 +370,7 @@ deepStrictEqual(clean(btc.p2tr(undefined, btc.p2tr_ms(2, [PubKey, PubKey2, PubKe
|
|
|
375
370
|
|
|
376
371
|
### P2TR-PK (Taproot single P2PK script)
|
|
377
372
|
|
|
378
|
-
|
|
373
|
+
Specific case of `p2tr_ns(1, [pubkey])`, which is the same as the BTC descriptor: `tr($H,pk(PUBKEY))`
|
|
379
374
|
|
|
380
375
|
```ts
|
|
381
376
|
const PubKey = hex.decode('0101010101010101010101010101010101010101010101010101010101010101');
|
|
@@ -392,10 +387,13 @@ deepStrictEqual(clean(btc.p2tr(undefined, [btc.p2tr_pk(PubKey)])), {
|
|
|
392
387
|
|
|
393
388
|
### Encode/decode
|
|
394
389
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
390
|
+
We support both PSBTv0 and draft PSBTv2 (there is no PSBTv1). If PSBTv2 transaction is encoded into PSBTv1, all PSBTv2 fields will be stripped.
|
|
391
|
+
|
|
392
|
+
We strip 'unknown' keys inside PSBT, they needed for new version/features support,
|
|
393
|
+
however any unsupported feature/new version can significantly break assumptions about code.
|
|
394
|
+
If you have use-case where they are needed, create a github issue.
|
|
395
|
+
|
|
396
|
+
PSBTv2 features tx_modifiable and taproot+bip32 are not supported yet.
|
|
399
397
|
|
|
400
398
|
```ts
|
|
401
399
|
// Decode
|
|
@@ -413,6 +411,9 @@ We have txid (BE) instead of hash (LE) in transactions. We can support both,
|
|
|
413
411
|
but txid is consistent across block explorers, while some explorers treat hash
|
|
414
412
|
as txid - so hash is not consistent.
|
|
415
413
|
|
|
414
|
+
Use `getInput` and `inputsLength` to read information about inputs: they return a copy.
|
|
415
|
+
This is neccessary to avoid accidential modification of internal structures without calling methods (addInput/updateInput) that will verify correctness.
|
|
416
|
+
|
|
416
417
|
```ts
|
|
417
418
|
type TransactionInput = {
|
|
418
419
|
txid?: Bytes,
|
|
@@ -499,13 +500,23 @@ deepStrictEqual(tx.inputs[0], {
|
|
|
499
500
|
index: 10,
|
|
500
501
|
sequence: btc.DEFAULT_SEQUENCE,
|
|
501
502
|
});
|
|
503
|
+
|
|
504
|
+
// Read inputs
|
|
505
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
506
|
+
console.log('I', tx.getInput(i));
|
|
507
|
+
}
|
|
502
508
|
```
|
|
503
509
|
|
|
504
510
|
### Outputs
|
|
505
511
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
512
|
+
`addOutputAddress` uses bigint amounts, which mean satoshis - NOT btc. If you need btc representation, use Decimal:
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
const amountSatoshi = btc.Decimal.decode('1.5'); // 1.5 btc in satoshi
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Use `getOutput` and `outputsLength` to read outputs information. This methods returns copy of output, instead of internal representation.
|
|
519
|
+
This is neccessary to avoid accidential modification of internal structures without calling methods (addOutput/updateOutput) that will verify correctness.
|
|
509
520
|
|
|
510
521
|
```ts
|
|
511
522
|
type TransactionOutput = {
|
|
@@ -562,6 +573,11 @@ deepStrictEqual(tx.outputs[0], {
|
|
|
562
573
|
script,
|
|
563
574
|
amount: 200n,
|
|
564
575
|
});
|
|
576
|
+
|
|
577
|
+
// Read outputs
|
|
578
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
579
|
+
console.log('O', tx.getOutput(i));
|
|
580
|
+
}
|
|
565
581
|
```
|
|
566
582
|
|
|
567
583
|
### Basic transaction sign
|
|
@@ -579,8 +595,8 @@ for (const inp of TX_TEST_INPUTS) {
|
|
|
579
595
|
},
|
|
580
596
|
});
|
|
581
597
|
}
|
|
582
|
-
for (const [address, amount] of TX_TEST_OUTPUTS)
|
|
583
|
-
deepStrictEqual(hex.encode(
|
|
598
|
+
for (const [address, amount] of TX_TEST_OUTPUTS) txP2WPKH.addOutputAddress(address, amount);
|
|
599
|
+
deepStrictEqual(hex.encode(txP2WPKH.unsignedTx), RAW_TX_HEX);
|
|
584
600
|
txP2WPKH.sign(privKey);
|
|
585
601
|
txP2WPKH.finalize();
|
|
586
602
|
deepStrictEqual(txP2WPKH.id, 'cbb94443b19861df0824914fa654212facc071854e0df6f7388b482a6394526d');
|
|
@@ -687,7 +703,7 @@ const psbt3 = tx3.toPSBT();
|
|
|
687
703
|
- cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr (m/0'/0'/0')
|
|
688
704
|
- cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d (m/0'/0'/2')
|
|
689
705
|
*/
|
|
690
|
-
//
|
|
706
|
+
// We don't use HDKey, because it will everything because of bip32 derivation
|
|
691
707
|
const tx4 = btc.Transaction.fromPSBT(psbt3);
|
|
692
708
|
tx4.sign(btc.WIF(testnet).decode('cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr'));
|
|
693
709
|
tx4.sign(btc.WIF(testnet).decode('cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d'));
|
|
@@ -783,7 +799,7 @@ deepStrictEqual(
|
|
|
783
799
|
|
|
784
800
|
### OutScript
|
|
785
801
|
|
|
786
|
-
Encoding/decoding of output scripts
|
|
802
|
+
Encoding / decoding of output scripts
|
|
787
803
|
|
|
788
804
|
```ts
|
|
789
805
|
deepStrictEqual(
|