@vbyte/btc-dev 1.1.8 → 2.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/CHANGELOG.md +94 -0
- package/README.md +260 -3
- package/dist/const.d.ts +3 -0
- package/dist/const.js +23 -22
- package/dist/index.d.ts +11 -11
- package/dist/index.js +10 -10
- package/dist/lib/address/api.d.ts +2 -2
- package/dist/lib/address/api.js +12 -12
- package/dist/lib/address/encode.d.ts +1 -1
- package/dist/lib/address/encode.js +24 -24
- package/dist/lib/address/index.d.ts +6 -6
- package/dist/lib/address/index.js +6 -6
- package/dist/lib/address/p2pkh.d.ts +2 -2
- package/dist/lib/address/p2pkh.js +14 -14
- package/dist/lib/address/p2sh.d.ts +2 -2
- package/dist/lib/address/p2sh.js +13 -13
- package/dist/lib/address/p2tr.d.ts +2 -2
- package/dist/lib/address/p2tr.js +13 -13
- package/dist/lib/address/p2wpkh.d.ts +2 -2
- package/dist/lib/address/p2wpkh.js +14 -14
- package/dist/lib/address/p2wsh.d.ts +2 -2
- package/dist/lib/address/p2wsh.js +13 -13
- package/dist/lib/address/script.d.ts +1 -1
- package/dist/lib/address/script.js +16 -16
- package/dist/lib/address/util.d.ts +1 -1
- package/dist/lib/address/util.js +22 -22
- package/dist/lib/meta/index.d.ts +4 -4
- package/dist/lib/meta/index.js +4 -4
- package/dist/lib/meta/locktime.d.ts +1 -1
- package/dist/lib/meta/locktime.js +12 -12
- package/dist/lib/meta/ref.js +9 -6
- package/dist/lib/meta/scribe.d.ts +2 -2
- package/dist/lib/meta/scribe.js +48 -53
- package/dist/lib/meta/sequence.d.ts +1 -1
- package/dist/lib/meta/sequence.js +16 -15
- package/dist/lib/script/decode.d.ts +2 -2
- package/dist/lib/script/decode.js +50 -15
- package/dist/lib/script/encode.d.ts +1 -1
- package/dist/lib/script/encode.js +20 -16
- package/dist/lib/script/index.d.ts +5 -13
- package/dist/lib/script/index.js +5 -14
- package/dist/lib/script/lock.d.ts +2 -2
- package/dist/lib/script/lock.js +15 -12
- package/dist/lib/script/util.js +4 -4
- package/dist/lib/script/words.js +129 -129
- package/dist/lib/sighash/index.d.ts +3 -3
- package/dist/lib/sighash/index.js +3 -3
- package/dist/lib/sighash/segwit.d.ts +2 -2
- package/dist/lib/sighash/segwit.js +15 -14
- package/dist/lib/sighash/taproot.d.ts +2 -2
- package/dist/lib/sighash/taproot.js +24 -23
- package/dist/lib/sighash/util.d.ts +2 -2
- package/dist/lib/sighash/util.js +7 -7
- package/dist/lib/signer/index.d.ts +2 -2
- package/dist/lib/signer/index.js +2 -2
- package/dist/lib/signer/sign.d.ts +1 -1
- package/dist/lib/signer/sign.js +42 -7
- package/dist/lib/signer/verify.d.ts +17 -3
- package/dist/lib/signer/verify.js +233 -3
- package/dist/lib/taproot/cblock.d.ts +1 -1
- package/dist/lib/taproot/cblock.js +14 -16
- package/dist/lib/taproot/encode.d.ts +1 -1
- package/dist/lib/taproot/encode.js +7 -7
- package/dist/lib/taproot/index.d.ts +4 -4
- package/dist/lib/taproot/index.js +4 -4
- package/dist/lib/taproot/parse.d.ts +1 -1
- package/dist/lib/taproot/parse.js +12 -14
- package/dist/lib/taproot/tree.d.ts +2 -2
- package/dist/lib/taproot/tree.js +11 -7
- package/dist/lib/tx/create.d.ts +1 -1
- package/dist/lib/tx/create.js +28 -12
- package/dist/lib/tx/decode.d.ts +2 -2
- package/dist/lib/tx/decode.js +50 -17
- package/dist/lib/tx/encode.d.ts +2 -2
- package/dist/lib/tx/encode.js +13 -16
- package/dist/lib/tx/index.d.ts +7 -7
- package/dist/lib/tx/index.js +7 -7
- package/dist/lib/tx/parse.d.ts +1 -1
- package/dist/lib/tx/parse.js +9 -9
- package/dist/lib/tx/size.d.ts +2 -2
- package/dist/lib/tx/size.js +9 -9
- package/dist/lib/tx/util.d.ts +2 -2
- package/dist/lib/tx/util.js +23 -20
- package/dist/lib/tx/validate.d.ts +1 -1
- package/dist/lib/tx/validate.js +3 -3
- package/dist/lib/witness/index.d.ts +2 -2
- package/dist/lib/witness/index.js +2 -2
- package/dist/lib/witness/parse.d.ts +2 -2
- package/dist/lib/witness/parse.js +24 -23
- package/dist/lib/witness/util.d.ts +2 -2
- package/dist/lib/witness/util.js +5 -5
- package/dist/main.cjs +2308 -1005
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +2308 -1005
- package/dist/module.mjs.map +1 -1
- package/dist/package.json +20 -17
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/base.js +17 -13
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +2 -2
- package/dist/schema/taproot.d.ts +1 -1
- package/dist/schema/taproot.js +2 -2
- package/dist/schema/tx.d.ts +1 -1
- package/dist/schema/tx.js +4 -4
- package/dist/script.js +8 -8
- package/dist/script.js.map +1 -1
- package/dist/types/address.d.ts +4 -4
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.js +8 -8
- package/dist/types/meta.d.ts +4 -4
- package/dist/types/psbt.d.ts +2 -2
- package/dist/types/script.d.ts +2 -2
- package/dist/types/sighash.d.ts +2 -2
- package/dist/types/witness.d.ts +5 -5
- package/package.json +20 -17
- package/src/const.ts +0 -61
- package/src/index.ts +0 -13
- package/src/lib/address/api.ts +0 -50
- package/src/lib/address/encode.ts +0 -183
- package/src/lib/address/index.ts +0 -7
- package/src/lib/address/p2pkh.ts +0 -94
- package/src/lib/address/p2sh.ts +0 -96
- package/src/lib/address/p2tr.ts +0 -91
- package/src/lib/address/p2wpkh.ts +0 -94
- package/src/lib/address/p2wsh.ts +0 -92
- package/src/lib/address/script.ts +0 -63
- package/src/lib/address/util.ts +0 -87
- package/src/lib/meta/index.ts +0 -4
- package/src/lib/meta/locktime.ts +0 -57
- package/src/lib/meta/ref.ts +0 -107
- package/src/lib/meta/scribe.ts +0 -256
- package/src/lib/meta/sequence.ts +0 -146
- package/src/lib/script/decode.ts +0 -85
- package/src/lib/script/encode.ts +0 -129
- package/src/lib/script/index.ts +0 -20
- package/src/lib/script/lock.ts +0 -73
- package/src/lib/script/util.ts +0 -78
- package/src/lib/script/words.ts +0 -182
- package/src/lib/sighash/index.ts +0 -3
- package/src/lib/sighash/segwit.ts +0 -152
- package/src/lib/sighash/taproot.ts +0 -206
- package/src/lib/sighash/util.ts +0 -51
- package/src/lib/signer/index.ts +0 -2
- package/src/lib/signer/sign.ts +0 -39
- package/src/lib/signer/verify.ts +0 -88
- package/src/lib/taproot/cblock.ts +0 -96
- package/src/lib/taproot/encode.ts +0 -49
- package/src/lib/taproot/index.ts +0 -4
- package/src/lib/taproot/parse.ts +0 -65
- package/src/lib/taproot/tree.ts +0 -94
- package/src/lib/tx/create.ts +0 -90
- package/src/lib/tx/decode.ts +0 -123
- package/src/lib/tx/encode.ts +0 -155
- package/src/lib/tx/index.ts +0 -7
- package/src/lib/tx/parse.ts +0 -69
- package/src/lib/tx/size.ts +0 -68
- package/src/lib/tx/util.ts +0 -111
- package/src/lib/tx/validate.ts +0 -49
- package/src/lib/witness/index.ts +0 -2
- package/src/lib/witness/parse.ts +0 -127
- package/src/lib/witness/util.ts +0 -18
- package/src/schema/base.ts +0 -57
- package/src/schema/index.ts +0 -2
- package/src/schema/taproot.ts +0 -12
- package/src/schema/tx.ts +0 -48
- package/src/types/address.ts +0 -35
- package/src/types/index.ts +0 -8
- package/src/types/meta.ts +0 -48
- package/src/types/psbt.ts +0 -15
- package/src/types/script.ts +0 -18
- package/src/types/sighash.ts +0 -16
- package/src/types/taproot.ts +0 -41
- package/src/types/txdata.ts +0 -85
- package/src/types/witness.ts +0 -42
package/src/lib/meta/scribe.ts
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { Buff, Bytes, 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
|
-
|
|
6
|
-
import type { InscriptionData } from '@/types/index.js'
|
|
7
|
-
|
|
8
|
-
const _0n = BigInt(0)
|
|
9
|
-
const _1n = BigInt(1)
|
|
10
|
-
const _26n = BigInt(26)
|
|
11
|
-
|
|
12
|
-
export namespace InscriptionUtil {
|
|
13
|
-
export type Type = InscriptionData
|
|
14
|
-
export const encode = encode_inscription
|
|
15
|
-
export const decode = decode_inscription
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function decode_inscription (
|
|
19
|
-
script : Bytes
|
|
20
|
-
) : InscriptionData[] {
|
|
21
|
-
const envelopes = parse_envelopes(script)
|
|
22
|
-
return envelopes.map(parse_record)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function encode_inscription (data : InscriptionData[]) : Buff {
|
|
26
|
-
return Buff.join(data.map(create_envelope))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function create_envelope (data : InscriptionData) : Buff {
|
|
30
|
-
let asm : string[] = [ 'OP_0', 'OP_IF', '6f7264' ]
|
|
31
|
-
|
|
32
|
-
if (typeof data.delegate === 'string') {
|
|
33
|
-
const id = encode_id(data.delegate)
|
|
34
|
-
asm.push('OP_11', id)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (typeof data.ref === 'string') {
|
|
38
|
-
asm.push('OP_WITHIN', data.ref)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (typeof data.parent === 'string') {
|
|
42
|
-
const id = encode_id(data.parent)
|
|
43
|
-
asm.push('OP_3', id)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (typeof data.opcode === 'number') {
|
|
47
|
-
const code = encode_pointer(data.opcode)
|
|
48
|
-
asm.push('OP_NOP', code)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (typeof data.pointer === 'number') {
|
|
52
|
-
const ptr = encode_pointer(data.pointer)
|
|
53
|
-
asm.push('OP_2', ptr)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (typeof data.rune === 'string') {
|
|
57
|
-
const label = encode_rune_label(data.rune)
|
|
58
|
-
asm.push('OP_13', label)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (typeof data.mimetype === 'string') {
|
|
62
|
-
const label = encode_label(data.mimetype)
|
|
63
|
-
asm.push('OP_1', label)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (typeof data.content === 'string') {
|
|
67
|
-
const chunks = encode_content(data.content)
|
|
68
|
-
asm.push('OP_0', ...chunks)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
asm.push('OP_ENDIF')
|
|
72
|
-
|
|
73
|
-
return encode_script(asm)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function parse_envelopes (
|
|
77
|
-
script : Bytes
|
|
78
|
-
) : string[][] {
|
|
79
|
-
|
|
80
|
-
const words = decode_script(script)
|
|
81
|
-
const start_idx = words.findIndex(e => e === 'OP_0')
|
|
82
|
-
|
|
83
|
-
Assert.ok(start_idx !== -1, 'inscription envelope not found')
|
|
84
|
-
|
|
85
|
-
const envelopes = []
|
|
86
|
-
|
|
87
|
-
for (let idx = start_idx; idx < words.length; idx++) {
|
|
88
|
-
Assert.ok(words[idx + 1] === 'OP_IF', 'OP_IF missing from envelope')
|
|
89
|
-
Assert.ok(words[idx + 2] === '6f7264', 'magic bytes missing from envelope')
|
|
90
|
-
|
|
91
|
-
const stop_idx = words.findIndex(e => e === 'OP_ENDIF')
|
|
92
|
-
Assert.ok(stop_idx !== -1, 'inscription envelope missing END_IF statement')
|
|
93
|
-
|
|
94
|
-
const env = words.slice(idx + 3, stop_idx)
|
|
95
|
-
envelopes.push(env)
|
|
96
|
-
idx += stop_idx
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return envelopes
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function parse_record (envelope : Bytes[]) {
|
|
103
|
-
const record : InscriptionData = {}
|
|
104
|
-
|
|
105
|
-
for (let i = 0; i < envelope.length; i++) {
|
|
106
|
-
switch (envelope[i]) {
|
|
107
|
-
case 'OP_1':
|
|
108
|
-
record.mimetype = decode_label(envelope[i+1])
|
|
109
|
-
i += 1
|
|
110
|
-
break
|
|
111
|
-
case 'OP_2':
|
|
112
|
-
record.pointer = decode_pointer(envelope[i+1])
|
|
113
|
-
i += 1
|
|
114
|
-
break
|
|
115
|
-
case 'OP_3':
|
|
116
|
-
record.parent = decode_id(envelope[i+1])
|
|
117
|
-
i += 1
|
|
118
|
-
break
|
|
119
|
-
case 'OP_11':
|
|
120
|
-
record.delegate = decode_id(envelope[i+1])
|
|
121
|
-
i += 1
|
|
122
|
-
break
|
|
123
|
-
case 'OP_13':
|
|
124
|
-
record.rune = decode_rune_label(envelope[i+1])
|
|
125
|
-
i += 1
|
|
126
|
-
break
|
|
127
|
-
case 'OP_WITHIN':
|
|
128
|
-
record.ref = decode_bytes(envelope[i+1])
|
|
129
|
-
i += 1
|
|
130
|
-
break;
|
|
131
|
-
case 'OP_NOP':
|
|
132
|
-
record.opcode = decode_pointer(envelope[i+1])
|
|
133
|
-
i += 1
|
|
134
|
-
break;
|
|
135
|
-
case 'OP_0':
|
|
136
|
-
record.content = decode_content(envelope.slice(i+1))
|
|
137
|
-
return record
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return record
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function decode_bytes (bytes : Bytes) : string {
|
|
144
|
-
return Buff.bytes(bytes).hex
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function encode_id (
|
|
148
|
-
identifier : string
|
|
149
|
-
) : string {
|
|
150
|
-
Assert.ok(identifier.includes('i'), 'identifier must include an index')
|
|
151
|
-
const parts = identifier.split('i')
|
|
152
|
-
const bytes = Buff.hex(parts[0])
|
|
153
|
-
const idx = Number(parts[1])
|
|
154
|
-
const txid = bytes.reverse().hex
|
|
155
|
-
return (idx !== 0) ? txid + Buff.num(idx).hex : txid
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function decode_id (
|
|
159
|
-
identifier : Bytes
|
|
160
|
-
) : string {
|
|
161
|
-
const bytes = Buff.bytes(identifier)
|
|
162
|
-
const idx = bytes.at(-1) ?? 0
|
|
163
|
-
const txid = bytes.slice(0, -1).reverse().hex
|
|
164
|
-
return txid + 'i' + String(idx)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function encode_pointer (
|
|
168
|
-
pointer : number
|
|
169
|
-
) : string {
|
|
170
|
-
return Buff.num(pointer).reverse().hex
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function decode_pointer (
|
|
174
|
-
bytes : Bytes
|
|
175
|
-
) : number {
|
|
176
|
-
return Buff.bytes(bytes).reverse().num
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function encode_label (
|
|
180
|
-
label : string
|
|
181
|
-
) : string {
|
|
182
|
-
return Buff.str(label).hex
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function decode_label (
|
|
186
|
-
label : Bytes
|
|
187
|
-
) : string {
|
|
188
|
-
return Buff.bytes(label).str
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function encode_content (
|
|
192
|
-
content : string
|
|
193
|
-
) : string[] {
|
|
194
|
-
const bytes = Buff.is_hex(content)
|
|
195
|
-
? Buff.hex(content)
|
|
196
|
-
: Buff.str(content)
|
|
197
|
-
const stream = new Stream(bytes)
|
|
198
|
-
const chunks : string[]= []
|
|
199
|
-
while (stream.size > 0) {
|
|
200
|
-
if (stream.size > 520) {
|
|
201
|
-
const chunk = stream.read(520)
|
|
202
|
-
chunks.push(chunk.hex)
|
|
203
|
-
} else {
|
|
204
|
-
const chunk = stream.read(stream.size)
|
|
205
|
-
chunks.push(chunk.hex)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return chunks
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function decode_content (
|
|
212
|
-
chunks : Bytes[],
|
|
213
|
-
format : 'hex' | 'utf8' = 'hex'
|
|
214
|
-
) : string {
|
|
215
|
-
const data = Buff.join(chunks)
|
|
216
|
-
return (format === 'hex')
|
|
217
|
-
? data.hex
|
|
218
|
-
: data.str
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function encode_rune_label (label : string) : string {
|
|
222
|
-
const str = label.toUpperCase()
|
|
223
|
-
let big = _0n
|
|
224
|
-
for (const char of str) {
|
|
225
|
-
if (char >= 'A' && char <= 'Z') {
|
|
226
|
-
big = big * _26n + BigInt(char.charCodeAt(0) - ('A'.charCodeAt(0) - 1))
|
|
227
|
-
} else { continue }
|
|
228
|
-
}
|
|
229
|
-
big = big - _1n
|
|
230
|
-
return Buff.big(big).reverse().hex
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function decode_rune_label (label: Bytes): string {
|
|
234
|
-
// Convert hex to BigInt, with byte order reversed
|
|
235
|
-
let big = Buff.bytes(label).reverse().big
|
|
236
|
-
// Add 1 as per the encoding algorithm
|
|
237
|
-
big = big + _1n
|
|
238
|
-
// Initialize result string
|
|
239
|
-
let result = ''
|
|
240
|
-
// Convert the BigInt back to a string of alphabet characters
|
|
241
|
-
while (big > _0n) {
|
|
242
|
-
// Get remainder after division by 26
|
|
243
|
-
const mod = big % _26n
|
|
244
|
-
// Convert remainder to character (0 maps to 'Z', 1 to 'A', 2 to 'B', etc.)
|
|
245
|
-
if (mod === _0n) {
|
|
246
|
-
result = 'Z' + result
|
|
247
|
-
big = big / _26n - _1n // Adjust for special case of 'Z'
|
|
248
|
-
} else {
|
|
249
|
-
// Map 1 to 'A', 2 to 'B', etc.
|
|
250
|
-
const charCode = Number(mod) + 'A'.charCodeAt(0) - 1
|
|
251
|
-
result = String.fromCharCode(charCode) + result
|
|
252
|
-
big = big / _26n
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return result
|
|
256
|
-
}
|
package/src/lib/meta/sequence.ts
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bitcoin Transaction Sequence Field Manipulation
|
|
3
|
-
*
|
|
4
|
-
* This module provides functionality for encoding and decoding the sequence field in Bitcoin transactions.
|
|
5
|
-
* The sequence field is a 32-bit integer that can be used for various purposes:
|
|
6
|
-
*
|
|
7
|
-
* 1. Relative timelocks (BIP-68).
|
|
8
|
-
* 2. Custom protocol data.
|
|
9
|
-
*
|
|
10
|
-
* The implementation follows BIP-68 for timelock functionality, and extends it with a custom protocol
|
|
11
|
-
* that allows additional metadata to be encoded in the sequence field (to be used by on-chain indexers).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { SequenceConfig, SequenceData } from '@/types/index.js'
|
|
15
|
-
|
|
16
|
-
/* ===== [ Constants ] ===================================================== */
|
|
17
|
-
|
|
18
|
-
const TIMELOCK_DISABLE = 0x80000000 // Bit 31: When set, disables relative timelock per BIP-68.
|
|
19
|
-
const TIMELOCK_TYPE = 0x00400000 // Bit 22: When set, indicates timestamp-based lock; when clear, indicates block-height-based lock.
|
|
20
|
-
const TIMELOCK_VALUE_MASK = 0x0000FFFF // Bits 0-15: Mask for extracting timelock value (16 bits).
|
|
21
|
-
const TIMELOCK_VALUE_MAX = 0xFFFF // Maximum value for timelock (2^16 - 1).
|
|
22
|
-
const TIMELOCK_GRANULARITY = 512 // Seconds per timestamp unit (BIP-68 specification).
|
|
23
|
-
|
|
24
|
-
/* ===== [ API ] ============================================================ */
|
|
25
|
-
|
|
26
|
-
export namespace SequenceField {
|
|
27
|
-
export const encode = encode_sequence
|
|
28
|
-
export const decode = decode_sequence
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/* ===== [ Encoder ] ======================================================== */
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Encodes a SequenceData object into a 32-bit integer sequence value
|
|
35
|
-
*
|
|
36
|
-
* @param data - The sequence data to encode
|
|
37
|
-
* @returns A 32-bit integer representing the encoded sequence
|
|
38
|
-
* @throws Error if the input data is invalid or exceeds maximum values
|
|
39
|
-
*/
|
|
40
|
-
export function encode_sequence (data : SequenceConfig): number {
|
|
41
|
-
// If the timelock is based on a block height,
|
|
42
|
-
if (data.mode === 'height') {
|
|
43
|
-
// Validate the height value.
|
|
44
|
-
const height = parse_height(data.height)
|
|
45
|
-
// For heightlock, only encode the height value (TIMELOCK_TYPE bit remains clear)
|
|
46
|
-
return (height & TIMELOCK_VALUE_MASK) >>> 0
|
|
47
|
-
}
|
|
48
|
-
// If the timelock is based on a timestamp,
|
|
49
|
-
if (data.mode === 'stamp') {
|
|
50
|
-
// Convert timestamp to 512-second granularity units as per BIP-68.
|
|
51
|
-
const stamp = parse_stamp(data.stamp)
|
|
52
|
-
// Set the TIMELOCK_TYPE bit and encode the timestamp value.
|
|
53
|
-
return (TIMELOCK_TYPE | (stamp & TIMELOCK_VALUE_MASK)) >>> 0
|
|
54
|
-
}
|
|
55
|
-
// Throw an error if the mode is unrecognized.
|
|
56
|
-
throw new Error('invalid timelock mode: ' + data.mode)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/* ===== [ Decoder ] ========================================================= */
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Decodes a 32-bit sequence value into a SequenceData object
|
|
63
|
-
*
|
|
64
|
-
* @param sequence - The 32-bit sequence value to decode
|
|
65
|
-
* @returns A SequenceData object or null if the sequence doesn't represent special data
|
|
66
|
-
* @throws Error if the sequence value is invalid or exceeds maximum values
|
|
67
|
-
*/
|
|
68
|
-
export function decode_sequence (sequence: number | string) : SequenceData | null {
|
|
69
|
-
// Parse and validate the sequence value.
|
|
70
|
-
const seq = parse_sequence(sequence)
|
|
71
|
-
// If the sequence is disabled, return null.
|
|
72
|
-
if (seq & TIMELOCK_DISABLE) return null
|
|
73
|
-
// Extract the value.
|
|
74
|
-
const value = seq & TIMELOCK_VALUE_MASK
|
|
75
|
-
// Check for timestamp-based lock (TIMELOCK_TYPE bit is set).
|
|
76
|
-
if (seq & TIMELOCK_TYPE) {
|
|
77
|
-
// Convert granularity units back to seconds for timestamp.
|
|
78
|
-
const stamp = value * TIMELOCK_GRANULARITY
|
|
79
|
-
// Validate the timestamp value.
|
|
80
|
-
if (stamp > 0xFFFFFFFF) {
|
|
81
|
-
throw new Error('Decoded timestamp exceeds 32-bit limit')
|
|
82
|
-
}
|
|
83
|
-
// Return the decoded timelock.
|
|
84
|
-
return { mode: 'stamp', stamp }
|
|
85
|
-
} else {
|
|
86
|
-
// Validate the height value.
|
|
87
|
-
if (value > TIMELOCK_VALUE_MAX) {
|
|
88
|
-
throw new Error('Decoded height exceeds maximum')
|
|
89
|
-
}
|
|
90
|
-
// Return the decoded heightlock.
|
|
91
|
-
return { mode: 'height', height: value }
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/* ===== [ Helpers ] ========================================================= */
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Parses a sequence value into a number.
|
|
99
|
-
*
|
|
100
|
-
* @param sequence - The sequence value to parse.
|
|
101
|
-
* @returns The parsed sequence value.
|
|
102
|
-
* @throws Error if the sequence value is invalid.
|
|
103
|
-
*/
|
|
104
|
-
function parse_sequence (sequence: number | string): number {
|
|
105
|
-
const seq = (typeof sequence === 'string')
|
|
106
|
-
? parseInt(sequence, 16)
|
|
107
|
-
: sequence
|
|
108
|
-
if (!Number.isInteger(seq) || seq < 0 || seq > 0xFFFFFFFF) {
|
|
109
|
-
throw new Error(`invalid sequence value: ${seq}`)
|
|
110
|
-
}
|
|
111
|
-
return seq
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Parses a timestamp value into a 512-second granularity units.
|
|
116
|
-
*
|
|
117
|
-
* @param stamp - The timestamp value to parse.
|
|
118
|
-
* @returns The parsed timestamp value.
|
|
119
|
-
* @throws Error if the timestamp value is invalid.
|
|
120
|
-
*/
|
|
121
|
-
function parse_stamp (stamp? : number) : number {
|
|
122
|
-
if (stamp === undefined || !Number.isInteger(stamp)) {
|
|
123
|
-
throw new Error(`timestamp must be a number`)
|
|
124
|
-
}
|
|
125
|
-
// Convert timestamp to 512-second granularity units as per BIP-68.
|
|
126
|
-
const ts = Math.floor(stamp / TIMELOCK_GRANULARITY)
|
|
127
|
-
// Validate the timestamp value.
|
|
128
|
-
if (!Number.isInteger(ts) || ts < 0 || ts > TIMELOCK_VALUE_MAX) {
|
|
129
|
-
throw new Error(`timelock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX} (in 512-second increments)`)
|
|
130
|
-
}
|
|
131
|
-
return ts
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Parses a height value into a number.
|
|
136
|
-
*
|
|
137
|
-
* @param height - The height value to parse.
|
|
138
|
-
* @returns The parsed height value.
|
|
139
|
-
* @throws Error if the height value is invalid.
|
|
140
|
-
*/
|
|
141
|
-
function parse_height (height? : number) : number {
|
|
142
|
-
if (height === undefined || !Number.isInteger(height) || height < 0 || height > TIMELOCK_VALUE_MAX) {
|
|
143
|
-
throw new Error(`Heightlock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX}`)
|
|
144
|
-
}
|
|
145
|
-
return height
|
|
146
|
-
}
|
package/src/lib/script/decode.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Buff, Bytes, Stream } from '@vbyte/buff'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
get_op_code,
|
|
5
|
-
get_op_type,
|
|
6
|
-
is_valid_op
|
|
7
|
-
} from './words.js'
|
|
8
|
-
|
|
9
|
-
import type { ScriptInfo } from '@/types/script.js'
|
|
10
|
-
|
|
11
|
-
export function parse_script (script: Bytes): ScriptInfo {
|
|
12
|
-
const bytes = Buff.bytes(script)
|
|
13
|
-
return {
|
|
14
|
-
asm: decode_script(bytes),
|
|
15
|
-
hex: bytes.hex
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Decode a bitcoin script into asm instructions.
|
|
21
|
-
*/
|
|
22
|
-
export function decode_script (
|
|
23
|
-
script : Bytes
|
|
24
|
-
) : string[] {
|
|
25
|
-
const stream = new Stream(script)
|
|
26
|
-
|
|
27
|
-
const stack : string[] = []
|
|
28
|
-
const stack_size = stream.size
|
|
29
|
-
|
|
30
|
-
let word : number
|
|
31
|
-
let word_type : string
|
|
32
|
-
let word_size : number
|
|
33
|
-
|
|
34
|
-
let count = 0
|
|
35
|
-
|
|
36
|
-
while (count < stack_size) {
|
|
37
|
-
word = stream.read(1).num
|
|
38
|
-
word_type = get_op_type(word)
|
|
39
|
-
count++
|
|
40
|
-
switch (word_type) {
|
|
41
|
-
case 'varint':
|
|
42
|
-
stack.push(stream.read(word).hex)
|
|
43
|
-
count += word
|
|
44
|
-
break
|
|
45
|
-
case 'pushdata1':
|
|
46
|
-
word_size = stream.read(1).reverse().num
|
|
47
|
-
stack.push(stream.read(word_size).hex)
|
|
48
|
-
count += word_size + 1
|
|
49
|
-
break
|
|
50
|
-
case 'pushdata2':
|
|
51
|
-
word_size = stream.read(2).reverse().num
|
|
52
|
-
stack.push(stream.read(word_size).hex)
|
|
53
|
-
count += word_size + 2
|
|
54
|
-
break
|
|
55
|
-
case 'pushdata4':
|
|
56
|
-
word_size = stream.read(4).reverse().num
|
|
57
|
-
stack.push(stream.read(word_size).hex)
|
|
58
|
-
count += word_size + 4
|
|
59
|
-
break
|
|
60
|
-
case 'opcode':
|
|
61
|
-
if (!is_valid_op(word)) {
|
|
62
|
-
throw new Error(`Invalid OPCODE: ${word}`)
|
|
63
|
-
}
|
|
64
|
-
stack.push(get_op_code(word))
|
|
65
|
-
break
|
|
66
|
-
default:
|
|
67
|
-
throw new Error(`Word type undefined: ${word}`)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return stack
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Check if a script is valid.
|
|
75
|
-
*/
|
|
76
|
-
export function is_valid_script (
|
|
77
|
-
script : string | Uint8Array
|
|
78
|
-
) : boolean {
|
|
79
|
-
try {
|
|
80
|
-
const stack = decode_script(script)
|
|
81
|
-
return stack.length > 0
|
|
82
|
-
} catch {
|
|
83
|
-
return false
|
|
84
|
-
}
|
|
85
|
-
}
|
package/src/lib/script/encode.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { Buff, Stream } from '@vbyte/buff'
|
|
2
|
-
import { get_asm_code, } from './words.js'
|
|
3
|
-
|
|
4
|
-
// The maximum size of a word in bytes.
|
|
5
|
-
const MAX_WORD_SIZE = 520
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Encode script asm instructions into a hex string.
|
|
9
|
-
*/
|
|
10
|
-
export function encode_script (
|
|
11
|
-
words : (string | number | Uint8Array)[],
|
|
12
|
-
varint = false
|
|
13
|
-
) : Buff {
|
|
14
|
-
if (words.length === 0) return Buff.num(0, 1)
|
|
15
|
-
|
|
16
|
-
const bytes = []
|
|
17
|
-
|
|
18
|
-
for (const word of words) {
|
|
19
|
-
|
|
20
|
-
bytes.push(encode_script_word(word))
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const buffer = Buff.join(bytes)
|
|
24
|
-
|
|
25
|
-
return (varint)
|
|
26
|
-
? buffer.prepend(Buff.varint(buffer.length, 'le'))
|
|
27
|
-
: buffer
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Check if the word is a valid opcode,
|
|
31
|
-
* and return its integer value.
|
|
32
|
-
*/
|
|
33
|
-
export function encode_script_word (word : string | number | Uint8Array) : Uint8Array {
|
|
34
|
-
let buff : Buff
|
|
35
|
-
|
|
36
|
-
// If word is a string:
|
|
37
|
-
if (typeof (word) === 'string') {
|
|
38
|
-
// If word is an opcode:
|
|
39
|
-
if (word.startsWith('OP_')) {
|
|
40
|
-
// Get the opcode number value.
|
|
41
|
-
const asm_code = get_asm_code(word)
|
|
42
|
-
// Return the opcode as a single byte.
|
|
43
|
-
return Buff.num(asm_code, 1)
|
|
44
|
-
// If word is valid hex:
|
|
45
|
-
} else if (Buff.is_hex(word)) {
|
|
46
|
-
// Encode as hex.
|
|
47
|
-
buff = Buff.hex(word)
|
|
48
|
-
} else {
|
|
49
|
-
// Encode as UTF8 string.
|
|
50
|
-
buff = Buff.str(word)
|
|
51
|
-
}
|
|
52
|
-
// If word is a number:
|
|
53
|
-
} else if (typeof (word) === 'number') {
|
|
54
|
-
// Encode the number value.
|
|
55
|
-
buff = Buff.num(word)
|
|
56
|
-
// If word is a Uint8Array:
|
|
57
|
-
} else if (word instanceof Uint8Array) {
|
|
58
|
-
// Encode as bytes.
|
|
59
|
-
buff = new Buff(word)
|
|
60
|
-
} else {
|
|
61
|
-
// If word is not a string, number, or Uint8Array, throw an error.
|
|
62
|
-
throw new Error('invalid word type:' + typeof (word))
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Format and return the word based on its size.
|
|
66
|
-
if (buff.length === 1 && buff[0] <= 16) {
|
|
67
|
-
// Number values 0-16 must be treated as opcodes.
|
|
68
|
-
if (buff[0] !== 0) buff[0] += 0x50
|
|
69
|
-
} else if (buff.length > MAX_WORD_SIZE) {
|
|
70
|
-
// Number values larger than max size must be split into chunks.
|
|
71
|
-
let words : Buff[]
|
|
72
|
-
// Split bytes into chunks, based on max word size.
|
|
73
|
-
words = split_script_word(buff)
|
|
74
|
-
// Prefix a varint length byte for each chunk.
|
|
75
|
-
words = words.map(e => prefix_word_size(e))
|
|
76
|
-
// Concatenate the chunks
|
|
77
|
-
buff = Buff.join(words)
|
|
78
|
-
} else {
|
|
79
|
-
// Else, return the word with a varint prefix.
|
|
80
|
-
buff = prefix_word_size(buff)
|
|
81
|
-
}
|
|
82
|
-
// Return the final result.
|
|
83
|
-
return buff
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Split a word into smaller chunks.
|
|
88
|
-
*/
|
|
89
|
-
export function split_script_word (
|
|
90
|
-
word : Uint8Array
|
|
91
|
-
) : Buff[] {
|
|
92
|
-
const words = []
|
|
93
|
-
const buff = new Stream(word)
|
|
94
|
-
while (buff.size > MAX_WORD_SIZE) {
|
|
95
|
-
// Push a word chunk to the array.
|
|
96
|
-
words.push(buff.read(MAX_WORD_SIZE))
|
|
97
|
-
}
|
|
98
|
-
// Push the remainder to the array.
|
|
99
|
-
words.push(buff.read(buff.size))
|
|
100
|
-
return words
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Prefix a word with its size, encoded as a varint.
|
|
105
|
-
*/
|
|
106
|
-
export function prefix_word_size (
|
|
107
|
-
word : Uint8Array
|
|
108
|
-
) : Buff {
|
|
109
|
-
const varint = get_size_varint(word.length)
|
|
110
|
-
return Buff.join([ varint, word ])
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Return a varint that encodes a size value.
|
|
115
|
-
*/
|
|
116
|
-
export function get_size_varint (size : number) : Buff {
|
|
117
|
-
const OP_PUSHDATA1 = Buff.num(0x4c, 1)
|
|
118
|
-
const OP_PUSHDATA2 = Buff.num(0x4d, 1)
|
|
119
|
-
switch (true) {
|
|
120
|
-
case (size <= 0x4b):
|
|
121
|
-
return Buff.num(size)
|
|
122
|
-
case (size > 0x4b && size < 0x100):
|
|
123
|
-
return Buff.join([ OP_PUSHDATA1, Buff.num(size, 1, 'le') ])
|
|
124
|
-
case (size >= 0x100 && size <= MAX_WORD_SIZE):
|
|
125
|
-
return Buff.join([ OP_PUSHDATA2, Buff.num(size, 2, 'le') ])
|
|
126
|
-
default:
|
|
127
|
-
throw new Error('Invalid word size:' + size.toString())
|
|
128
|
-
}
|
|
129
|
-
}
|
package/src/lib/script/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { encode_script } from './encode.js'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
decode_script,
|
|
5
|
-
is_valid_script,
|
|
6
|
-
parse_script
|
|
7
|
-
} from './decode.js'
|
|
8
|
-
|
|
9
|
-
export * from './decode.js'
|
|
10
|
-
export * from './encode.js'
|
|
11
|
-
export * from './lock.js'
|
|
12
|
-
export * from './util.js'
|
|
13
|
-
export * from './words.js'
|
|
14
|
-
|
|
15
|
-
export namespace ScriptUtil {
|
|
16
|
-
export const parse = parse_script
|
|
17
|
-
export const decode = decode_script
|
|
18
|
-
export const encode = encode_script
|
|
19
|
-
export const is_valid = is_valid_script
|
|
20
|
-
}
|
package/src/lib/script/lock.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Buff, Bytes } from '@vbyte/buff'
|
|
2
|
-
import { LOCK_SCRIPT_REGEX } from '@/const.js'
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
LockScriptInfo,
|
|
6
|
-
LockScriptType,
|
|
7
|
-
WitnessVersion
|
|
8
|
-
} from '@/types/index.js'
|
|
9
|
-
|
|
10
|
-
export function is_return_script (script : Bytes) : boolean {
|
|
11
|
-
const bytes = Buff.bytes(script)
|
|
12
|
-
return bytes.at(0) === 0x6a
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function get_lock_script_info (script : Bytes) : LockScriptInfo {
|
|
16
|
-
return {
|
|
17
|
-
type : get_lock_script_type(script),
|
|
18
|
-
version : get_lock_script_version(script)
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function get_lock_script_type (script : Bytes) : LockScriptType | null {
|
|
23
|
-
// Get the hex string of the script.
|
|
24
|
-
const hex = Buff.bytes(script).hex
|
|
25
|
-
// Iterate over the lock script regexes.
|
|
26
|
-
for (const [ type, regex ] of Object.entries(LOCK_SCRIPT_REGEX)) {
|
|
27
|
-
// If the script matches the regex, return the type.
|
|
28
|
-
if (regex.test(hex)) return type as LockScriptType
|
|
29
|
-
}
|
|
30
|
-
// If the script does not match any regex, return null.
|
|
31
|
-
return null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function get_lock_script_version (script : Bytes) : WitnessVersion | null {
|
|
35
|
-
// Get the version of the script.
|
|
36
|
-
const version = Buff.bytes(script)
|
|
37
|
-
// Return the version of the script.
|
|
38
|
-
switch (version.at(0)) {
|
|
39
|
-
case 0x00 : return 0
|
|
40
|
-
case 0x51 : return 1
|
|
41
|
-
default : return null
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function is_p2pkh_script (script : Bytes) : boolean {
|
|
46
|
-
const hex = Buff.bytes(script).hex
|
|
47
|
-
return LOCK_SCRIPT_REGEX['p2pkh'].test(hex)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function is_p2sh_script (script : Bytes) : boolean {
|
|
51
|
-
const hex = Buff.bytes(script).hex
|
|
52
|
-
return LOCK_SCRIPT_REGEX['p2sh'].test(hex)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function is_p2wpkh_script (script : Bytes) : boolean {
|
|
56
|
-
const hex = Buff.bytes(script).hex
|
|
57
|
-
return LOCK_SCRIPT_REGEX['p2wpkh'].test(hex)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function is_p2wsh_script (script : Bytes) : boolean {
|
|
61
|
-
const hex = Buff.bytes(script).hex
|
|
62
|
-
return LOCK_SCRIPT_REGEX['p2wsh'].test(hex)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function is_p2tr_script (script : Bytes) : boolean {
|
|
66
|
-
const hex = Buff.bytes(script).hex
|
|
67
|
-
return LOCK_SCRIPT_REGEX['p2tr'].test(hex)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function is_opreturn_script (script : Bytes) : boolean {
|
|
71
|
-
const hex = Buff.bytes(script).hex
|
|
72
|
-
return LOCK_SCRIPT_REGEX['opreturn'].test(hex)
|
|
73
|
-
}
|