@vbyte/btc-dev 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/LICENSE +121 -0
- package/README.md +5 -0
- package/dist/class/index.d.ts +5 -0
- package/dist/class/index.js +5 -0
- package/dist/class/signer.d.ts +18 -0
- package/dist/class/signer.js +32 -0
- package/dist/class/tx.d.ts +38 -0
- package/dist/class/tx.js +73 -0
- package/dist/class/txin.d.ts +29 -0
- package/dist/class/txin.js +68 -0
- package/dist/class/txout.d.ts +18 -0
- package/dist/class/txout.js +38 -0
- package/dist/class/witness.d.ts +20 -0
- package/dist/class/witness.js +57 -0
- package/dist/const.d.ts +22 -0
- package/dist/const.js +36 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lib/address/encode.d.ts +3 -0
- package/dist/lib/address/encode.js +79 -0
- package/dist/lib/address/index.d.ts +20 -0
- package/dist/lib/address/index.js +21 -0
- package/dist/lib/address/p2pkh.d.ts +10 -0
- package/dist/lib/address/p2pkh.js +33 -0
- package/dist/lib/address/p2sh.d.ts +10 -0
- package/dist/lib/address/p2sh.js +33 -0
- package/dist/lib/address/p2tr.d.ts +8 -0
- package/dist/lib/address/p2tr.js +26 -0
- package/dist/lib/address/p2wpkh.d.ts +10 -0
- package/dist/lib/address/p2wpkh.js +34 -0
- package/dist/lib/address/p2wsh.d.ts +10 -0
- package/dist/lib/address/p2wsh.js +33 -0
- package/dist/lib/address/script.d.ts +5 -0
- package/dist/lib/address/script.js +46 -0
- package/dist/lib/address/util.d.ts +4 -0
- package/dist/lib/address/util.js +57 -0
- package/dist/lib/meta/index.d.ts +2 -0
- package/dist/lib/meta/index.js +2 -0
- package/dist/lib/meta/pointer.d.ts +42 -0
- package/dist/lib/meta/pointer.js +69 -0
- package/dist/lib/meta/scribe.d.ts +7 -0
- package/dist/lib/meta/scribe.js +192 -0
- package/dist/lib/psbt/encoder.d.ts +5 -0
- package/dist/lib/psbt/encoder.js +21 -0
- package/dist/lib/psbt/index.d.ts +4 -0
- package/dist/lib/psbt/index.js +4 -0
- package/dist/lib/psbt/meta.d.ts +3 -0
- package/dist/lib/psbt/meta.js +11 -0
- package/dist/lib/psbt/util.d.ts +5 -0
- package/dist/lib/psbt/util.js +44 -0
- package/dist/lib/psbt/validate.d.ts +2 -0
- package/dist/lib/psbt/validate.js +11 -0
- package/dist/lib/script/decode.d.ts +2 -0
- package/dist/lib/script/decode.js +55 -0
- package/dist/lib/script/encode.d.ts +6 -0
- package/dist/lib/script/encode.js +80 -0
- package/dist/lib/script/index.d.ts +126 -0
- package/dist/lib/script/index.js +17 -0
- package/dist/lib/script/util.d.ts +2 -0
- package/dist/lib/script/util.js +10 -0
- package/dist/lib/script/words.d.ts +116 -0
- package/dist/lib/script/words.js +164 -0
- package/dist/lib/sighash/index.d.ts +5 -0
- package/dist/lib/sighash/index.js +5 -0
- package/dist/lib/sighash/segwit.d.ts +3 -0
- package/dist/lib/sighash/segwit.js +89 -0
- package/dist/lib/sighash/sign.d.ts +3 -0
- package/dist/lib/sighash/sign.js +20 -0
- package/dist/lib/sighash/taproot.d.ts +9 -0
- package/dist/lib/sighash/taproot.js +124 -0
- package/dist/lib/sighash/util.d.ts +3 -0
- package/dist/lib/sighash/util.js +16 -0
- package/dist/lib/sighash/verify.d.ts +1 -0
- package/dist/lib/sighash/verify.js +1 -0
- package/dist/lib/taproot/cblock.d.ts +3 -0
- package/dist/lib/taproot/cblock.js +55 -0
- package/dist/lib/taproot/encode.d.ts +6 -0
- package/dist/lib/taproot/encode.js +26 -0
- package/dist/lib/taproot/index.d.ts +4 -0
- package/dist/lib/taproot/index.js +4 -0
- package/dist/lib/taproot/parse.d.ts +11 -0
- package/dist/lib/taproot/parse.js +47 -0
- package/dist/lib/taproot/tree.d.ts +3 -0
- package/dist/lib/taproot/tree.js +49 -0
- package/dist/lib/tx/create.d.ts +6 -0
- package/dist/lib/tx/create.js +54 -0
- package/dist/lib/tx/decode.d.ts +9 -0
- package/dist/lib/tx/decode.js +109 -0
- package/dist/lib/tx/encode.d.ts +15 -0
- package/dist/lib/tx/encode.js +94 -0
- package/dist/lib/tx/index.d.ts +10 -0
- package/dist/lib/tx/index.js +10 -0
- package/dist/lib/tx/locktime.d.ts +7 -0
- package/dist/lib/tx/locktime.js +37 -0
- package/dist/lib/tx/meta.d.ts +8 -0
- package/dist/lib/tx/meta.js +48 -0
- package/dist/lib/tx/parse.d.ts +2 -0
- package/dist/lib/tx/parse.js +12 -0
- package/dist/lib/tx/sequence.d.ts +7 -0
- package/dist/lib/tx/sequence.js +65 -0
- package/dist/lib/tx/size.d.ts +10 -0
- package/dist/lib/tx/size.js +48 -0
- package/dist/lib/tx/validate.d.ts +7 -0
- package/dist/lib/tx/validate.js +21 -0
- package/dist/lib/tx/witness.d.ts +3 -0
- package/dist/lib/tx/witness.js +85 -0
- package/dist/main.cjs +14625 -0
- package/dist/main.cjs.map +1 -0
- package/dist/module.mjs +14610 -0
- package/dist/module.mjs.map +1 -0
- package/dist/package.json +106 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/taproot.d.ts +18 -0
- package/dist/schema/taproot.js +9 -0
- package/dist/schema/tx.d.ts +278 -0
- package/dist/schema/tx.js +35 -0
- package/dist/script.js +15 -0
- package/dist/script.js.map +1 -0
- package/dist/types/address.d.ts +34 -0
- package/dist/types/address.js +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +9 -0
- package/dist/types/meta.d.ts +10 -0
- package/dist/types/meta.js +1 -0
- package/dist/types/psbt.d.ts +9 -0
- package/dist/types/psbt.js +1 -0
- package/dist/types/sighash.d.ts +14 -0
- package/dist/types/sighash.js +1 -0
- package/dist/types/taproot.d.ts +35 -0
- package/dist/types/taproot.js +1 -0
- package/dist/types/transaction.d.ts +81 -0
- package/dist/types/transaction.js +1 -0
- package/dist/types/txdata.d.ts +45 -0
- package/dist/types/txdata.js +1 -0
- package/dist/types/txmeta.d.ts +19 -0
- package/dist/types/txmeta.js +1 -0
- package/dist/types/witness.d.ts +30 -0
- package/dist/types/witness.js +1 -0
- package/package.json +106 -0
- package/src/class/index.ts +5 -0
- package/src/class/signer.ts +47 -0
- package/src/class/tx.ts +118 -0
- package/src/class/txin.ts +95 -0
- package/src/class/txout.ts +57 -0
- package/src/class/witness.ts +85 -0
- package/src/const.ts +43 -0
- package/src/index.ts +14 -0
- package/src/lib/address/encode.ts +183 -0
- package/src/lib/address/index.ts +24 -0
- package/src/lib/address/p2pkh.ts +65 -0
- package/src/lib/address/p2sh.ts +65 -0
- package/src/lib/address/p2tr.ts +51 -0
- package/src/lib/address/p2wpkh.ts +67 -0
- package/src/lib/address/p2wsh.ts +65 -0
- package/src/lib/address/script.ts +63 -0
- package/src/lib/address/util.ts +102 -0
- package/src/lib/meta/index.ts +2 -0
- package/src/lib/meta/pointer.ts +107 -0
- package/src/lib/meta/scribe.ts +251 -0
- package/src/lib/psbt/encoder.ts +24 -0
- package/src/lib/psbt/index.ts +4 -0
- package/src/lib/psbt/meta.ts +15 -0
- package/src/lib/psbt/util.ts +62 -0
- package/src/lib/psbt/validate.ts +18 -0
- package/src/lib/script/decode.ts +75 -0
- package/src/lib/script/encode.ts +130 -0
- package/src/lib/script/index.ts +26 -0
- package/src/lib/script/util.ts +78 -0
- package/src/lib/script/words.ts +182 -0
- package/src/lib/sighash/index.ts +5 -0
- package/src/lib/sighash/segwit.ts +152 -0
- package/src/lib/sighash/sign.ts +35 -0
- package/src/lib/sighash/taproot.ts +236 -0
- package/src/lib/sighash/util.ts +29 -0
- package/src/lib/sighash/verify.ts +83 -0
- package/src/lib/taproot/cblock.ts +95 -0
- package/src/lib/taproot/encode.ts +49 -0
- package/src/lib/taproot/index.ts +4 -0
- package/src/lib/taproot/parse.ts +65 -0
- package/src/lib/taproot/tree.ts +94 -0
- package/src/lib/tx/create.ts +82 -0
- package/src/lib/tx/decode.ts +145 -0
- package/src/lib/tx/encode.ts +154 -0
- package/src/lib/tx/index.ts +10 -0
- package/src/lib/tx/locktime.ts +57 -0
- package/src/lib/tx/meta.ts +73 -0
- package/src/lib/tx/parse.ts +16 -0
- package/src/lib/tx/sequence.ts +146 -0
- package/src/lib/tx/size.ts +77 -0
- package/src/lib/tx/validate.ts +36 -0
- package/src/lib/tx/witness.ts +122 -0
- package/src/schema/index.ts +2 -0
- package/src/schema/taproot.ts +12 -0
- package/src/schema/tx.ts +42 -0
- package/src/types/address.ts +39 -0
- package/src/types/index.ts +9 -0
- package/src/types/meta.ts +10 -0
- package/src/types/psbt.ts +15 -0
- package/src/types/sighash.ts +16 -0
- package/src/types/taproot.ts +40 -0
- package/src/types/transaction.ts +98 -0
- package/src/types/txdata.ts +53 -0
- package/src/types/txmeta.ts +25 -0
- package/src/types/witness.ts +36 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Buff, Stream } from '@vbyte/buff';
|
|
2
|
+
import { Assert } from '@vbyte/micro-lib';
|
|
3
|
+
import { encode_script } from '../../lib/script/encode.js';
|
|
4
|
+
import { decode_script } from '../../lib/script/decode.js';
|
|
5
|
+
const _0n = BigInt(0);
|
|
6
|
+
const _1n = BigInt(1);
|
|
7
|
+
const _26n = BigInt(26);
|
|
8
|
+
export var Inscription;
|
|
9
|
+
(function (Inscription) {
|
|
10
|
+
Inscription.encode = encode_inscription;
|
|
11
|
+
Inscription.decode = decode_inscription;
|
|
12
|
+
})(Inscription || (Inscription = {}));
|
|
13
|
+
export function decode_inscription(script) {
|
|
14
|
+
const envelopes = parse_envelopes(script);
|
|
15
|
+
return envelopes.map(parse_record);
|
|
16
|
+
}
|
|
17
|
+
export function encode_inscription(data) {
|
|
18
|
+
return data.map(create_envelope).join('');
|
|
19
|
+
}
|
|
20
|
+
function create_envelope(data) {
|
|
21
|
+
let asm = ['OP_0', 'OP_IF', '6f7264'];
|
|
22
|
+
if (typeof data.delegate === 'string') {
|
|
23
|
+
const id = encode_id(data.delegate);
|
|
24
|
+
asm.push('OP_11', id);
|
|
25
|
+
}
|
|
26
|
+
if (typeof data.ref === 'string') {
|
|
27
|
+
asm.push('OP_WITHIN', data.ref);
|
|
28
|
+
}
|
|
29
|
+
if (typeof data.parent === 'string') {
|
|
30
|
+
const id = encode_id(data.parent);
|
|
31
|
+
asm.push('OP_3', id);
|
|
32
|
+
}
|
|
33
|
+
if (typeof data.opcode === 'number') {
|
|
34
|
+
const code = encode_pointer(data.opcode);
|
|
35
|
+
asm.push('OP_NOP', code);
|
|
36
|
+
}
|
|
37
|
+
if (typeof data.pointer === 'number') {
|
|
38
|
+
const ptr = encode_pointer(data.pointer);
|
|
39
|
+
asm.push('OP_2', ptr);
|
|
40
|
+
}
|
|
41
|
+
if (typeof data.rune === 'string') {
|
|
42
|
+
const label = encode_rune_label(data.rune);
|
|
43
|
+
asm.push('OP_13', label);
|
|
44
|
+
}
|
|
45
|
+
if (typeof data.mimetype === 'string') {
|
|
46
|
+
const label = encode_label(data.mimetype);
|
|
47
|
+
asm.push('OP_1', label);
|
|
48
|
+
}
|
|
49
|
+
if (typeof data.content === 'string') {
|
|
50
|
+
const chunks = encode_content(data.content);
|
|
51
|
+
asm.push('OP_0', ...chunks);
|
|
52
|
+
}
|
|
53
|
+
asm.push('OP_ENDIF');
|
|
54
|
+
return encode_script(asm);
|
|
55
|
+
}
|
|
56
|
+
function parse_envelopes(script) {
|
|
57
|
+
const words = decode_script(script);
|
|
58
|
+
const start_idx = words.findIndex(e => e === 'OP_0');
|
|
59
|
+
Assert.ok(start_idx !== -1, 'inscription envelope not found');
|
|
60
|
+
const envelopes = [];
|
|
61
|
+
for (let idx = start_idx; idx < words.length; idx++) {
|
|
62
|
+
Assert.ok(words[idx + 1] === 'OP_IF', 'OP_IF missing from envelope');
|
|
63
|
+
Assert.ok(words[idx + 2] === '6f7264', 'magic bytes missing from envelope');
|
|
64
|
+
const stop_idx = words.findIndex(e => e === 'OP_ENDIF');
|
|
65
|
+
Assert.ok(stop_idx !== -1, 'inscription envelope missing END_IF statement');
|
|
66
|
+
const env = words.slice(idx + 3, stop_idx);
|
|
67
|
+
envelopes.push(env);
|
|
68
|
+
idx += stop_idx;
|
|
69
|
+
}
|
|
70
|
+
return envelopes;
|
|
71
|
+
}
|
|
72
|
+
function parse_record(envelope) {
|
|
73
|
+
const record = {};
|
|
74
|
+
for (let i = 0; i < envelope.length; i++) {
|
|
75
|
+
switch (envelope[i]) {
|
|
76
|
+
case 'OP_1':
|
|
77
|
+
record.mimetype = decode_label(envelope[i + 1]);
|
|
78
|
+
i += 1;
|
|
79
|
+
break;
|
|
80
|
+
case 'OP_2':
|
|
81
|
+
record.pointer = decode_pointer(envelope[i + 1]);
|
|
82
|
+
i += 1;
|
|
83
|
+
break;
|
|
84
|
+
case 'OP_3':
|
|
85
|
+
record.parent = decode_id(envelope[i + 1]);
|
|
86
|
+
i += 1;
|
|
87
|
+
break;
|
|
88
|
+
case 'OP_11':
|
|
89
|
+
record.delegate = decode_id(envelope[i + 1]);
|
|
90
|
+
i += 1;
|
|
91
|
+
break;
|
|
92
|
+
case 'OP_13':
|
|
93
|
+
record.rune = decode_rune_label(envelope[i + 1]);
|
|
94
|
+
i += 1;
|
|
95
|
+
break;
|
|
96
|
+
case 'OP_WITHIN':
|
|
97
|
+
record.ref = envelope[i + 1];
|
|
98
|
+
i += 1;
|
|
99
|
+
break;
|
|
100
|
+
case 'OP_NOP':
|
|
101
|
+
record.opcode = decode_pointer(envelope[i + 1]);
|
|
102
|
+
i += 1;
|
|
103
|
+
break;
|
|
104
|
+
case 'OP_0':
|
|
105
|
+
record.content = decode_content(envelope.slice(i + 1));
|
|
106
|
+
return record;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return record;
|
|
110
|
+
}
|
|
111
|
+
function encode_id(identifier) {
|
|
112
|
+
Assert.ok(identifier.includes('i'), 'identifier must include an index');
|
|
113
|
+
const parts = identifier.split('i');
|
|
114
|
+
const bytes = Buff.hex(parts[0]);
|
|
115
|
+
const idx = Number(parts[1]);
|
|
116
|
+
const txid = bytes.reverse().hex;
|
|
117
|
+
return (idx !== 0) ? txid + Buff.num(idx).hex : txid;
|
|
118
|
+
}
|
|
119
|
+
function decode_id(hexstr) {
|
|
120
|
+
const bytes = Buff.hex(hexstr);
|
|
121
|
+
const idx = bytes.at(-1) ?? 0;
|
|
122
|
+
const txid = bytes.slice(0, -1).reverse().hex;
|
|
123
|
+
return txid + 'i' + String(idx);
|
|
124
|
+
}
|
|
125
|
+
function encode_pointer(pointer) {
|
|
126
|
+
return Buff.num(pointer).reverse().hex;
|
|
127
|
+
}
|
|
128
|
+
function decode_pointer(hexstr) {
|
|
129
|
+
return Buff.hex(hexstr).reverse().num;
|
|
130
|
+
}
|
|
131
|
+
function encode_label(label) {
|
|
132
|
+
return Buff.str(label).hex;
|
|
133
|
+
}
|
|
134
|
+
function decode_label(hexstr) {
|
|
135
|
+
return Buff.hex(hexstr).str;
|
|
136
|
+
}
|
|
137
|
+
function encode_content(content) {
|
|
138
|
+
const bytes = Buff.is_hex(content)
|
|
139
|
+
? Buff.hex(content)
|
|
140
|
+
: Buff.str(content);
|
|
141
|
+
const stream = new Stream(bytes);
|
|
142
|
+
const chunks = [];
|
|
143
|
+
while (stream.size > 0) {
|
|
144
|
+
if (stream.size > 520) {
|
|
145
|
+
const chunk = stream.read(520);
|
|
146
|
+
chunks.push(chunk.hex);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const chunk = stream.read(stream.size);
|
|
150
|
+
chunks.push(chunk.hex);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return chunks;
|
|
154
|
+
}
|
|
155
|
+
function decode_content(hexstrs, type = 'hex') {
|
|
156
|
+
const data = Buff.join(hexstrs);
|
|
157
|
+
return (type === 'hex')
|
|
158
|
+
? data.hex
|
|
159
|
+
: data.str;
|
|
160
|
+
}
|
|
161
|
+
function encode_rune_label(label) {
|
|
162
|
+
const str = label.toUpperCase();
|
|
163
|
+
let big = _0n;
|
|
164
|
+
for (const char of str) {
|
|
165
|
+
if (char >= 'A' && char <= 'Z') {
|
|
166
|
+
big = big * _26n + BigInt(char.charCodeAt(0) - ('A'.charCodeAt(0) - 1));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
big = big - _1n;
|
|
173
|
+
return Buff.big(big).reverse().hex;
|
|
174
|
+
}
|
|
175
|
+
function decode_rune_label(hex) {
|
|
176
|
+
let big = Buff.hex(hex).reverse().big;
|
|
177
|
+
big = big + _1n;
|
|
178
|
+
let result = '';
|
|
179
|
+
while (big > _0n) {
|
|
180
|
+
const mod = big % _26n;
|
|
181
|
+
if (mod === _0n) {
|
|
182
|
+
result = 'Z' + result;
|
|
183
|
+
big = big / _26n - _1n;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const charCode = Number(mod) + 'A'.charCodeAt(0) - 1;
|
|
187
|
+
result = String.fromCharCode(charCode) + result;
|
|
188
|
+
big = big / _26n;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Transaction } from '@scure/btc-signer';
|
|
2
|
+
import type { PSBTData } from '../../types/index.js';
|
|
3
|
+
export declare function decode_psbt(b64str: string): Transaction;
|
|
4
|
+
export declare function encode_psbt(psbt: PSBTData): string;
|
|
5
|
+
export declare function parse_psbt(psbt: string | PSBTData): Transaction;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Base64 } from '@vbyte/micro-lib';
|
|
2
|
+
import { Transaction } from '@scure/btc-signer';
|
|
3
|
+
export function decode_psbt(b64str) {
|
|
4
|
+
const psbt = Base64.decode(b64str);
|
|
5
|
+
return Transaction.fromPSBT(psbt, { allowUnknownOutputs: true });
|
|
6
|
+
}
|
|
7
|
+
export function encode_psbt(psbt) {
|
|
8
|
+
const psbt_bytes = psbt.toPSBT(0);
|
|
9
|
+
return Base64.encode(psbt_bytes);
|
|
10
|
+
}
|
|
11
|
+
export function parse_psbt(psbt) {
|
|
12
|
+
if (psbt instanceof Transaction) {
|
|
13
|
+
return psbt;
|
|
14
|
+
}
|
|
15
|
+
else if (typeof psbt === 'string') {
|
|
16
|
+
return decode_psbt(psbt);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
throw new Error('invalid psbt input: ' + psbt);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { parse_psbt } from './encoder.js';
|
|
2
|
+
import { finalize_legacy_inputs } from './util.js';
|
|
3
|
+
export function get_vsize(psbt) {
|
|
4
|
+
const pdata = parse_psbt(psbt);
|
|
5
|
+
return pdata.vsize;
|
|
6
|
+
}
|
|
7
|
+
export function get_txhex(psbt) {
|
|
8
|
+
let pdata = parse_psbt(psbt);
|
|
9
|
+
pdata = finalize_legacy_inputs(pdata);
|
|
10
|
+
return pdata.hex;
|
|
11
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PSBTData, PSBTInput, PSBTOutput, PSBTPrevouts } from '../../types/index.js';
|
|
2
|
+
export declare function collect_vins(psbt: string | PSBTData): PSBTInput[];
|
|
3
|
+
export declare function collect_vouts(psbt: string | PSBTData): PSBTOutput[];
|
|
4
|
+
export declare function collect_prevouts(psbt: PSBTData): PSBTPrevouts;
|
|
5
|
+
export declare function finalize_legacy_inputs(pdata: PSBTData): import("node_modules/@scure/btc-signer/transaction.js").Transaction;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Assert } from '@vbyte/micro-lib';
|
|
2
|
+
import { parse_psbt } from './encoder.js';
|
|
3
|
+
export function collect_vins(psbt) {
|
|
4
|
+
const pdata = parse_psbt(psbt);
|
|
5
|
+
const count = pdata.inputsLength;
|
|
6
|
+
const vins = [];
|
|
7
|
+
for (let i = 0; i < count; i++) {
|
|
8
|
+
const vin = pdata.getInput(i);
|
|
9
|
+
vins.push(vin);
|
|
10
|
+
}
|
|
11
|
+
return vins;
|
|
12
|
+
}
|
|
13
|
+
export function collect_vouts(psbt) {
|
|
14
|
+
const pdata = parse_psbt(psbt);
|
|
15
|
+
const count = pdata.outputsLength;
|
|
16
|
+
const vouts = [];
|
|
17
|
+
for (let i = 0; i < count; i++) {
|
|
18
|
+
const vout = pdata.getOutput(i);
|
|
19
|
+
vouts.push(vout);
|
|
20
|
+
}
|
|
21
|
+
return vouts;
|
|
22
|
+
}
|
|
23
|
+
export function collect_prevouts(psbt) {
|
|
24
|
+
const amounts = [], scripts = [];
|
|
25
|
+
const pdata = parse_psbt(psbt);
|
|
26
|
+
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
27
|
+
const txin = pdata.getInput(i);
|
|
28
|
+
Assert.exists(txin.witnessUtxo, `witness utxo does not exist for input ${i}`);
|
|
29
|
+
amounts.push(txin.witnessUtxo.amount);
|
|
30
|
+
scripts.push(txin.witnessUtxo.script);
|
|
31
|
+
}
|
|
32
|
+
return { amounts, scripts };
|
|
33
|
+
}
|
|
34
|
+
export function finalize_legacy_inputs(pdata) {
|
|
35
|
+
for (let i = 0; i < pdata.inputsLength; i++) {
|
|
36
|
+
const pvin = pdata.getInput(i);
|
|
37
|
+
const script = pvin.redeemScript;
|
|
38
|
+
const psig = pvin.partialSig?.at(0);
|
|
39
|
+
if (script !== undefined && psig !== undefined) {
|
|
40
|
+
pdata.finalizeIdx(i);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return pdata;
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Assert } from '@vbyte/micro-lib';
|
|
2
|
+
import { parse_psbt } from './encoder.js';
|
|
3
|
+
import { collect_vins, collect_vouts } from './util.js';
|
|
4
|
+
export function assert_psbt_is_funded(psbt) {
|
|
5
|
+
const pdata = parse_psbt(psbt);
|
|
6
|
+
const pvouts = collect_vins(pdata);
|
|
7
|
+
const txouts = collect_vouts(pdata);
|
|
8
|
+
const vin_amt = pvouts.reduce((p, n) => p + Number(n.witnessUtxo?.amount ?? 0), 0);
|
|
9
|
+
const out_amt = txouts.reduce((p, n) => p + Number(n.amount ?? 0), 0);
|
|
10
|
+
Assert.ok(vin_amt >= out_amt, `value in (${vin_amt}) < value out (${out_amt})`);
|
|
11
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Stream } from '@vbyte/buff';
|
|
2
|
+
import { get_op_code, get_op_type, is_valid_op } from './words.js';
|
|
3
|
+
export function decode_script(script) {
|
|
4
|
+
const stream = new Stream(script);
|
|
5
|
+
const stack = [];
|
|
6
|
+
const stack_size = stream.size;
|
|
7
|
+
let word;
|
|
8
|
+
let word_type;
|
|
9
|
+
let word_size;
|
|
10
|
+
let count = 0;
|
|
11
|
+
while (count < stack_size) {
|
|
12
|
+
word = stream.read(1).num;
|
|
13
|
+
word_type = get_op_type(word);
|
|
14
|
+
count++;
|
|
15
|
+
switch (word_type) {
|
|
16
|
+
case 'varint':
|
|
17
|
+
stack.push(stream.read(word).hex);
|
|
18
|
+
count += word;
|
|
19
|
+
break;
|
|
20
|
+
case 'pushdata1':
|
|
21
|
+
word_size = stream.read(1).reverse().num;
|
|
22
|
+
stack.push(stream.read(word_size).hex);
|
|
23
|
+
count += word_size + 1;
|
|
24
|
+
break;
|
|
25
|
+
case 'pushdata2':
|
|
26
|
+
word_size = stream.read(2).reverse().num;
|
|
27
|
+
stack.push(stream.read(word_size).hex);
|
|
28
|
+
count += word_size + 2;
|
|
29
|
+
break;
|
|
30
|
+
case 'pushdata4':
|
|
31
|
+
word_size = stream.read(4).reverse().num;
|
|
32
|
+
stack.push(stream.read(word_size).hex);
|
|
33
|
+
count += word_size + 4;
|
|
34
|
+
break;
|
|
35
|
+
case 'opcode':
|
|
36
|
+
if (!is_valid_op(word)) {
|
|
37
|
+
throw new Error(`Invalid OPCODE: ${word}`);
|
|
38
|
+
}
|
|
39
|
+
stack.push(get_op_code(word));
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`Word type undefined: ${word}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return stack;
|
|
46
|
+
}
|
|
47
|
+
export function is_valid_script(script) {
|
|
48
|
+
try {
|
|
49
|
+
const stack = decode_script(script);
|
|
50
|
+
return stack.length > 0;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff';
|
|
2
|
+
export declare function encode_script(words: (string | number | Uint8Array)[], varint?: boolean): string;
|
|
3
|
+
export declare function encode_script_word(word: string | number | Uint8Array): Uint8Array;
|
|
4
|
+
export declare function split_script_word(word: Uint8Array): Buff[];
|
|
5
|
+
export declare function prefix_word_size(word: Uint8Array): Buff;
|
|
6
|
+
export declare function get_size_varint(size: number): Buff;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Buff, Stream } from '@vbyte/buff';
|
|
2
|
+
import { get_asm_code, } from './words.js';
|
|
3
|
+
const MAX_WORD_SIZE = 520;
|
|
4
|
+
export function encode_script(words, varint = false) {
|
|
5
|
+
if (words.length === 0)
|
|
6
|
+
return '00';
|
|
7
|
+
const bytes = [];
|
|
8
|
+
for (const word of words) {
|
|
9
|
+
bytes.push(encode_script_word(word));
|
|
10
|
+
}
|
|
11
|
+
const buffer = Buff.join(bytes);
|
|
12
|
+
return (varint)
|
|
13
|
+
? buffer.prepend(Buff.varint(buffer.length, 'le')).hex
|
|
14
|
+
: buffer.hex;
|
|
15
|
+
}
|
|
16
|
+
export function encode_script_word(word) {
|
|
17
|
+
let buff;
|
|
18
|
+
if (typeof (word) === 'string') {
|
|
19
|
+
if (word.startsWith('OP_')) {
|
|
20
|
+
const asm_code = get_asm_code(word);
|
|
21
|
+
return Buff.num(asm_code, 1);
|
|
22
|
+
}
|
|
23
|
+
else if (Buff.is_hex(word)) {
|
|
24
|
+
buff = Buff.hex(word);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
buff = Buff.str(word);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (typeof (word) === 'number') {
|
|
31
|
+
buff = Buff.num(word);
|
|
32
|
+
}
|
|
33
|
+
else if (word instanceof Uint8Array) {
|
|
34
|
+
buff = new Buff(word);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw new Error('invalid word type:' + typeof (word));
|
|
38
|
+
}
|
|
39
|
+
if (buff.length === 1 && buff[0] <= 16) {
|
|
40
|
+
if (buff[0] !== 0)
|
|
41
|
+
buff[0] += 0x50;
|
|
42
|
+
}
|
|
43
|
+
else if (buff.length > MAX_WORD_SIZE) {
|
|
44
|
+
let words;
|
|
45
|
+
words = split_script_word(buff);
|
|
46
|
+
words = words.map(e => prefix_word_size(e));
|
|
47
|
+
buff = Buff.join(words);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
buff = prefix_word_size(buff);
|
|
51
|
+
}
|
|
52
|
+
return buff;
|
|
53
|
+
}
|
|
54
|
+
export function split_script_word(word) {
|
|
55
|
+
const words = [];
|
|
56
|
+
const buff = new Stream(word);
|
|
57
|
+
while (buff.size > MAX_WORD_SIZE) {
|
|
58
|
+
words.push(buff.read(MAX_WORD_SIZE));
|
|
59
|
+
}
|
|
60
|
+
words.push(buff.read(buff.size));
|
|
61
|
+
return words;
|
|
62
|
+
}
|
|
63
|
+
export function prefix_word_size(word) {
|
|
64
|
+
const varint = get_size_varint(word.length);
|
|
65
|
+
return Buff.join([varint, word]);
|
|
66
|
+
}
|
|
67
|
+
export function get_size_varint(size) {
|
|
68
|
+
const OP_PUSHDATA1 = Buff.num(0x4c, 1);
|
|
69
|
+
const OP_PUSHDATA2 = Buff.num(0x4d, 1);
|
|
70
|
+
switch (true) {
|
|
71
|
+
case (size <= 0x4b):
|
|
72
|
+
return Buff.num(size);
|
|
73
|
+
case (size > 0x4b && size < 0x100):
|
|
74
|
+
return Buff.join([OP_PUSHDATA1, Buff.num(size, 1, 'le')]);
|
|
75
|
+
case (size >= 0x100 && size <= MAX_WORD_SIZE):
|
|
76
|
+
return Buff.join([OP_PUSHDATA2, Buff.num(size, 2, 'le')]);
|
|
77
|
+
default:
|
|
78
|
+
throw new Error('Invalid word size:' + size.toString());
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { encode_script } from './encode.js';
|
|
2
|
+
import { decode_script, is_valid_script } from './decode.js';
|
|
3
|
+
import { parse_script_pubkeys, prefix_script_size } from './util.js';
|
|
4
|
+
export * from './decode.js';
|
|
5
|
+
export * from './encode.js';
|
|
6
|
+
export * from './util.js';
|
|
7
|
+
export * from './words.js';
|
|
8
|
+
export declare namespace ScriptUtil {
|
|
9
|
+
const prefix_size: typeof prefix_script_size;
|
|
10
|
+
const decode: typeof decode_script;
|
|
11
|
+
const encode: typeof encode_script;
|
|
12
|
+
const is_valid: typeof is_valid_script;
|
|
13
|
+
const get_pubkeys: typeof parse_script_pubkeys;
|
|
14
|
+
const OPCODES: {
|
|
15
|
+
OP_0: number;
|
|
16
|
+
OP_PUSHDATA1: number;
|
|
17
|
+
OP_PUSHDATA2: number;
|
|
18
|
+
OP_PUSHDATA4: number;
|
|
19
|
+
OP_1NEGATE: number;
|
|
20
|
+
OP_SUCCESS80: number;
|
|
21
|
+
OP_1: number;
|
|
22
|
+
OP_2: number;
|
|
23
|
+
OP_3: number;
|
|
24
|
+
OP_4: number;
|
|
25
|
+
OP_5: number;
|
|
26
|
+
OP_6: number;
|
|
27
|
+
OP_7: number;
|
|
28
|
+
OP_8: number;
|
|
29
|
+
OP_9: number;
|
|
30
|
+
OP_10: number;
|
|
31
|
+
OP_11: number;
|
|
32
|
+
OP_12: number;
|
|
33
|
+
OP_13: number;
|
|
34
|
+
OP_14: number;
|
|
35
|
+
OP_15: number;
|
|
36
|
+
OP_16: number;
|
|
37
|
+
OP_NOP: number;
|
|
38
|
+
OP_SUCCESS98: number;
|
|
39
|
+
OP_IF: number;
|
|
40
|
+
OP_NOTIF: number;
|
|
41
|
+
OP_ELSE: number;
|
|
42
|
+
OP_ENDIF: number;
|
|
43
|
+
OP_VERIFY: number;
|
|
44
|
+
OP_RETURN: number;
|
|
45
|
+
OP_TOALTSTACK: number;
|
|
46
|
+
OP_FROMALTSTACK: number;
|
|
47
|
+
OP_2DROP: number;
|
|
48
|
+
OP_2DUP: number;
|
|
49
|
+
OP_3DUP: number;
|
|
50
|
+
OP_2OVER: number;
|
|
51
|
+
OP_2ROT: number;
|
|
52
|
+
OP_2SWAP: number;
|
|
53
|
+
OP_IFDUP: number;
|
|
54
|
+
OP_DEPTH: number;
|
|
55
|
+
OP_DROP: number;
|
|
56
|
+
OP_DUP: number;
|
|
57
|
+
OP_NIP: number;
|
|
58
|
+
OP_OVER: number;
|
|
59
|
+
OP_PICK: number;
|
|
60
|
+
OP_ROLL: number;
|
|
61
|
+
OP_ROT: number;
|
|
62
|
+
OP_SWAP: number;
|
|
63
|
+
OP_TUCK: number;
|
|
64
|
+
OP_SUCCESS126: number;
|
|
65
|
+
OP_SUCCESS127: number;
|
|
66
|
+
OP_SUCCESS128: number;
|
|
67
|
+
OP_SUCCESS129: number;
|
|
68
|
+
OP_SIZE: number;
|
|
69
|
+
OP_SUCCESS131: number;
|
|
70
|
+
OP_SUCCESS132: number;
|
|
71
|
+
OP_SUCCESS133: number;
|
|
72
|
+
OP_SUCCESS134: number;
|
|
73
|
+
OP_EQUAL: number;
|
|
74
|
+
OP_EQUALVERIFY: number;
|
|
75
|
+
OP_SUCCESS137: number;
|
|
76
|
+
OP_SUCCESS138: number;
|
|
77
|
+
OP_1ADD: number;
|
|
78
|
+
OP_1SUB: number;
|
|
79
|
+
OP_SUCCESS141: number;
|
|
80
|
+
OP_SUCCESS142: number;
|
|
81
|
+
OP_NEGATE: number;
|
|
82
|
+
OP_ABS: number;
|
|
83
|
+
OP_NOT: number;
|
|
84
|
+
OP_0NOTEQUAL: number;
|
|
85
|
+
OP_ADD: number;
|
|
86
|
+
OP_SUB: number;
|
|
87
|
+
OP_SUCCESS149: number;
|
|
88
|
+
OP_SUCCESS150: number;
|
|
89
|
+
OP_SUCCESS151: number;
|
|
90
|
+
OP_SUCCESS152: number;
|
|
91
|
+
OP_SUCCESS153: number;
|
|
92
|
+
OP_BOOLAND: number;
|
|
93
|
+
OP_BOOLOR: number;
|
|
94
|
+
OP_NUMEQUAL: number;
|
|
95
|
+
OP_NUMEQUALVERIFY: number;
|
|
96
|
+
OP_NUMNOTEQUAL: number;
|
|
97
|
+
OP_LESSTHAN: number;
|
|
98
|
+
OP_GREATERTHAN: number;
|
|
99
|
+
OP_LESSTHANOREQUAL: number;
|
|
100
|
+
OP_GREATERTHANOREQUAL: number;
|
|
101
|
+
OP_MIN: number;
|
|
102
|
+
OP_MAX: number;
|
|
103
|
+
OP_WITHIN: number;
|
|
104
|
+
OP_RIPEMD160: number;
|
|
105
|
+
OP_SHA1: number;
|
|
106
|
+
OP_SHA256: number;
|
|
107
|
+
OP_HASH160: number;
|
|
108
|
+
OP_HASH256: number;
|
|
109
|
+
OP_CODESEPARATOR: number;
|
|
110
|
+
OP_CHECKSIG: number;
|
|
111
|
+
OP_CHECKSIGVERIFY: number;
|
|
112
|
+
OP_CHECKMULTISIG: number;
|
|
113
|
+
OP_CHECKMULTISIGVERIFY: number;
|
|
114
|
+
OP_NOP1: number;
|
|
115
|
+
OP_CHECKLOCKTIMEVERIFY: number;
|
|
116
|
+
OP_CHECKSEQUENCEVERIFY: number;
|
|
117
|
+
OP_NOP4: number;
|
|
118
|
+
OP_NOP5: number;
|
|
119
|
+
OP_NOP6: number;
|
|
120
|
+
OP_NOP7: number;
|
|
121
|
+
OP_NOP8: number;
|
|
122
|
+
OP_NOP9: number;
|
|
123
|
+
OP_NOP10: number;
|
|
124
|
+
OP_CHECKSIGADD: number;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OPCODE_MAP } from './words.js';
|
|
2
|
+
import { encode_script } from './encode.js';
|
|
3
|
+
import { decode_script, is_valid_script } from './decode.js';
|
|
4
|
+
import { parse_script_pubkeys, prefix_script_size } from './util.js';
|
|
5
|
+
export * from './decode.js';
|
|
6
|
+
export * from './encode.js';
|
|
7
|
+
export * from './util.js';
|
|
8
|
+
export * from './words.js';
|
|
9
|
+
export var ScriptUtil;
|
|
10
|
+
(function (ScriptUtil) {
|
|
11
|
+
ScriptUtil.prefix_size = prefix_script_size;
|
|
12
|
+
ScriptUtil.decode = decode_script;
|
|
13
|
+
ScriptUtil.encode = encode_script;
|
|
14
|
+
ScriptUtil.is_valid = is_valid_script;
|
|
15
|
+
ScriptUtil.get_pubkeys = parse_script_pubkeys;
|
|
16
|
+
ScriptUtil.OPCODES = OPCODE_MAP;
|
|
17
|
+
})(ScriptUtil || (ScriptUtil = {}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Buff } from '@vbyte/buff';
|
|
2
|
+
export function prefix_script_size(script) {
|
|
3
|
+
return Buff.bytes(script).prefix_varint('le').hex;
|
|
4
|
+
}
|
|
5
|
+
export function parse_script_pubkeys(script) {
|
|
6
|
+
const scriptHex = typeof script === 'string' ? script : Buff.bytes(script).hex;
|
|
7
|
+
const pubkeyPattern = /20([0-9a-f]{64})(ac|ad|ba)/gi;
|
|
8
|
+
const matches = [...scriptHex.matchAll(pubkeyPattern)];
|
|
9
|
+
return matches.map(match => match[1]);
|
|
10
|
+
}
|