@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.
- package/README.md +19 -3
- package/lib/_type_test.d.ts +2 -0
- package/lib/_type_test.d.ts.map +1 -0
- package/lib/_type_test.js +59 -0
- package/lib/_type_test.js.map +1 -0
- package/lib/esm/_type_test.js +57 -0
- package/lib/esm/_type_test.js.map +1 -0
- package/lib/esm/index.js +13 -3007
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/payment.js +681 -0
- package/lib/esm/payment.js.map +1 -0
- package/lib/esm/psbt.js +441 -0
- package/lib/esm/psbt.js.map +1 -0
- package/lib/esm/script.js +347 -0
- package/lib/esm/script.js.map +1 -0
- package/lib/esm/transaction.js +1013 -0
- package/lib/esm/transaction.js.map +1 -0
- package/lib/esm/utils.js +115 -0
- package/lib/esm/utils.js.map +1 -0
- package/lib/esm/utxo.js +491 -0
- package/lib/esm/utxo.js.map +1 -0
- package/lib/index.d.ts +18 -1439
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +53 -3042
- package/lib/index.js.map +1 -0
- package/lib/payment.d.ts +167 -0
- package/lib/payment.d.ts.map +1 -0
- package/lib/payment.js +704 -0
- package/lib/payment.js.map +1 -0
- package/lib/psbt.d.ts +834 -0
- package/lib/psbt.d.ts.map +1 -0
- package/lib/psbt.js +446 -0
- package/lib/psbt.js.map +1 -0
- package/lib/script.d.ts +154 -0
- package/lib/script.d.ts.map +1 -0
- package/lib/script.js +353 -0
- package/lib/script.js.map +1 -0
- package/lib/transaction.d.ts +223 -0
- package/lib/transaction.d.ts.map +1 -0
- package/lib/transaction.js +1022 -0
- package/lib/transaction.js.map +1 -0
- package/lib/utils.d.ts +30 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +128 -0
- package/lib/utils.js.map +1 -0
- package/lib/utxo.d.ts +251 -0
- package/lib/utxo.d.ts.map +1 -0
- package/lib/utxo.js +501 -0
- package/lib/utxo.js.map +1 -0
- package/package.json +40 -10
- package/src/_type_test.ts +69 -0
- package/src/index.ts +28 -0
- package/src/package.json +3 -0
- package/src/payment.ts +749 -0
- package/src/psbt.ts +512 -0
- package/src/script.ts +236 -0
- package/src/transaction.ts +1065 -0
- package/src/utils.ts +118 -0
- package/src/utxo.ts +517 -0
- 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);
|