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.
Files changed (67) hide show
  1. package/CHANGELOG.md +123 -1
  2. package/README.md +233 -277
  3. package/bsv.bundle.js +39 -0
  4. package/bsv.min.js +8 -8
  5. package/docs/ADVANCED_COVENANT_DEVELOPMENT.md +533 -0
  6. package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +169 -0
  7. package/docs/CUSTOM_SCRIPT_DEVELOPMENT.md +320 -0
  8. package/docs/README.md +201 -0
  9. package/docs/block.md +46 -0
  10. package/docs/ecies.md +102 -0
  11. package/docs/index.md +104 -0
  12. package/docs/nchain.md +958 -0
  13. package/docs/networks.md +55 -0
  14. package/docs/preimage.md +126 -0
  15. package/docs/script.md +139 -0
  16. package/docs/transaction.md +174 -0
  17. package/docs/unspentoutput.md +32 -0
  18. package/examples/README.md +200 -0
  19. package/examples/basic/transaction-creation.js +534 -0
  20. package/examples/basic/transaction_signature_api_gap.js +178 -0
  21. package/examples/covenants/advanced_covenant_demo.js +219 -0
  22. package/examples/covenants/covenant_interface_demo.js +270 -0
  23. package/examples/covenants/covenant_manual_signature_resolved.js +212 -0
  24. package/examples/covenants/covenant_signature_template.js +117 -0
  25. package/examples/covenants2/covenant_bidirectional_example.js +262 -0
  26. package/examples/covenants2/covenant_utils_demo.js +120 -0
  27. package/examples/covenants2/preimage_covenant_utils.js +287 -0
  28. package/examples/covenants2/production_integration.js +256 -0
  29. package/examples/data/covenant_utxos.json +28 -0
  30. package/examples/data/utxos.json +26 -0
  31. package/examples/preimage/README.md +178 -0
  32. package/examples/preimage/extract_preimage_bidirectional.js +421 -0
  33. package/examples/preimage/generate_sample_preimage.js +208 -0
  34. package/examples/preimage/generate_sighash_examples.js +152 -0
  35. package/examples/preimage/parse_preimage.js +117 -0
  36. package/examples/preimage/test_preimage_extractor.js +53 -0
  37. package/examples/preimage/test_varint_extraction.js +95 -0
  38. package/examples/scripts/custom_script_helper_example.js +273 -0
  39. package/examples/scripts/custom_script_signature_test.js +344 -0
  40. package/examples/scripts/script_interpreter.js +193 -0
  41. package/examples/smart_contract/complete_workflow_demo.js +343 -0
  42. package/examples/smart_contract/covenant_builder_demo.js +176 -0
  43. package/examples/smart_contract/script_testing_integration.js +198 -0
  44. package/index.js +3 -0
  45. package/lib/covenant-interface.js +713 -0
  46. package/lib/opcode.js +14 -7
  47. package/lib/smart_contract/API_REFERENCE.md +754 -0
  48. package/lib/smart_contract/DOCUMENTATION_SUMMARY.md +201 -0
  49. package/lib/smart_contract/EXAMPLES.md +751 -0
  50. package/lib/smart_contract/QUICK_START.md +549 -0
  51. package/lib/smart_contract/README.md +395 -0
  52. package/lib/smart_contract/builder.js +452 -0
  53. package/lib/smart_contract/covenant.js +336 -0
  54. package/lib/smart_contract/covenant_builder.js +512 -0
  55. package/lib/smart_contract/index.js +311 -0
  56. package/lib/smart_contract/opcode_list.js +30 -0
  57. package/lib/smart_contract/opcode_map.js +1174 -0
  58. package/lib/smart_contract/opcodes.md +1173 -0
  59. package/lib/smart_contract/preimage.js +903 -0
  60. package/lib/smart_contract/script_tester.js +487 -0
  61. package/lib/smart_contract/script_utils.js +609 -0
  62. package/lib/smart_contract/sighash.js +310 -0
  63. package/lib/smart_contract/smartledger-opcode_review.md +70 -0
  64. package/lib/smart_contract/test_integration.js +269 -0
  65. package/lib/smart_contract/utxo_generator.js +367 -0
  66. package/package.json +43 -10
  67. 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