1
1
'use strict' ;
2
+ /**
3
+ * Script tools module for working with Bitcoin scripts.
4
+ * Provides utilities such as decompiling, compiling, converting to/from ASM, stack manipulation,
5
+ * and script validation functions.
6
+ *
7
+ * @packageDocumentation
8
+ */
2
9
var __createBinding =
3
10
( this && this . __createBinding ) ||
4
11
( Object . create
@@ -55,10 +62,6 @@ exports.toStack = toStack;
55
62
exports . isCanonicalPubKey = isCanonicalPubKey ;
56
63
exports . isDefinedHashType = isDefinedHashType ;
57
64
exports . isCanonicalScriptSignature = isCanonicalScriptSignature ;
58
- /**
59
- * Script tools, including decompile, compile, toASM, fromASM, toStack, isCanonicalPubKey, isCanonicalScriptSignature
60
- * @packageDocumentation
61
- */
62
65
const bip66 = __importStar ( require ( './bip66.cjs' ) ) ;
63
66
const ops_js_1 = require ( './ops.cjs' ) ;
64
67
Object . defineProperty ( exports , 'OPS' , {
@@ -73,8 +76,16 @@ const scriptSignature = __importStar(require('./script_signature.cjs'));
73
76
const types = __importStar ( require ( './types.cjs' ) ) ;
74
77
const tools = __importStar ( require ( 'uint8array-tools' ) ) ;
75
78
const v = __importStar ( require ( 'valibot' ) ) ;
79
+ /** Base opcode for OP_INT values. */
76
80
const OP_INT_BASE = ops_js_1 . OPS . OP_RESERVED ; // OP_1 - 1
81
+ /** Validation schema for a Bitcoin script stack. */
77
82
const StackSchema = v . array ( v . union ( [ v . instance ( Uint8Array ) , v . number ( ) ] ) ) ;
83
+ /**
84
+ * Determines if a value corresponds to an OP_INT opcode.
85
+ *
86
+ * @param value - The opcode to check.
87
+ * @returns True if the value is an OP_INT, false otherwise.
88
+ */
78
89
function isOPInt ( value ) {
79
90
return (
80
91
v . is ( v . number ( ) , value ) &&
@@ -83,57 +94,95 @@ function isOPInt(value) {
83
94
value === ops_js_1 . OPS . OP_1NEGATE )
84
95
) ;
85
96
}
97
+ /**
98
+ * Checks if a script chunk is push-only (contains only data or OP_INT opcodes).
99
+ *
100
+ * @param value - The chunk to check.
101
+ * @returns True if the chunk is push-only, false otherwise.
102
+ */
86
103
function isPushOnlyChunk ( value ) {
87
104
return v . is ( types . BufferSchema , value ) || isOPInt ( value ) ;
88
105
}
106
+ /**
107
+ * Determines if a stack consists of only push operations.
108
+ *
109
+ * @param value - The stack to check.
110
+ * @returns True if all elements in the stack are push-only, false otherwise.
111
+ */
89
112
function isPushOnly ( value ) {
90
113
return v . is ( v . pipe ( v . any ( ) , v . everyItem ( isPushOnlyChunk ) ) , value ) ;
91
114
}
115
+ /**
116
+ * Counts the number of non-push-only opcodes in a stack.
117
+ *
118
+ * @param value - The stack to analyze.
119
+ * @returns The count of non-push-only opcodes.
120
+ */
92
121
function countNonPushOnlyOPs ( value ) {
93
122
return value . length - value . filter ( isPushOnlyChunk ) . length ;
94
123
}
124
+ /**
125
+ * Converts a minimal script buffer to its corresponding opcode, if applicable.
126
+ *
127
+ * @param buffer - The buffer to check.
128
+ * @returns The corresponding opcode or undefined if not minimal.
129
+ */
95
130
function asMinimalOP ( buffer ) {
96
131
if ( buffer . length === 0 ) return ops_js_1 . OPS . OP_0 ;
97
132
if ( buffer . length !== 1 ) return ;
98
133
if ( buffer [ 0 ] >= 1 && buffer [ 0 ] <= 16 ) return OP_INT_BASE + buffer [ 0 ] ;
99
134
if ( buffer [ 0 ] === 0x81 ) return ops_js_1 . OPS . OP_1NEGATE ;
100
135
}
136
+ /**
137
+ * Determines if a buffer or stack is a Uint8Array.
138
+ *
139
+ * @param buf - The buffer or stack to check.
140
+ * @returns True if the input is a Uint8Array, false otherwise.
141
+ */
101
142
function chunksIsBuffer ( buf ) {
102
143
return buf instanceof Uint8Array ;
103
144
}
145
+ /**
146
+ * Determines if a buffer or stack is a valid stack.
147
+ *
148
+ * @param buf - The buffer or stack to check.
149
+ * @returns True if the input is a stack, false otherwise.
150
+ */
104
151
function chunksIsArray ( buf ) {
105
152
return v . is ( StackSchema , buf ) ;
106
153
}
154
+ /**
155
+ * Determines if a single chunk is a Uint8Array.
156
+ *
157
+ * @param buf - The chunk to check.
158
+ * @returns True if the chunk is a Uint8Array, false otherwise.
159
+ */
107
160
function singleChunkIsBuffer ( buf ) {
108
161
return buf instanceof Uint8Array ;
109
162
}
110
163
/**
111
- * Compiles an array of chunks into a Buffer .
164
+ * Compiles an array of script chunks into a Uint8Array .
112
165
*
113
- * @param chunks - The array of chunks to compile.
114
- * @returns The compiled Buffer .
115
- * @throws Error if the compilation fails.
166
+ * @param chunks - The chunks to compile.
167
+ * @returns The compiled script as a Uint8Array .
168
+ * @throws Error if compilation fails.
116
169
*/
117
170
function compile ( chunks ) {
118
- // TODO: remove me
119
171
if ( chunksIsBuffer ( chunks ) ) return chunks ;
120
172
v . parse ( StackSchema , chunks ) ;
121
173
const bufferSize = chunks . reduce ( ( accum , chunk ) => {
122
- // data chunk
123
174
if ( singleChunkIsBuffer ( chunk ) ) {
124
175
// adhere to BIP62.3, minimal push policy
125
176
if ( chunk . length === 1 && asMinimalOP ( chunk ) !== undefined ) {
126
177
return accum + 1 ;
127
178
}
128
179
return accum + pushdata . encodingLength ( chunk . length ) + chunk . length ;
129
180
}
130
- // opcode
131
181
return accum + 1 ;
132
- } , 0.0 ) ;
182
+ } , 0 ) ;
133
183
const buffer = new Uint8Array ( bufferSize ) ;
134
184
let offset = 0 ;
135
185
chunks . forEach ( chunk => {
136
- // data chunk
137
186
if ( singleChunkIsBuffer ( chunk ) ) {
138
187
// adhere to BIP62.3, minimal push policy
139
188
const opcode = asMinimalOP ( chunk ) ;
@@ -154,15 +203,19 @@ function compile(chunks) {
154
203
if ( offset !== buffer . length ) throw new Error ( 'Could not decode chunks' ) ;
155
204
return buffer ;
156
205
}
206
+ /**
207
+ * Decompiles a script buffer into an array of chunks.
208
+ *
209
+ * @param buffer - The script buffer to decompile.
210
+ * @returns The decompiled chunks or null if decompilation fails.
211
+ */
157
212
function decompile ( buffer ) {
158
- // TODO: remove me
159
213
if ( chunksIsArray ( buffer ) ) return buffer ;
160
214
v . parse ( types . BufferSchema , buffer ) ;
161
215
const chunks = [ ] ;
162
216
let i = 0 ;
163
217
while ( i < buffer . length ) {
164
218
const opcode = buffer [ i ] ;
165
- // data chunk
166
219
if ( opcode > ops_js_1 . OPS . OP_0 && opcode <= ops_js_1 . OPS . OP_PUSHDATA4 ) {
167
220
const d = pushdata . decode ( buffer , i ) ;
168
221
// did reading a pushDataInt fail?
@@ -179,7 +232,6 @@ function decompile(buffer) {
179
232
} else {
180
233
chunks . push ( data ) ;
181
234
}
182
- // opcode
183
235
} else {
184
236
chunks . push ( opcode ) ;
185
237
i += 1 ;
@@ -202,7 +254,6 @@ function toASM(chunks) {
202
254
}
203
255
return chunks
204
256
. map ( chunk => {
205
- // data?
206
257
if ( singleChunkIsBuffer ( chunk ) ) {
207
258
const op = asMinimalOP ( chunk ) ;
208
259
if ( op === undefined ) return tools . toHex ( chunk ) ;
@@ -245,13 +296,36 @@ function toStack(chunks) {
245
296
return scriptNumber . encode ( op - OP_INT_BASE ) ;
246
297
} ) ;
247
298
}
299
+ /**
300
+ * Checks if the provided buffer is a canonical public key.
301
+ *
302
+ * @param buffer - The buffer to check, expected to be a Uint8Array.
303
+ * @returns A boolean indicating whether the buffer is a canonical public key.
304
+ */
248
305
function isCanonicalPubKey ( buffer ) {
249
306
return types . isPoint ( buffer ) ;
250
307
}
308
+ /**
309
+ * Checks if the provided hash type is defined.
310
+ *
311
+ * A hash type is considered defined if its modified value (after masking with ~0x80)
312
+ * is greater than 0x00 and less than 0x04.
313
+ *
314
+ * @param hashType - The hash type to check.
315
+ * @returns True if the hash type is defined, false otherwise.
316
+ */
251
317
function isDefinedHashType ( hashType ) {
252
318
const hashTypeMod = hashType & ~ 0x80 ;
253
319
return hashTypeMod > 0x00 && hashTypeMod < 0x04 ;
254
320
}
321
+ /**
322
+ * Checks if the provided buffer is a canonical script signature.
323
+ *
324
+ * A canonical script signature is a valid DER-encoded signature followed by a valid hash type byte.
325
+ *
326
+ * @param buffer - The buffer to check.
327
+ * @returns `true` if the buffer is a canonical script signature, `false` otherwise.
328
+ */
255
329
function isCanonicalScriptSignature ( buffer ) {
256
330
if ( ! ( buffer instanceof Uint8Array ) ) return false ;
257
331
if ( ! isDefinedHashType ( buffer [ buffer . length - 1 ] ) ) return false ;
0 commit comments