@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.
Files changed (174) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +260 -3
  3. package/dist/const.d.ts +3 -0
  4. package/dist/const.js +23 -22
  5. package/dist/index.d.ts +11 -11
  6. package/dist/index.js +10 -10
  7. package/dist/lib/address/api.d.ts +2 -2
  8. package/dist/lib/address/api.js +12 -12
  9. package/dist/lib/address/encode.d.ts +1 -1
  10. package/dist/lib/address/encode.js +24 -24
  11. package/dist/lib/address/index.d.ts +6 -6
  12. package/dist/lib/address/index.js +6 -6
  13. package/dist/lib/address/p2pkh.d.ts +2 -2
  14. package/dist/lib/address/p2pkh.js +14 -14
  15. package/dist/lib/address/p2sh.d.ts +2 -2
  16. package/dist/lib/address/p2sh.js +13 -13
  17. package/dist/lib/address/p2tr.d.ts +2 -2
  18. package/dist/lib/address/p2tr.js +13 -13
  19. package/dist/lib/address/p2wpkh.d.ts +2 -2
  20. package/dist/lib/address/p2wpkh.js +14 -14
  21. package/dist/lib/address/p2wsh.d.ts +2 -2
  22. package/dist/lib/address/p2wsh.js +13 -13
  23. package/dist/lib/address/script.d.ts +1 -1
  24. package/dist/lib/address/script.js +16 -16
  25. package/dist/lib/address/util.d.ts +1 -1
  26. package/dist/lib/address/util.js +22 -22
  27. package/dist/lib/meta/index.d.ts +4 -4
  28. package/dist/lib/meta/index.js +4 -4
  29. package/dist/lib/meta/locktime.d.ts +1 -1
  30. package/dist/lib/meta/locktime.js +12 -12
  31. package/dist/lib/meta/ref.js +9 -6
  32. package/dist/lib/meta/scribe.d.ts +2 -2
  33. package/dist/lib/meta/scribe.js +48 -53
  34. package/dist/lib/meta/sequence.d.ts +1 -1
  35. package/dist/lib/meta/sequence.js +16 -15
  36. package/dist/lib/script/decode.d.ts +2 -2
  37. package/dist/lib/script/decode.js +50 -15
  38. package/dist/lib/script/encode.d.ts +1 -1
  39. package/dist/lib/script/encode.js +20 -16
  40. package/dist/lib/script/index.d.ts +5 -13
  41. package/dist/lib/script/index.js +5 -14
  42. package/dist/lib/script/lock.d.ts +2 -2
  43. package/dist/lib/script/lock.js +15 -12
  44. package/dist/lib/script/util.js +4 -4
  45. package/dist/lib/script/words.js +129 -129
  46. package/dist/lib/sighash/index.d.ts +3 -3
  47. package/dist/lib/sighash/index.js +3 -3
  48. package/dist/lib/sighash/segwit.d.ts +2 -2
  49. package/dist/lib/sighash/segwit.js +15 -14
  50. package/dist/lib/sighash/taproot.d.ts +2 -2
  51. package/dist/lib/sighash/taproot.js +24 -23
  52. package/dist/lib/sighash/util.d.ts +2 -2
  53. package/dist/lib/sighash/util.js +7 -7
  54. package/dist/lib/signer/index.d.ts +2 -2
  55. package/dist/lib/signer/index.js +2 -2
  56. package/dist/lib/signer/sign.d.ts +1 -1
  57. package/dist/lib/signer/sign.js +42 -7
  58. package/dist/lib/signer/verify.d.ts +17 -3
  59. package/dist/lib/signer/verify.js +233 -3
  60. package/dist/lib/taproot/cblock.d.ts +1 -1
  61. package/dist/lib/taproot/cblock.js +14 -16
  62. package/dist/lib/taproot/encode.d.ts +1 -1
  63. package/dist/lib/taproot/encode.js +7 -7
  64. package/dist/lib/taproot/index.d.ts +4 -4
  65. package/dist/lib/taproot/index.js +4 -4
  66. package/dist/lib/taproot/parse.d.ts +1 -1
  67. package/dist/lib/taproot/parse.js +12 -14
  68. package/dist/lib/taproot/tree.d.ts +2 -2
  69. package/dist/lib/taproot/tree.js +11 -7
  70. package/dist/lib/tx/create.d.ts +1 -1
  71. package/dist/lib/tx/create.js +28 -12
  72. package/dist/lib/tx/decode.d.ts +2 -2
  73. package/dist/lib/tx/decode.js +50 -17
  74. package/dist/lib/tx/encode.d.ts +2 -2
  75. package/dist/lib/tx/encode.js +13 -16
  76. package/dist/lib/tx/index.d.ts +7 -7
  77. package/dist/lib/tx/index.js +7 -7
  78. package/dist/lib/tx/parse.d.ts +1 -1
  79. package/dist/lib/tx/parse.js +9 -9
  80. package/dist/lib/tx/size.d.ts +2 -2
  81. package/dist/lib/tx/size.js +9 -9
  82. package/dist/lib/tx/util.d.ts +2 -2
  83. package/dist/lib/tx/util.js +23 -20
  84. package/dist/lib/tx/validate.d.ts +1 -1
  85. package/dist/lib/tx/validate.js +3 -3
  86. package/dist/lib/witness/index.d.ts +2 -2
  87. package/dist/lib/witness/index.js +2 -2
  88. package/dist/lib/witness/parse.d.ts +2 -2
  89. package/dist/lib/witness/parse.js +24 -23
  90. package/dist/lib/witness/util.d.ts +2 -2
  91. package/dist/lib/witness/util.js +5 -5
  92. package/dist/main.cjs +2308 -1005
  93. package/dist/main.cjs.map +1 -1
  94. package/dist/module.mjs +2308 -1005
  95. package/dist/module.mjs.map +1 -1
  96. package/dist/package.json +20 -17
  97. package/dist/schema/base.d.ts +1 -1
  98. package/dist/schema/base.js +17 -13
  99. package/dist/schema/index.d.ts +2 -2
  100. package/dist/schema/index.js +2 -2
  101. package/dist/schema/taproot.d.ts +1 -1
  102. package/dist/schema/taproot.js +2 -2
  103. package/dist/schema/tx.d.ts +1 -1
  104. package/dist/schema/tx.js +4 -4
  105. package/dist/script.js +8 -8
  106. package/dist/script.js.map +1 -1
  107. package/dist/types/address.d.ts +4 -4
  108. package/dist/types/index.d.ts +8 -8
  109. package/dist/types/index.js +8 -8
  110. package/dist/types/meta.d.ts +4 -4
  111. package/dist/types/psbt.d.ts +2 -2
  112. package/dist/types/script.d.ts +2 -2
  113. package/dist/types/sighash.d.ts +2 -2
  114. package/dist/types/witness.d.ts +5 -5
  115. package/package.json +20 -17
  116. package/src/const.ts +0 -61
  117. package/src/index.ts +0 -13
  118. package/src/lib/address/api.ts +0 -50
  119. package/src/lib/address/encode.ts +0 -183
  120. package/src/lib/address/index.ts +0 -7
  121. package/src/lib/address/p2pkh.ts +0 -94
  122. package/src/lib/address/p2sh.ts +0 -96
  123. package/src/lib/address/p2tr.ts +0 -91
  124. package/src/lib/address/p2wpkh.ts +0 -94
  125. package/src/lib/address/p2wsh.ts +0 -92
  126. package/src/lib/address/script.ts +0 -63
  127. package/src/lib/address/util.ts +0 -87
  128. package/src/lib/meta/index.ts +0 -4
  129. package/src/lib/meta/locktime.ts +0 -57
  130. package/src/lib/meta/ref.ts +0 -107
  131. package/src/lib/meta/scribe.ts +0 -256
  132. package/src/lib/meta/sequence.ts +0 -146
  133. package/src/lib/script/decode.ts +0 -85
  134. package/src/lib/script/encode.ts +0 -129
  135. package/src/lib/script/index.ts +0 -20
  136. package/src/lib/script/lock.ts +0 -73
  137. package/src/lib/script/util.ts +0 -78
  138. package/src/lib/script/words.ts +0 -182
  139. package/src/lib/sighash/index.ts +0 -3
  140. package/src/lib/sighash/segwit.ts +0 -152
  141. package/src/lib/sighash/taproot.ts +0 -206
  142. package/src/lib/sighash/util.ts +0 -51
  143. package/src/lib/signer/index.ts +0 -2
  144. package/src/lib/signer/sign.ts +0 -39
  145. package/src/lib/signer/verify.ts +0 -88
  146. package/src/lib/taproot/cblock.ts +0 -96
  147. package/src/lib/taproot/encode.ts +0 -49
  148. package/src/lib/taproot/index.ts +0 -4
  149. package/src/lib/taproot/parse.ts +0 -65
  150. package/src/lib/taproot/tree.ts +0 -94
  151. package/src/lib/tx/create.ts +0 -90
  152. package/src/lib/tx/decode.ts +0 -123
  153. package/src/lib/tx/encode.ts +0 -155
  154. package/src/lib/tx/index.ts +0 -7
  155. package/src/lib/tx/parse.ts +0 -69
  156. package/src/lib/tx/size.ts +0 -68
  157. package/src/lib/tx/util.ts +0 -111
  158. package/src/lib/tx/validate.ts +0 -49
  159. package/src/lib/witness/index.ts +0 -2
  160. package/src/lib/witness/parse.ts +0 -127
  161. package/src/lib/witness/util.ts +0 -18
  162. package/src/schema/base.ts +0 -57
  163. package/src/schema/index.ts +0 -2
  164. package/src/schema/taproot.ts +0 -12
  165. package/src/schema/tx.ts +0 -48
  166. package/src/types/address.ts +0 -35
  167. package/src/types/index.ts +0 -8
  168. package/src/types/meta.ts +0 -48
  169. package/src/types/psbt.ts +0 -15
  170. package/src/types/script.ts +0 -18
  171. package/src/types/sighash.ts +0 -16
  172. package/src/types/taproot.ts +0 -41
  173. package/src/types/txdata.ts +0 -85
  174. package/src/types/witness.ts +0 -42
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }