@smartledger/bsv 3.1.0 → 3.2.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 +123 -1
- package/README.md +233 -277
- package/bsv.bundle.js +39 -0
- package/bsv.min.js +8 -8
- package/docs/ADVANCED_COVENANT_DEVELOPMENT.md +533 -0
- package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +169 -0
- package/docs/CUSTOM_SCRIPT_DEVELOPMENT.md +320 -0
- package/docs/README.md +201 -0
- package/docs/block.md +46 -0
- package/docs/ecies.md +102 -0
- package/docs/index.md +104 -0
- package/docs/nchain.md +958 -0
- package/docs/networks.md +55 -0
- package/docs/preimage.md +126 -0
- package/docs/script.md +139 -0
- package/docs/transaction.md +174 -0
- package/docs/unspentoutput.md +32 -0
- package/examples/README.md +200 -0
- package/examples/basic/transaction-creation.js +534 -0
- package/examples/basic/transaction_signature_api_gap.js +178 -0
- package/examples/covenants/advanced_covenant_demo.js +219 -0
- package/examples/covenants/covenant_interface_demo.js +270 -0
- package/examples/covenants/covenant_manual_signature_resolved.js +212 -0
- package/examples/covenants/covenant_signature_template.js +117 -0
- package/examples/covenants2/covenant_bidirectional_example.js +262 -0
- package/examples/covenants2/covenant_utils_demo.js +120 -0
- package/examples/covenants2/preimage_covenant_utils.js +287 -0
- package/examples/covenants2/production_integration.js +256 -0
- package/examples/data/covenant_utxos.json +28 -0
- package/examples/data/utxos.json +26 -0
- package/examples/preimage/README.md +178 -0
- package/examples/preimage/extract_preimage_bidirectional.js +421 -0
- package/examples/preimage/generate_sample_preimage.js +208 -0
- package/examples/preimage/generate_sighash_examples.js +152 -0
- package/examples/preimage/parse_preimage.js +117 -0
- package/examples/preimage/test_preimage_extractor.js +53 -0
- package/examples/preimage/test_varint_extraction.js +95 -0
- package/examples/scripts/custom_script_helper_example.js +273 -0
- package/examples/scripts/custom_script_signature_test.js +344 -0
- package/examples/scripts/script_interpreter.js +193 -0
- package/examples/smart_contract/complete_workflow_demo.js +343 -0
- package/examples/smart_contract/covenant_builder_demo.js +176 -0
- package/examples/smart_contract/script_testing_integration.js +198 -0
- package/index.js +3 -0
- package/lib/covenant-interface.js +713 -0
- package/lib/opcode.js +14 -7
- package/lib/smart_contract/API_REFERENCE.md +754 -0
- package/lib/smart_contract/DOCUMENTATION_SUMMARY.md +201 -0
- package/lib/smart_contract/EXAMPLES.md +751 -0
- package/lib/smart_contract/QUICK_START.md +549 -0
- package/lib/smart_contract/README.md +395 -0
- package/lib/smart_contract/builder.js +452 -0
- package/lib/smart_contract/covenant.js +336 -0
- package/lib/smart_contract/covenant_builder.js +512 -0
- package/lib/smart_contract/index.js +311 -0
- package/lib/smart_contract/opcode_list.js +30 -0
- package/lib/smart_contract/opcode_map.js +1174 -0
- package/lib/smart_contract/opcodes.md +1173 -0
- package/lib/smart_contract/preimage.js +903 -0
- package/lib/smart_contract/script_tester.js +487 -0
- package/lib/smart_contract/script_utils.js +609 -0
- package/lib/smart_contract/sighash.js +310 -0
- package/lib/smart_contract/smartledger-opcode_review.md +70 -0
- package/lib/smart_contract/test_integration.js +269 -0
- package/lib/smart_contract/utxo_generator.js +367 -0
- package/package.json +43 -10
- package/utilities/blockchain-state.json +20478 -3
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartContract.ScriptTester Class
|
|
3
|
+
* ================================
|
|
4
|
+
*
|
|
5
|
+
* Bitcoin Script execution and debugging capabilities integrated
|
|
6
|
+
* with the SmartContract module for covenant testing.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Execute any Bitcoin Script locally
|
|
10
|
+
* - Step-by-step debugging with stack visualization
|
|
11
|
+
* - Truth evaluation for script verification
|
|
12
|
+
* - Direct integration with Preimage field extraction
|
|
13
|
+
* - Covenant script testing with real preimages
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict'
|
|
17
|
+
|
|
18
|
+
var bsv = require('../..')
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ScriptTester Class - Local script execution and debugging
|
|
22
|
+
* @param {Object} options - Configuration options
|
|
23
|
+
*/
|
|
24
|
+
function ScriptTester(options) {
|
|
25
|
+
if (!(this instanceof ScriptTester)) {
|
|
26
|
+
return new ScriptTester(options)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.options = options || {}
|
|
30
|
+
this.flags = this.options.flags || bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID
|
|
31
|
+
this.satoshis = this.options.satoshis || 100000
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Execute a script and return the result
|
|
36
|
+
* @param {Object} config - Execution configuration
|
|
37
|
+
* @returns {Object} Execution result
|
|
38
|
+
*/
|
|
39
|
+
ScriptTester.prototype.execute = function(config) {
|
|
40
|
+
var unlockingScript = this._parseScript(config.unlocking)
|
|
41
|
+
var lockingScript = this._parseScript(config.locking)
|
|
42
|
+
|
|
43
|
+
// Create dummy transaction for testing
|
|
44
|
+
var tx = this._createDummyTransaction()
|
|
45
|
+
|
|
46
|
+
// Create interpreter
|
|
47
|
+
var interpreter = new bsv.Script.Interpreter()
|
|
48
|
+
var satoshisBN = new bsv.crypto.BN(this.satoshis)
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
var verified = interpreter.verify(
|
|
52
|
+
unlockingScript,
|
|
53
|
+
lockingScript,
|
|
54
|
+
tx,
|
|
55
|
+
0,
|
|
56
|
+
this.flags,
|
|
57
|
+
satoshisBN
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
success: verified,
|
|
62
|
+
unlocking: unlockingScript.toASM(),
|
|
63
|
+
locking: lockingScript.toASM(),
|
|
64
|
+
finalStack: interpreter.stack.map(function(b) { return b.toString('hex') }),
|
|
65
|
+
altStack: interpreter.altstack.map(function(b) { return b.toString('hex') }),
|
|
66
|
+
error: null
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
unlocking: unlockingScript.toASM(),
|
|
72
|
+
locking: lockingScript.toASM(),
|
|
73
|
+
finalStack: [],
|
|
74
|
+
altStack: [],
|
|
75
|
+
error: error.message
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Step through script execution with detailed debugging
|
|
82
|
+
* @param {Object} config - Execution configuration
|
|
83
|
+
* @returns {Array} Array of execution steps
|
|
84
|
+
*/
|
|
85
|
+
ScriptTester.prototype.debug = function(config) {
|
|
86
|
+
var combinedScript = this._createCombinedScript(config)
|
|
87
|
+
var tx = this._createDummyTransaction()
|
|
88
|
+
|
|
89
|
+
var interpreter = new bsv.Script.Interpreter()
|
|
90
|
+
interpreter.script = combinedScript
|
|
91
|
+
interpreter.tx = tx
|
|
92
|
+
interpreter.nIn = 0
|
|
93
|
+
interpreter.flags = this.flags
|
|
94
|
+
|
|
95
|
+
var steps = []
|
|
96
|
+
var chunks = combinedScript.chunks
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
100
|
+
var chunk = chunks[i]
|
|
101
|
+
var opname = bsv.Opcode.reverseMap[chunk.opcodenum] || 'PUSH'
|
|
102
|
+
|
|
103
|
+
// Capture state before execution
|
|
104
|
+
var beforeStack = interpreter.stack.map(function(b) { return b.toString('hex') })
|
|
105
|
+
var beforeAltStack = interpreter.altstack.map(function(b) { return b.toString('hex') })
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
interpreter.step()
|
|
109
|
+
|
|
110
|
+
// Capture state after execution
|
|
111
|
+
var afterStack = interpreter.stack.map(function(b) { return b.toString('hex') })
|
|
112
|
+
var afterAltStack = interpreter.altstack.map(function(b) { return b.toString('hex') })
|
|
113
|
+
|
|
114
|
+
steps.push({
|
|
115
|
+
step: i + 1,
|
|
116
|
+
opcode: opname,
|
|
117
|
+
beforeStack: beforeStack,
|
|
118
|
+
afterStack: afterStack,
|
|
119
|
+
beforeAltStack: beforeAltStack,
|
|
120
|
+
afterAltStack: afterAltStack,
|
|
121
|
+
success: true,
|
|
122
|
+
error: null
|
|
123
|
+
})
|
|
124
|
+
} catch (stepError) {
|
|
125
|
+
steps.push({
|
|
126
|
+
step: i + 1,
|
|
127
|
+
opcode: opname,
|
|
128
|
+
beforeStack: beforeStack,
|
|
129
|
+
afterStack: beforeStack, // No change on error
|
|
130
|
+
beforeAltStack: beforeAltStack,
|
|
131
|
+
afterAltStack: beforeAltStack,
|
|
132
|
+
success: false,
|
|
133
|
+
error: stepError.message
|
|
134
|
+
})
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
steps.push({
|
|
140
|
+
step: 0,
|
|
141
|
+
opcode: 'INITIALIZATION',
|
|
142
|
+
beforeStack: [],
|
|
143
|
+
afterStack: [],
|
|
144
|
+
beforeAltStack: [],
|
|
145
|
+
afterAltStack: [],
|
|
146
|
+
success: false,
|
|
147
|
+
error: error.message
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
script: combinedScript.toASM(),
|
|
153
|
+
steps: steps,
|
|
154
|
+
totalSteps: chunks.length,
|
|
155
|
+
success: steps.length > 0 && steps[steps.length - 1].success
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Test a covenant with preimage and constraints
|
|
161
|
+
* @param {Object} config - Covenant test configuration
|
|
162
|
+
* @returns {Object} Covenant test result
|
|
163
|
+
*/
|
|
164
|
+
ScriptTester.prototype.testCovenant = function(config) {
|
|
165
|
+
var preimageHex = config.preimage
|
|
166
|
+
var constraints = config.constraints || {}
|
|
167
|
+
|
|
168
|
+
// Build covenant locking script from constraints
|
|
169
|
+
var lockingASM = this._buildCovenantScript(constraints, preimageHex)
|
|
170
|
+
|
|
171
|
+
// Test execution
|
|
172
|
+
var result = this.execute({
|
|
173
|
+
unlocking: preimageHex,
|
|
174
|
+
locking: lockingASM
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// Add covenant-specific analysis
|
|
178
|
+
result.covenant = {
|
|
179
|
+
preimageLength: preimageHex.length / 2,
|
|
180
|
+
constraints: constraints,
|
|
181
|
+
constraintResults: this._analyzeConstraints(preimageHex, constraints)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Test preimage field extraction
|
|
189
|
+
* @param {string} preimageHex - Raw preimage hex
|
|
190
|
+
* @param {string} fieldName - Field to extract
|
|
191
|
+
* @returns {Object} Field extraction test result
|
|
192
|
+
*/
|
|
193
|
+
ScriptTester.prototype.testFieldExtraction = function(preimageHex, fieldName) {
|
|
194
|
+
try {
|
|
195
|
+
// Extract field and generate clean ASM (no comments)
|
|
196
|
+
var Preimage = require('./preimage')
|
|
197
|
+
var fieldData = Preimage.extractFromHex(preimageHex, fieldName, { includeComments: false })
|
|
198
|
+
var cleanASM = this._cleanASM(fieldData.asm) // Clean the ASM to handle numeric literals
|
|
199
|
+
|
|
200
|
+
// Test the generated ASM
|
|
201
|
+
var result = this.execute({
|
|
202
|
+
unlocking: preimageHex,
|
|
203
|
+
locking: cleanASM + ' OP_DROP OP_TRUE' // Drop extracted field and succeed
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
result.fieldExtraction = {
|
|
207
|
+
field: fieldName,
|
|
208
|
+
strategy: fieldData.strategy,
|
|
209
|
+
value: fieldData.value,
|
|
210
|
+
interpretation: fieldData.interpretation,
|
|
211
|
+
asmGenerated: fieldData.asm,
|
|
212
|
+
cleanedASM: cleanASM
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return result
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
success: false,
|
|
219
|
+
error: error.message,
|
|
220
|
+
fieldExtraction: {
|
|
221
|
+
field: fieldName,
|
|
222
|
+
error: error.message
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Parse script from ASM or HEX, with special handling for preimage data
|
|
230
|
+
* @private
|
|
231
|
+
*/
|
|
232
|
+
ScriptTester.prototype._parseScript = function(input) {
|
|
233
|
+
if (!input) return new bsv.Script()
|
|
234
|
+
|
|
235
|
+
var trimmed = input.trim()
|
|
236
|
+
var isHex = /^[0-9a-fA-F]+$/.test(trimmed.replace(/\s+/g, ''))
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
if (isHex) {
|
|
240
|
+
// Check if this looks like preimage data (around 180-200 bytes)
|
|
241
|
+
var hexLength = trimmed.length / 2
|
|
242
|
+
if (hexLength >= 180 && hexLength <= 250) {
|
|
243
|
+
// This is likely preimage data - create a script that pushes it
|
|
244
|
+
return new bsv.Script().add(Buffer.from(trimmed, 'hex'))
|
|
245
|
+
} else {
|
|
246
|
+
// Normal hex script
|
|
247
|
+
return bsv.Script.fromHex(trimmed)
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
return bsv.Script.fromASM(trimmed)
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
throw new Error('Failed to parse script: ' + error.message)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create combined script from unlocking and locking parts
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
ScriptTester.prototype._createCombinedScript = function(config) {
|
|
262
|
+
if (config.combined) {
|
|
263
|
+
return this._parseScript(config.combined)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
var unlockingScript = this._parseScript(config.unlocking)
|
|
267
|
+
var lockingScript = this._parseScript(config.locking)
|
|
268
|
+
|
|
269
|
+
var combinedBuffer = Buffer.concat([
|
|
270
|
+
unlockingScript.toBuffer(),
|
|
271
|
+
lockingScript.toBuffer()
|
|
272
|
+
])
|
|
273
|
+
|
|
274
|
+
return bsv.Script.fromBuffer(combinedBuffer)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create dummy transaction for testing
|
|
279
|
+
* @private
|
|
280
|
+
*/
|
|
281
|
+
ScriptTester.prototype._createDummyTransaction = function() {
|
|
282
|
+
var tx = new bsv.Transaction()
|
|
283
|
+
|
|
284
|
+
// Create dummy input
|
|
285
|
+
var dummyInput = new bsv.Transaction.Input({
|
|
286
|
+
prevTxId: '0'.repeat(64),
|
|
287
|
+
outputIndex: 0,
|
|
288
|
+
script: bsv.Script.empty(),
|
|
289
|
+
satoshis: this.satoshis,
|
|
290
|
+
output: new bsv.Transaction.Output({
|
|
291
|
+
satoshis: this.satoshis,
|
|
292
|
+
script: bsv.Script.empty()
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
tx.addInput(dummyInput)
|
|
297
|
+
tx.addOutput(new bsv.Transaction.Output({
|
|
298
|
+
satoshis: this.satoshis,
|
|
299
|
+
script: bsv.Script.empty()
|
|
300
|
+
}))
|
|
301
|
+
|
|
302
|
+
return tx
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Build covenant locking script from constraints
|
|
307
|
+
* @private
|
|
308
|
+
*/
|
|
309
|
+
ScriptTester.prototype._buildCovenantScript = function(constraints, preimageHex) {
|
|
310
|
+
var scriptParts = []
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
var Preimage = require('./preimage')
|
|
314
|
+
|
|
315
|
+
// Amount constraint
|
|
316
|
+
if (constraints.minimumAmount) {
|
|
317
|
+
var valueASM = Preimage.generateASMFromHex(preimageHex, 'value', false) // No comments
|
|
318
|
+
scriptParts.push(valueASM)
|
|
319
|
+
scriptParts.push('OP_BIN2NUM')
|
|
320
|
+
scriptParts.push(constraints.minimumAmount.toString())
|
|
321
|
+
scriptParts.push('OP_GREATERTHANOREQUAL')
|
|
322
|
+
scriptParts.push('OP_VERIFY')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// SIGHASH constraint
|
|
326
|
+
if (constraints.requiredSighash) {
|
|
327
|
+
var sighashASM = Preimage.generateASMFromHex(preimageHex, 'sighashType', false) // No comments
|
|
328
|
+
scriptParts.push(sighashASM)
|
|
329
|
+
scriptParts.push('OP_BIN2NUM')
|
|
330
|
+
scriptParts.push(constraints.requiredSighash.toString())
|
|
331
|
+
scriptParts.push('OP_EQUAL')
|
|
332
|
+
scriptParts.push('OP_VERIFY')
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Add success condition
|
|
336
|
+
scriptParts.push('OP_TRUE')
|
|
337
|
+
|
|
338
|
+
var scriptString = scriptParts.join(' ')
|
|
339
|
+
|
|
340
|
+
// Clean the entire script to handle numeric literals
|
|
341
|
+
return this._cleanASM(scriptString)
|
|
342
|
+
} catch (error) {
|
|
343
|
+
throw new Error('Failed to build covenant script: ' + error.message)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Clean ASM by removing comments and extra whitespace
|
|
349
|
+
* @private
|
|
350
|
+
*/
|
|
351
|
+
ScriptTester.prototype._cleanASM = function(asm) {
|
|
352
|
+
var cleaned = asm
|
|
353
|
+
.split('\n')
|
|
354
|
+
.map(function(line) { return line.split('#')[0].trim() }) // Remove comments
|
|
355
|
+
.filter(function(line) { return line.length > 0 }) // Remove empty lines
|
|
356
|
+
.join(' ') // Join with spaces
|
|
357
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
358
|
+
.trim()
|
|
359
|
+
|
|
360
|
+
// Convert numeric literals to proper ASM format
|
|
361
|
+
cleaned = cleaned.replace(/\b(\d+)\b/g, function(match, number) {
|
|
362
|
+
var num = parseInt(number)
|
|
363
|
+
if (num >= 1 && num <= 16) {
|
|
364
|
+
return 'OP_' + num
|
|
365
|
+
} else if (num === 0) {
|
|
366
|
+
return 'OP_0'
|
|
367
|
+
} else if (num === -1) {
|
|
368
|
+
return 'OP_1NEGATE'
|
|
369
|
+
} else {
|
|
370
|
+
// For larger numbers, convert to minimal little-endian representation
|
|
371
|
+
var value = num
|
|
372
|
+
var negative = value < 0
|
|
373
|
+
if (negative) value = -value
|
|
374
|
+
|
|
375
|
+
var bytes = []
|
|
376
|
+
|
|
377
|
+
// Convert to little-endian bytes
|
|
378
|
+
while (value > 0) {
|
|
379
|
+
bytes.push(value & 0xFF)
|
|
380
|
+
value = Math.floor(value / 256)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Handle empty case (zero)
|
|
384
|
+
if (bytes.length === 0) {
|
|
385
|
+
bytes.push(0)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Handle negative numbers
|
|
389
|
+
if (negative) {
|
|
390
|
+
// Set the high bit of the most significant byte
|
|
391
|
+
if (bytes[bytes.length - 1] >= 0x80) {
|
|
392
|
+
bytes.push(0x80)
|
|
393
|
+
} else {
|
|
394
|
+
bytes[bytes.length - 1] |= 0x80
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
// Ensure we don't have negative interpretation (high bit set)
|
|
398
|
+
if (bytes.length > 0 && bytes[bytes.length - 1] >= 0x80) {
|
|
399
|
+
bytes.push(0x00)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Convert to hex string
|
|
404
|
+
return Buffer.from(bytes).toString('hex')
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
return cleaned
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Analyze constraint satisfaction
|
|
413
|
+
* @private
|
|
414
|
+
*/
|
|
415
|
+
ScriptTester.prototype._analyzeConstraints = function(preimageHex, constraints) {
|
|
416
|
+
var results = {}
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
var Preimage = require('./preimage')
|
|
420
|
+
|
|
421
|
+
if (constraints.minimumAmount) {
|
|
422
|
+
var valueField = Preimage.extractFromHex(preimageHex, 'value', { includeComments: false })
|
|
423
|
+
var currentAmount = parseInt(valueField.interpretation.satoshis)
|
|
424
|
+
results.amountCheck = {
|
|
425
|
+
constraint: constraints.minimumAmount,
|
|
426
|
+
actual: currentAmount,
|
|
427
|
+
satisfied: currentAmount >= constraints.minimumAmount
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (constraints.requiredSighash) {
|
|
432
|
+
var sighashField = Preimage.extractFromHex(preimageHex, 'sighashType', { includeComments: false })
|
|
433
|
+
var currentSighash = parseInt(sighashField.value, 16)
|
|
434
|
+
results.sighashCheck = {
|
|
435
|
+
constraint: constraints.requiredSighash,
|
|
436
|
+
actual: currentSighash,
|
|
437
|
+
satisfied: (currentSighash & 0xFF) === constraints.requiredSighash
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
441
|
+
results.error = error.message
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return results
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Static utility methods
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Quick script execution test
|
|
453
|
+
* @param {string} unlocking - Unlocking script
|
|
454
|
+
* @param {string} locking - Locking script
|
|
455
|
+
* @param {Object} options - Test options
|
|
456
|
+
* @returns {Object} Test result
|
|
457
|
+
*/
|
|
458
|
+
ScriptTester.test = function(unlocking, locking, options) {
|
|
459
|
+
var tester = new ScriptTester(options)
|
|
460
|
+
return tester.execute({ unlocking: unlocking, locking: locking })
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Quick covenant test
|
|
465
|
+
* @param {string} preimageHex - Preimage hex
|
|
466
|
+
* @param {Object} constraints - Covenant constraints
|
|
467
|
+
* @param {Object} options - Test options
|
|
468
|
+
* @returns {Object} Covenant test result
|
|
469
|
+
*/
|
|
470
|
+
ScriptTester.testCovenant = function(preimageHex, constraints, options) {
|
|
471
|
+
var tester = new ScriptTester(options)
|
|
472
|
+
return tester.testCovenant({ preimage: preimageHex, constraints: constraints })
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Quick field extraction test
|
|
477
|
+
* @param {string} preimageHex - Preimage hex
|
|
478
|
+
* @param {string} fieldName - Field to extract
|
|
479
|
+
* @param {Object} options - Test options
|
|
480
|
+
* @returns {Object} Field extraction test result
|
|
481
|
+
*/
|
|
482
|
+
ScriptTester.testFieldExtraction = function(preimageHex, fieldName, options) {
|
|
483
|
+
var tester = new ScriptTester(options)
|
|
484
|
+
return tester.testFieldExtraction(preimageHex, fieldName)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
module.exports = ScriptTester
|