@scure/btc-signer 1.2.2 → 1.3.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.
Files changed (60) hide show
  1. package/README.md +19 -3
  2. package/lib/_type_test.d.ts +2 -0
  3. package/lib/_type_test.d.ts.map +1 -0
  4. package/lib/_type_test.js +59 -0
  5. package/lib/_type_test.js.map +1 -0
  6. package/lib/esm/_type_test.js +57 -0
  7. package/lib/esm/_type_test.js.map +1 -0
  8. package/lib/esm/index.js +13 -3007
  9. package/lib/esm/index.js.map +1 -1
  10. package/lib/esm/payment.js +681 -0
  11. package/lib/esm/payment.js.map +1 -0
  12. package/lib/esm/psbt.js +441 -0
  13. package/lib/esm/psbt.js.map +1 -0
  14. package/lib/esm/script.js +347 -0
  15. package/lib/esm/script.js.map +1 -0
  16. package/lib/esm/transaction.js +1013 -0
  17. package/lib/esm/transaction.js.map +1 -0
  18. package/lib/esm/utils.js +115 -0
  19. package/lib/esm/utils.js.map +1 -0
  20. package/lib/esm/utxo.js +491 -0
  21. package/lib/esm/utxo.js.map +1 -0
  22. package/lib/index.d.ts +18 -1439
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +53 -3042
  25. package/lib/index.js.map +1 -0
  26. package/lib/payment.d.ts +167 -0
  27. package/lib/payment.d.ts.map +1 -0
  28. package/lib/payment.js +704 -0
  29. package/lib/payment.js.map +1 -0
  30. package/lib/psbt.d.ts +834 -0
  31. package/lib/psbt.d.ts.map +1 -0
  32. package/lib/psbt.js +446 -0
  33. package/lib/psbt.js.map +1 -0
  34. package/lib/script.d.ts +154 -0
  35. package/lib/script.d.ts.map +1 -0
  36. package/lib/script.js +353 -0
  37. package/lib/script.js.map +1 -0
  38. package/lib/transaction.d.ts +223 -0
  39. package/lib/transaction.d.ts.map +1 -0
  40. package/lib/transaction.js +1022 -0
  41. package/lib/transaction.js.map +1 -0
  42. package/lib/utils.d.ts +30 -0
  43. package/lib/utils.d.ts.map +1 -0
  44. package/lib/utils.js +128 -0
  45. package/lib/utils.js.map +1 -0
  46. package/lib/utxo.d.ts +251 -0
  47. package/lib/utxo.d.ts.map +1 -0
  48. package/lib/utxo.js +501 -0
  49. package/lib/utxo.js.map +1 -0
  50. package/package.json +40 -10
  51. package/src/_type_test.ts +69 -0
  52. package/src/index.ts +28 -0
  53. package/src/package.json +3 -0
  54. package/src/payment.ts +749 -0
  55. package/src/psbt.ts +512 -0
  56. package/src/script.ts +236 -0
  57. package/src/transaction.ts +1065 -0
  58. package/src/utils.ts +118 -0
  59. package/src/utxo.ts +517 -0
  60. package/index.ts +0 -3067
