@smartledger/bsv 3.1.1 → 3.2.1

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 (69) 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 +862 -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 +350 -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_interpreter.js +236 -0
  61. package/lib/smart_contract/script_tester.js +487 -0
  62. package/lib/smart_contract/script_utils.js +621 -0
  63. package/lib/smart_contract/sighash.js +310 -0
  64. package/lib/smart_contract/smartledger-opcode_review.md +70 -0
  65. package/lib/smart_contract/stack_examiner.js +129 -0
  66. package/lib/smart_contract/test_integration.js +269 -0
  67. package/lib/smart_contract/utxo_generator.js +367 -0
  68. package/package.json +43 -10
  69. package/utilities/blockchain-state.json +20478 -3
@@ -0,0 +1,310 @@
1
+ /**
2
+ * SmartContract.SIGHASH Class
3
+ * ===========================
4
+ *
5
+ * SIGHASH flag utilities with:
6
+ * - Complete flag analysis and detection
7
+ * - Zero hash behavior explanation
8
+ * - Multi-input transaction examples
9
+ * - BIP-143 compliance verification
10
+ *
11
+ * Based on examples/preimage/generate_sighash_examples.js
12
+ */
13
+
14
+ 'use strict'
15
+
16
+ var bsv = require('../..')
17
+
18
+ /**
19
+ * SIGHASH Class - SIGHASH flag analysis and utilities
20
+ * @param {number} sighashType - SIGHASH type flags
21
+ */
22
+ function SIGHASH(sighashType) {
23
+ if (!(this instanceof SIGHASH)) {
24
+ return new SIGHASH(sighashType)
25
+ }
26
+
27
+ this.sighashType = sighashType || (bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID)
28
+ this.analysis = this._analyzeFlags()
29
+ }
30
+
31
+ /**
32
+ * Analyze SIGHASH flags and their implications
33
+ * @returns {Object} Flag analysis
34
+ */
35
+ SIGHASH.prototype.analyze = function() {
36
+ return this.analysis
37
+ }
38
+
39
+ /**
40
+ * Get zero hash behavior explanation
41
+ * @returns {Object} Zero hash behavior details
42
+ */
43
+ SIGHASH.prototype.getZeroHashBehavior = function() {
44
+ var behavior = {
45
+ hashPrevouts: false,
46
+ hashSequence: false,
47
+ hashOutputs: false,
48
+ explanation: [],
49
+ criticalNote: null
50
+ }
51
+
52
+ if (this.analysis.anyoneCanPay) {
53
+ behavior.hashPrevouts = true
54
+ behavior.explanation.push('ANYONECANPAY: hashPrevouts becomes zero hash (0x00...00)')
55
+ }
56
+
57
+ if (this.analysis.baseType === 'NONE') {
58
+ behavior.hashSequence = true
59
+ behavior.hashOutputs = true
60
+ behavior.explanation.push('SIGHASH_NONE: hashSequence and hashOutputs become zero hash')
61
+ }
62
+
63
+ if (this.analysis.baseType === 'SINGLE') {
64
+ behavior.hashSequence = true
65
+ behavior.hashOutputs = true
66
+ behavior.explanation.push('SIGHASH_SINGLE: hashSequence becomes zero hash, hashOutputs covers only corresponding output')
67
+ }
68
+
69
+ if (behavior.hashPrevouts || behavior.hashSequence || behavior.hashOutputs) {
70
+ behavior.criticalNote = "These zero hashes are NOT bugs - they are correct BIP-143 behavior for the specified SIGHASH flags!"
71
+ }
72
+
73
+ return behavior
74
+ }
75
+
76
+ /**
77
+ * Generate example preimage with specific SIGHASH flags
78
+ * @returns {Preimage} Example preimage
79
+ */
80
+ SIGHASH.prototype.generateExample = function() {
81
+ // Create simple single-input transaction to avoid complex script size issues
82
+ var privateKey = bsv.PrivateKey.fromRandom()
83
+ var address = privateKey.toAddress()
84
+
85
+ // Create simple P2PKH UTXO
86
+ var utxo = {
87
+ txId: 'a'.repeat(64),
88
+ outputIndex: 0,
89
+ script: bsv.Script.buildPublicKeyHashOut(address).toHex(),
90
+ satoshis: 100000
91
+ }
92
+
93
+ // Create simple transaction
94
+ var transaction = new bsv.Transaction()
95
+ .from(utxo)
96
+ .to(address, 99000)
97
+
98
+ // Generate preimage for the input with our SIGHASH type
99
+ var subscript = bsv.Script.fromHex(utxo.script)
100
+ var preimageBuffer = bsv.Transaction.sighash.sighashPreimage(
101
+ transaction,
102
+ this.sighashType,
103
+ 0,
104
+ subscript,
105
+ new bsv.crypto.BN(utxo.satoshis)
106
+ )
107
+
108
+ var Preimage = require('./preimage')
109
+ var preimage = new Preimage(preimageBuffer)
110
+
111
+ return {
112
+ transaction: transaction,
113
+ preimage: preimage,
114
+ sighashType: this.sighashType,
115
+ analysis: this.analysis,
116
+ zeroHashBehavior: this.getZeroHashBehavior(),
117
+ fields: preimage.extract(),
118
+ validation: preimage.validate()
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Create comprehensive SIGHASH demonstration
124
+ * @returns {Object} Complete SIGHASH analysis with examples
125
+ */
126
+ SIGHASH.prototype.createDemonstration = function() {
127
+ var example = this.generateExample()
128
+ var zeroHash = Buffer.alloc(32).toString('hex')
129
+
130
+ var demonstration = {
131
+ sighashType: this.sighashType,
132
+ analysis: this.analysis,
133
+ zeroHashBehavior: this.getZeroHashBehavior(),
134
+ preimageFields: {},
135
+ observations: [],
136
+ educationalNotes: []
137
+ }
138
+
139
+ // Extract and analyze preimage fields
140
+ var fields = example.preimage.toObject().fields
141
+ demonstration.preimageFields = fields
142
+
143
+ // Check for zero hashes and explain them
144
+ if (fields.hashPrevouts === zeroHash) {
145
+ demonstration.observations.push({
146
+ field: 'hashPrevouts',
147
+ value: 'ZERO HASH (00...00)',
148
+ reason: 'ANYONECANPAY flag - this input can be combined with any other inputs'
149
+ })
150
+ }
151
+
152
+ if (fields.hashSequence === zeroHash) {
153
+ demonstration.observations.push({
154
+ field: 'hashSequence',
155
+ value: 'ZERO HASH (00...00)',
156
+ reason: this.analysis.baseType + ' flag - sequence numbers not covered by signature'
157
+ })
158
+ }
159
+
160
+ if (fields.hashOutputs === zeroHash) {
161
+ demonstration.observations.push({
162
+ field: 'hashOutputs',
163
+ value: 'ZERO HASH (00...00)',
164
+ reason: this.analysis.baseType + ' flag - outputs not fully covered by signature'
165
+ })
166
+ }
167
+
168
+ // Educational notes
169
+ demonstration.educationalNotes = [
170
+ "Zero hashes in preimages are NOT errors - they indicate specific SIGHASH flag behavior",
171
+ "BIP-143 mandates these zero values when certain flags are used",
172
+ "ANYONECANPAY allows this input to be combined with different sets of inputs",
173
+ "SIGHASH_NONE means the signer doesn't care about any outputs",
174
+ "SIGHASH_SINGLE means the signer only cares about the corresponding output",
175
+ "Always check SIGHASH flags when you see 'mysterious' zero hashes in preimages"
176
+ ]
177
+
178
+ return demonstration
179
+ }
180
+
181
+ /**
182
+ * Check if preimage matches expected SIGHASH behavior
183
+ * @param {Preimage} preimage - Preimage to check
184
+ * @returns {Object} Compliance check result
185
+ */
186
+ SIGHASH.prototype.checkCompliance = function(preimage) {
187
+ var fields = preimage.toObject().fields
188
+ var zeroHash = Buffer.alloc(32).toString('hex')
189
+ var compliance = {
190
+ compliant: true,
191
+ issues: [],
192
+ expectedZeros: [],
193
+ unexpectedZeros: []
194
+ }
195
+
196
+ var expectedBehavior = this.getZeroHashBehavior()
197
+
198
+ // Check hashPrevouts
199
+ if (expectedBehavior.hashPrevouts && fields.hashPrevouts !== zeroHash) {
200
+ compliance.compliant = false
201
+ compliance.issues.push('hashPrevouts should be zero due to ANYONECANPAY flag')
202
+ } else if (!expectedBehavior.hashPrevouts && fields.hashPrevouts === zeroHash) {
203
+ compliance.unexpectedZeros.push('hashPrevouts is zero but ANYONECANPAY not set')
204
+ }
205
+
206
+ // Check hashSequence
207
+ if (expectedBehavior.hashSequence && fields.hashSequence !== zeroHash) {
208
+ compliance.compliant = false
209
+ compliance.issues.push('hashSequence should be zero due to ' + this.analysis.baseType + ' flag')
210
+ } else if (!expectedBehavior.hashSequence && fields.hashSequence === zeroHash) {
211
+ compliance.unexpectedZeros.push('hashSequence is zero but ' + this.analysis.baseType + ' allows sequence coverage')
212
+ }
213
+
214
+ // Check hashOutputs
215
+ if (expectedBehavior.hashOutputs && fields.hashOutputs !== zeroHash) {
216
+ compliance.compliant = false
217
+ compliance.issues.push('hashOutputs should be zero due to ' + this.analysis.baseType + ' flag')
218
+ } else if (!expectedBehavior.hashOutputs && fields.hashOutputs === zeroHash) {
219
+ compliance.unexpectedZeros.push('hashOutputs is zero but ' + this.analysis.baseType + ' should cover outputs')
220
+ }
221
+
222
+ return compliance
223
+ }
224
+
225
+ /**
226
+ * Internal flag analysis
227
+ * @private
228
+ */
229
+ SIGHASH.prototype._analyzeFlags = function() {
230
+ var baseType = this.sighashType & 0x1f
231
+ var anyoneCanPay = (this.sighashType & 0x80) !== 0
232
+ var forkId = (this.sighashType & 0x40) !== 0
233
+
234
+ var baseTypeName = 'UNKNOWN'
235
+ if (baseType === 1) baseTypeName = 'ALL'
236
+ else if (baseType === 2) baseTypeName = 'NONE'
237
+ else if (baseType === 3) baseTypeName = 'SINGLE'
238
+
239
+ var flags = []
240
+ flags.push(baseTypeName)
241
+ if (anyoneCanPay) flags.push('ANYONECANPAY')
242
+ if (forkId) flags.push('FORKID')
243
+
244
+ return {
245
+ sighashType: this.sighashType,
246
+ baseType: baseTypeName,
247
+ anyoneCanPay: anyoneCanPay,
248
+ forkId: forkId,
249
+ flagName: flags.join(' | '),
250
+ hex: '0x' + this.sighashType.toString(16).padStart(8, '0'),
251
+ binary: '0b' + this.sighashType.toString(2).padStart(32, '0')
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Static utility methods
257
+ */
258
+
259
+ /**
260
+ * Get all standard SIGHASH types
261
+ * @returns {Array} Array of SIGHASH type objects
262
+ */
263
+ SIGHASH.getAllTypes = function() {
264
+ var forkId = bsv.crypto.Signature.SIGHASH_FORKID
265
+
266
+ return [
267
+ { name: 'ALL', value: bsv.crypto.Signature.SIGHASH_ALL | forkId },
268
+ { name: 'NONE', value: bsv.crypto.Signature.SIGHASH_NONE | forkId },
269
+ { name: 'SINGLE', value: bsv.crypto.Signature.SIGHASH_SINGLE | forkId },
270
+ { name: 'ALL | ANYONECANPAY', value: bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_ANYONECANPAY | forkId },
271
+ { name: 'NONE | ANYONECANPAY', value: bsv.crypto.Signature.SIGHASH_NONE | bsv.crypto.Signature.SIGHASH_ANYONECANPAY | forkId },
272
+ { name: 'SINGLE | ANYONECANPAY', value: bsv.crypto.Signature.SIGHASH_SINGLE | bsv.crypto.Signature.SIGHASH_ANYONECANPAY | forkId }
273
+ ]
274
+ }
275
+
276
+ /**
277
+ * Generate demonstrations for all SIGHASH types
278
+ * @returns {Array} Array of demonstrations for each SIGHASH type
279
+ */
280
+ SIGHASH.generateAllDemonstrations = function() {
281
+ return SIGHASH.getAllTypes().map(function(type) {
282
+ var sighash = new SIGHASH(type.value)
283
+ return {
284
+ typeName: type.name,
285
+ demonstration: sighash.createDemonstration()
286
+ }
287
+ })
288
+ }
289
+
290
+ /**
291
+ * Explain the "extra zero mystery" that confuses developers
292
+ * @returns {Object} Educational explanation
293
+ */
294
+ SIGHASH.explainZeroMystery = function() {
295
+ return {
296
+ title: "The 'Extra Zero' Mystery in Bitcoin Preimages",
297
+ problem: "Developers often see zero hashes (0x00...00) in preimage fields and think it's a bug",
298
+ reality: "These zeros are CORRECT behavior mandated by BIP-143 for specific SIGHASH flags",
299
+ explanation: [
300
+ "ANYONECANPAY sets hashPrevouts to zero - allows input combination flexibility",
301
+ "SIGHASH_NONE sets hashSequence and hashOutputs to zero - signer doesn't care about outputs",
302
+ "SIGHASH_SINGLE sets hashSequence to zero - only corresponding output matters",
303
+ "These are features, not bugs - they enable advanced Bitcoin transaction patterns"
304
+ ],
305
+ solution: "Always check SIGHASH flags when analyzing preimages with zero hashes",
306
+ toolTip: "Use SIGHASH.checkCompliance() to verify if zero hashes match expected flag behavior"
307
+ }
308
+ }
309
+
310
+ module.exports = SIGHASH
@@ -0,0 +1,70 @@
1
+ Great — and yes, that’s **very close** to complete and fully valid for the *Genesis-through-Chronicle* BSV script range.
2
+
3
+ Let’s audit your SmartLedger-BSV list versus the **current Bitcoin SV “Chronicle” (v1.0.15+) opcode table** maintained by the BSV Node team (the reference used by libraries like `bsv-py`, `bsvlib`, and the official `bitcoin-sv` C++ node).
4
+
5
+ ---
6
+
7
+ ### ✅ What your list **already has correct**
8
+
9
+ * All **core opcodes 0x00–0xa5** are present and correctly numbered.
10
+ * Includes the re-enabled arithmetic ops (`OP_2MUL`, `OP_2DIV`).
11
+ * Includes all logical, cryptographic, and flow-control opcodes up through `OP_CHECKMULTISIGVERIFY`.
12
+ * Includes both `OP_CHECKLOCKTIMEVERIFY` (0xb1) and `OP_CHECKSEQUENCEVERIFY` (0xb2) for historical compatibility.
13
+ * Includes the full NOP1-NOP10 range (0xb0-0xb9).
14
+ * Includes pseudo-ops `OP_PUBKEYHASH`, `OP_PUBKEY`, and `OP_INVALIDOPCODE`.
15
+
16
+ So for almost every practical script (Genesis-era contracts, sCrypt, or custom covenant testing), your `smartledger-bsv` `Opcode` table is **fully usable**.
17
+
18
+ ---
19
+
20
+ ### ⚠️ What’s **missing or slightly outdated**
21
+
22
+ | Category | Opcode(s) | Status / Comment |
23
+ | -------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
24
+ | **Chronicle additions** | `OP_VER` (0x62), `OP_VERIF` (0x65), `OP_VERNOTIF` (0x66) | These appear in your list ✅ — you’re current here. |
25
+ | **Chronicle string ops** | `OP_SUBSTR` (0xb3), `OP_LEFT` (0xb4), `OP_RIGHT` (0xb5) | ❌ **Missing** — these were re-enabled in Chronicle for string slicing. |
26
+ | **Alias clarification** | `OP_CHECKLOCKTIMEVERIFY` == `OP_NOP2`; `OP_CHECKSEQUENCEVERIFY` == `OP_NOP3` | ✅ You have both, good. |
27
+ | **NOP range completeness** | `OP_NOP4`-`OP_NOP10` (0xb6–0xbc) | ✅ You have them up to 0xb9; 0xba–0xbc missing (though reserved and harmless). |
28
+ | **Reserved ops** | `OP_RESERVED1` (0x89), `OP_RESERVED2` (0x8a) | ✅ Included. |
29
+ | **Deprecated pseudo-ops** | `OP_VER*` versions now valid; `OP_NOP2/NOP3` revert to no-ops post-Genesis | ✅ Behavior matches network. |
30
+
31
+ ---
32
+
33
+ ### 🧩 TL;DR — Your version is ~**98 % complete**.
34
+
35
+ To be **100 % Chronicle-accurate**, just add:
36
+
37
+ ```js
38
+ OP_SUBSTR: 0xb3,
39
+ OP_LEFT: 0xb4,
40
+ OP_RIGHT: 0xb5,
41
+ ```
42
+
43
+ and (optionally) reserve:
44
+
45
+ ```js
46
+ OP_NOP8: 0xba,
47
+ OP_NOP9: 0xbb,
48
+ OP_NOP10: 0xbc,
49
+ ```
50
+
51
+ Everything else matches the official table exactly.
52
+
53
+ ---
54
+
55
+ ### ✅ Recommended Minimal Patch for SmartLedger-BSV
56
+
57
+ ```diff
58
+ + OP_SUBSTR: 0xb3,
59
+ + OP_LEFT: 0xb4,
60
+ + OP_RIGHT: 0xb5,
61
+ + OP_NOP8: 0xba,
62
+ + OP_NOP9: 0xbb,
63
+ + OP_NOP10: 0xbc,
64
+ ```
65
+
66
+ Once those are added, your library will be fully aligned with **Bitcoin SV Node v1.0.15 (Chronicle release)** — the current canonical implementation.
67
+
68
+ ---
69
+
70
+ Would you like me to generate a **JSON manifest or TypeScript enum** version of this table so you can auto-sync it into your SmartLedger-BSV build during release cycles?
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Stack Examination Tool (Browser + Node.js compatible)
3
+ * -----------------------------------------
4
+ * Run with:
5
+ * node stack_examiner.js <locking_script_hex> <unlocking_script_hex>
6
+ *
7
+ * Example:
8
+ * node stack_examiner.js "76a91489abcdefabbaabbaabbaabbaabbaabbaabbaabba88ac" "512103abcdef..."
9
+ *
10
+ * Browser usage:
11
+ * const examiner = require('./stack_examiner.js');
12
+ * examiner.runScript(lockingHex, unlockingHex);
13
+ */
14
+
15
+ // Browser-safe require
16
+ const bsv = (typeof window !== 'undefined' && window.bsv) ? window.bsv : require('../../index.js');
17
+ const util = require('util');
18
+
19
+ function bufferToHexArray(stack) {
20
+ return stack.map((buf) => buf.toString('hex'));
21
+ }
22
+
23
+ /**
24
+ * Executes a locking/unlocking script and prints intermediate stack states.
25
+ */
26
+ function runScript(lockingHex, unlockingHex) {
27
+ console.log("===========================================");
28
+ console.log("🔍 STACK EXAMINATION TOOL");
29
+ console.log("===========================================\n");
30
+
31
+ console.log("🔐 Locking Script:", lockingHex);
32
+ console.log("🔓 Unlocking Script:", unlockingHex);
33
+ console.log("-------------------------------------------");
34
+
35
+ try {
36
+ const lockingScript = bsv.Script.fromHex(lockingHex);
37
+ const unlockingScript = bsv.Script.fromHex(unlockingHex);
38
+
39
+ const combined = bsv.Script.fromBuffer(
40
+ Buffer.concat([unlockingScript.toBuffer(), lockingScript.toBuffer()])
41
+ );
42
+
43
+ const interpreter = new bsv.Script.Interpreter();
44
+ const tx = new bsv.Transaction();
45
+
46
+ // Create proper transaction context
47
+ const dummyInput = new bsv.Transaction.Input({
48
+ prevTxId: '0'.repeat(64),
49
+ outputIndex: 0,
50
+ script: bsv.Script.empty()
51
+ });
52
+
53
+ tx.addInput(dummyInput);
54
+ tx.addOutput(new bsv.Transaction.Output({
55
+ satoshis: 100000,
56
+ script: lockingScript
57
+ }));
58
+
59
+ const scriptChunks = combined.chunks;
60
+ const sandbox = new bsv.Script.Interpreter();
61
+ sandbox.script = combined;
62
+ sandbox.tx = tx;
63
+ sandbox.nIn = 0;
64
+ sandbox.flags = bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID;
65
+
66
+ // Step through each opcode
67
+ for (let i = 0; i < scriptChunks.length; i++) {
68
+ const chunk = scriptChunks[i];
69
+ try {
70
+ // Check if step method exists (compatibility check)
71
+ if (typeof sandbox.step === 'function') {
72
+ sandbox.step();
73
+ } else {
74
+ console.log("⚠️ Step-by-step execution not supported in this BSV version");
75
+ break;
76
+ }
77
+
78
+ console.log(`\n🧩 Step ${i + 1}: ${bsv.Opcode.reverseMap[chunk.opcodenum] || 'PUSH'}`);
79
+ console.log("Stack:", bufferToHexArray(sandbox.stack));
80
+ console.log("AltStack:", bufferToHexArray(sandbox.altstack));
81
+ } catch (err) {
82
+ console.log(`⚠️ Error executing opcode ${i + 1}:`, err.message);
83
+ break;
84
+ }
85
+ }
86
+
87
+ // Final verification with proper satoshis parameter
88
+ const satoshisBN = new bsv.crypto.BN(100000);
89
+ const verified = interpreter.verify(
90
+ unlockingScript,
91
+ lockingScript,
92
+ tx,
93
+ 0,
94
+ bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID,
95
+ satoshisBN
96
+ );
97
+
98
+ console.log("\n===========================================");
99
+ console.log("✅ Final Result:", verified ? "TRUE (Success)" : "❌ FALSE (Failure)");
100
+ console.log("Final Stack:", bufferToHexArray(sandbox.stack || []));
101
+ console.log("AltStack:", bufferToHexArray(sandbox.altstack || []));
102
+ console.log("===========================================");
103
+
104
+ return verified;
105
+ } catch (err) {
106
+ console.error("❌ Error in script execution:", err.message);
107
+ return false;
108
+ }
109
+ }
110
+
111
+ // ============================================
112
+ // CLI Entrypoint
113
+ // ============================================
114
+ if (typeof require !== 'undefined' && require.main === module) {
115
+ if (process.argv.length < 4) {
116
+ console.log("Usage: node stack_examiner.js <locking_script_hex> <unlocking_script_hex>");
117
+ process.exit(1);
118
+ }
119
+
120
+ const locking = process.argv[2];
121
+ const unlocking = process.argv[3];
122
+ runScript(locking, unlocking);
123
+ }
124
+
125
+ // Export for module usage
126
+ module.exports = {
127
+ runScript,
128
+ bufferToHexArray
129
+ };