@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 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
- Old script, doesn't have address at all. Should be wrapped in P2SH/P2WSH/P2SH-P2WSH.
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 address (pre-SegWit)
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
- Same as P2PKH, but for SegWit V0. Basic bech32 address.
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 smart-contracts.
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
- **_NOTE_**: redeemScript should be added to transaction input in order to spend.
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
- Same as P2SH but for SegWit V0.
179
+ SegWit V0 version of [P2SH](#p2sh-script-hash).
182
180
 
183
- **_NOTE_**: witnessScript should be added to transaction input in order to spend.
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
- **_NOTE_**: both reedemScript and witnessScript should be added to transaction in order to spend.
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, doesn't have an address, should be wrapped in P2SH/P2WSH/P2SH-P2WSH.
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
- **_NOTE_**: By default we don't accept duplicate public keys, to avoid creating wrong multisig by mistake. However there is a flag: allowSamePubkeys, in case you really need that.
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
- **_NOTE_**: it takes `p2tr(PubKey?, ScriptTree?)` and works as PubKey OR ScriptTree, which means
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
- **_NOTE_**: tapInternalKey, tapMerkleRoot, tapLeafScript should be added to transaction input in order to spend.
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
- // NOTE: by default P2TR expects binary tree, but btc.p2tr can build it if list of scripts passed.
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
- **_NOTE_**: First arg is M, if M!=PubKeys.length, it will create a multi-leaf M-of-N taproot script tree.
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
- **_NOTE_**: By default we don't accept duplicate public keys, to avoid creating the wrong multisig by mistake. However there is a flag called allowSamePubkeys, in case you really need that.
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
- **_NOTE_**: By default, we don't accept duplicate public keys in order to avoid creating the wrong multisig by mistake. However, there is a flag called allowSamePubkeys, in case you really need that.
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
- **_NOTE_**: experimental, use at your own risk.
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
- This is a specific case of `p2tr_ns(1, [pubkey])`, which is the same as the BTC descriptor: `tr($H,pk(PUBKEY))`
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
- **_NOTE_**: we support both PSBTv0 and draft PSBTv2 (there is no PSBTv1). If PSBTv2 transaction encoded into PSBTv1, all PSBTv2 fields will be stripped.
396
- **_NOTE_**: we strip 'unknown' keys inside PSBT, they needed for new version/features support, however any unsupported feature/new version can significantly break assumptions about code.
397
- If you have use-case where they needed, please open issue.
398
- For PSBTv2: tx_modifiable, taproot+bip32 is not supported yet.
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
- **_NOTE_**: amount in addOutputAddress handled as 'bitcoins' if string, and as satoshi if bigint.
507
- Why? BigInt usually comes from calculations/API, which is usually in satoshi. String is probably user input, so it is worth
508
- to handle conversion to satoshi's in that case.
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) tx32.addOutputAddress(address, amount);
583
- deepStrictEqual(hex.encode(tx32.unsignedTx), RAW_TX_HEX);
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
- // NOTE: we don't use HDKey, because it will everything because of bip32 derivation
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(