package/src/script.ts ADDED
@@ -0,0 +1,236 @@
1
+ import * as P from 'micro-packed';
2
+
3
+ export const MAX_SCRIPT_BYTE_LENGTH = 520;
4
+
5
+ // prettier-ignore
6
+ export enum OP {
7
+ OP_0 = 0x00, PUSHDATA1 = 0x4c, PUSHDATA2, PUSHDATA4, '1NEGATE',
8
+ RESERVED = 0x50,
9
+ OP_1, OP_2, OP_3, OP_4, OP_5, OP_6, OP_7, OP_8,
10
+ OP_9, OP_10, OP_11, OP_12, OP_13, OP_14, OP_15, OP_16,
11
+ // Control
12
+ NOP, VER, IF, NOTIF, VERIF, VERNOTIF, ELSE, ENDIF, VERIFY, RETURN,
13
+ // Stack
14
+ TOALTSTACK, FROMALTSTACK, '2DROP', '2DUP', '3DUP', '2OVER', '2ROT', '2SWAP',
15
+ IFDUP, DEPTH, DROP, DUP, NIP, OVER, PICK, ROLL, ROT, SWAP, TUCK,
16
+ // Splice
17
+ CAT, SUBSTR, LEFT, RIGHT, SIZE,
18
+ // Boolean logic
19
+ INVERT, AND, OR, XOR, EQUAL, EQUALVERIFY, RESERVED1, RESERVED2,
20
+ // Numbers
21
+ '1ADD', '1SUB', '2MUL', '2DIV',
22
+ NEGATE, ABS, NOT, '0NOTEQUAL',
23
+ ADD, SUB, MUL, DIV, MOD, LSHIFT, RSHIFT, BOOLAND, BOOLOR,
24
+ NUMEQUAL, NUMEQUALVERIFY, NUMNOTEQUAL, LESSTHAN, GREATERTHAN,
25
+ LESSTHANOREQUAL, GREATERTHANOREQUAL, MIN, MAX, WITHIN,
26
+ // Crypto
27
+ RIPEMD160, SHA1, SHA256, HASH160, HASH256, CODESEPARATOR,
28
+ CHECKSIG, CHECKSIGVERIFY, CHECKMULTISIG, CHECKMULTISIGVERIFY,
29
+ // Expansion
30
+ NOP1, CHECKLOCKTIMEVERIFY, CHECKSEQUENCEVERIFY, NOP4, NOP5, NOP6, NOP7, NOP8, NOP9, NOP10,
31
+ // BIP 342
32
+ CHECKSIGADD,
33
+ // Invalid
34
+ INVALID = 0xff,
35
+ }
36
+
37
+ export type ScriptOP = keyof typeof OP | Uint8Array | number;
38
+ export type ScriptType = ScriptOP[];
39
+
40
+ // We can encode almost any number as ScriptNum, however, parsing will be a problem
41
+ // since we can't know if buffer is a number or something else.
42
+ export function ScriptNum(bytesLimit = 6, forceMinimal = false): P.CoderType<bigint> {
43
+ return P.wrap({
44
+ encodeStream: (w: P.Writer, value: bigint) => {
45
+ if (value === 0n) return;
46
+ const neg = value < 0;
47
+ const val = BigInt(value);
48
+ const nums = [];
49
+ for (let abs = neg ? -val : val; abs; abs >>= 8n) nums.push(Number(abs & 0xffn));
50
+ if (nums[nums.length - 1] >= 0x80) nums.push(neg ? 0x80 : 0);
51
+ else if (neg) nums[nums.length - 1] |= 0x80;
52
+ w.bytes(new Uint8Array(nums));
53
+ },
54
+ decodeStream: (r: P.Reader): bigint => {
55
+ const len = r.leftBytes;
56
+ if (len > bytesLimit)
57
+ throw new Error(`ScriptNum: number (${len}) bigger than limit=${bytesLimit}`);
58
+ if (len === 0) return 0n;
59
+ if (forceMinimal) {
60
+ // MSB is zero (without sign bit) -> not minimally encoded
61
+ if ((r.data[len - 1] & 0x7f) === 0) {
62
+ // exception
63
+ if (len <= 1 || (r.data[len - 2] & 0x80) === 0)
64
+ throw new Error('Non-minimally encoded ScriptNum');
65
+ }
66
+ }
67
+ let last = 0;
68
+ let res = 0n;
69
+ for (let i = 0; i < len; ++i) {
70
+ last = r.byte();
71
+ res |= BigInt(last) << (8n * BigInt(i));
72
+ }
73
+ if (last >= 0x80) {
74
+ res &= (2n ** BigInt(len * 8) - 1n) >> 1n;
75
+ res = -res;
76
+ }
77
+ return res;
78
+ },
79
+ });
80
+ }
81
+
82
+ export function OpToNum(op: ScriptOP, bytesLimit = 4, forceMinimal = true) {
83
+ if (typeof op === 'number') return op;
84
+ if (P.isBytes(op)) {
85
+ try {
86
+ const val = ScriptNum(bytesLimit, forceMinimal).decode(op);
87
+ if (val > Number.MAX_SAFE_INTEGER) return;
88
+ return Number(val);
89
+ } catch (e) {
90
+ return;
91
+ }
92
+ }
93
+ return;
94
+ }
95
+
96
+ // Converts script bytes to parsed script
97
+ // 5221030000000000000000000000000000000000000000000000000000000000000001210300000000000000000000000000000000000000000000000000000000000000022103000000000000000000000000000000000000000000000000000000000000000353ae
98
+ // =>
99
+ // OP_2
100
+ // 030000000000000000000000000000000000000000000000000000000000000001
101
+ // 030000000000000000000000000000000000000000000000000000000000000002
102
+ // 030000000000000000000000000000000000000000000000000000000000000003
103
+ // OP_3
104
+ // CHECKMULTISIG
105
+ export const Script: P.CoderType<ScriptType> = P.wrap({
106
+ encodeStream: (w: P.Writer, value: ScriptType) => {
107
+ for (let o of value) {
108
+ if (typeof o === 'string') {
109
+ if (OP[o] === undefined) throw new Error(`Unknown opcode=${o}`);
110
+ w.byte(OP[o]);
111
+ continue;
112
+ } else if (typeof o === 'number') {
113
+ if (o === 0x00) {
114
+ w.byte(0x00);
115
+ continue;
116
+ } else if (1 <= o && o <= 16) {
117
+ w.byte(OP.OP_1 - 1 + o);
118
+ continue;
119
+ }
120
+ }
121
+ // Encode big numbers
122
+ if (typeof o === 'number') o = ScriptNum().encode(BigInt(o));
123
+ if (!P.isBytes(o)) throw new Error(`Wrong Script OP=${o} (${typeof o})`);
124
+ // Bytes
125
+ const len = o.length;
126
+ if (len < OP.PUSHDATA1) w.byte(len);
127
+ else if (len <= 0xff) {
128
+ w.byte(OP.PUSHDATA1);
129
+ w.byte(len);
130
+ } else if (len <= 0xffff) {
131
+ w.byte(OP.PUSHDATA2);
132
+ w.bytes(P.U16LE.encode(len));
133
+ } else {
134
+ w.byte(OP.PUSHDATA4);
135
+ w.bytes(P.U32LE.encode(len));
136
+ }
137
+ w.bytes(o);
138
+ }
139
+ },
140
+ decodeStream: (r: P.Reader): ScriptType => {
141
+ const out: ScriptType = [];
142
+ while (!r.isEnd()) {
143
+ const cur = r.byte();
144
+ // if 0 < cur < 78
145
+ if (OP.OP_0 < cur && cur <= OP.PUSHDATA4) {
146
+ let len;
147
+ if (cur < OP.PUSHDATA1) len = cur;
148
+ else if (cur === OP.PUSHDATA1) len = P.U8.decodeStream(r);
149
+ else if (cur === OP.PUSHDATA2) len = P.U16LE.decodeStream(r);
150
+ else if (cur === OP.PUSHDATA4) len = P.U32LE.decodeStream(r);
151
+ else throw new Error('Should be not possible');
152
+ out.push(r.bytes(len));
153
+ } else if (cur === 0x00) {
154
+ out.push(0);
155
+ } else if (OP.OP_1 <= cur && cur <= OP.OP_16) {
156
+ out.push(cur - (OP.OP_1 - 1));
157
+ } else {
158
+ const op = OP[cur] as keyof typeof OP;
159
+ if (op === undefined) throw new Error(`Unknown opcode=${cur.toString(16)}`);
160
+ out.push(op);
161
+ }
162
+ }
163
+ return out;
164
+ },
165
+ });
166
+
167
+ // BTC specific variable length integer encoding
168
+ // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
169
+ const CSLimits: Record<number, [number, number, bigint, bigint]> = {
170
+ 0xfd: [0xfd, 2, 253n, 65535n],
171
+ 0xfe: [0xfe, 4, 65536n, 4294967295n],
172
+ 0xff: [0xff, 8, 4294967296n, 18446744073709551615n],
173
+ };
174
+ export const CompactSize: P.CoderType<bigint> = P.wrap({
175
+ encodeStream: (w: P.Writer, value: bigint) => {
176
+ if (typeof value === 'number') value = BigInt(value);
177
+ if (0n <= value && value <= 252n) return w.byte(Number(value));
178
+ for (const [flag, bytes, start, stop] of Object.values(CSLimits)) {
179
+ if (start > value || value > stop) continue;
180
+ w.byte(flag);
181
+ for (let i = 0; i < bytes; i++) w.byte(Number((value >> (8n * BigInt(i))) & 0xffn));
182
+ return;
183
+ }
184
+ throw w.err(`VarInt too big: ${value}`);
185
+ },
186
+ decodeStream: (r: P.Reader): bigint => {
187
+ const b0 = r.byte();
188
+ if (b0 <= 0xfc) return BigInt(b0);
189
+ const [_, bytes, start] = CSLimits[b0];
190
+ let num = 0n;
191
+ for (let i = 0; i < bytes; i++) num |= BigInt(r.byte()) << (8n * BigInt(i));
192
+ if (num < start) throw r.err(`Wrong CompactSize(${8 * bytes})`);
193
+ return num;
194
+ },
195
+ });
196
+
197
+ // Same thing, but in number instead of bigint. Checks for safe integer inside
198
+ export const CompactSizeLen = P.apply(CompactSize, P.coders.number);
199
+
200
+ // ui8a of size <CompactSize>
201
+ export const VarBytes = P.bytes(CompactSize);
202
+
203
+ // SegWit v0 stack of witness buffers
204
+ export const RawWitness = P.array(CompactSizeLen, VarBytes);
205
+
206
+ // Array of size <CompactSize>
207
+ export const BTCArray = <T>(t: P.CoderType<T>): P.CoderType<T[]> => P.array(CompactSize, t);
208
+
209
+ export const RawInput = P.struct({
210
+ txid: P.bytes(32, true), // hash(prev_tx),
211
+ index: P.U32LE, // output number of previous tx
212
+ finalScriptSig: VarBytes, // btc merges input and output script, executes it. If ok = tx passes
213
+ sequence: P.U32LE, // ?
214
+ });
215
+
216
+ export const RawOutput = P.struct({ amount: P.U64LE, script: VarBytes });
217
+
218
+ // https://en.bitcoin.it/wiki/Protocol_documentation#tx
219
+ const _RawTx = P.struct({
220
+ version: P.I32LE,
221
+ segwitFlag: P.flag(new Uint8Array([0x00, 0x01])),
222
+ inputs: BTCArray(RawInput),
223
+ outputs: BTCArray(RawOutput),
224
+ witnesses: P.flagged('segwitFlag', P.array('inputs/length', RawWitness)),
225
+ // < 500000000 Block number at which this transaction is unlocked
226
+ // >= 500000000 UNIX timestamp at which this transaction is unlocked
227
+ // Handled as part of PSBTv2
228
+ lockTime: P.U32LE,
229
+ });
230
+
231
+ function validateRawTx(tx: P.UnwrapCoder<typeof _RawTx>) {
232
+ if (tx.segwitFlag && tx.witnesses && !tx.witnesses.length)
233
+ throw new Error('Segwit flag with empty witnesses array');
234
+ return tx;
235
+ }
236
+ export const RawTx = P.validate(_RawTx, validateRawTx);