@smartledger/bsv 3.2.0 ā 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.
- package/bsv.bundle.js +9 -9
- package/bsv.min.js +8 -8
- package/lib/smart_contract/API_REFERENCE.md +111 -3
- package/lib/smart_contract/index.js +40 -1
- package/lib/smart_contract/script_interpreter.js +236 -0
- package/lib/smart_contract/script_utils.js +16 -4
- package/lib/smart_contract/stack_examiner.js +129 -0
- package/package.json +2 -2
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
1. [Quick Start](#quick-start)
|
|
8
8
|
2. [Core Classes](#core-classes)
|
|
9
|
-
3. [Interface Functions (
|
|
9
|
+
3. [Interface Functions (52 Total)](#interface-functions)
|
|
10
10
|
4. [JavaScript-to-Script Framework](#javascript-to-script-framework)
|
|
11
11
|
5. [CovenantBuilder API (61 Methods)](#covenantbuilder-api)
|
|
12
12
|
6. [Opcode Mapping System](#opcode-mapping-system)
|
|
13
|
-
7. [
|
|
14
|
-
8. [
|
|
13
|
+
7. [Debug and Analysis Tools](#debug-and-analysis-tools)
|
|
14
|
+
8. [Real-World Examples](#real-world-examples)
|
|
15
|
+
9. [Error Handling](#error-handling)
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
@@ -598,6 +599,113 @@ console.log('Result stack:', newStack) // ['05'] (2 + 3 = 5)
|
|
|
598
599
|
|
|
599
600
|
---
|
|
600
601
|
|
|
602
|
+
## Debug and Analysis Tools
|
|
603
|
+
|
|
604
|
+
### Stack Examination
|
|
605
|
+
|
|
606
|
+
**Examine stack states during script execution**
|
|
607
|
+
|
|
608
|
+
```javascript
|
|
609
|
+
// Examine stack evolution step-by-step
|
|
610
|
+
const lockingScript = "76a914" + "89abcdef".repeat(5) + "88ac" // P2PKH
|
|
611
|
+
const unlockingScript = "47" + "30440220".repeat(4) + "01" // Signature
|
|
612
|
+
|
|
613
|
+
const success = SmartContract.examineStack(lockingScript, unlockingScript)
|
|
614
|
+
// Outputs:
|
|
615
|
+
// š STACK EXAMINATION TOOL
|
|
616
|
+
// š§© Step 1: PUSH (signature)
|
|
617
|
+
// Stack: ['30440220...']
|
|
618
|
+
// š§© Step 2: OP_DUP
|
|
619
|
+
// Stack: ['30440220...', '30440220...']
|
|
620
|
+
// ... continues through all opcodes
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Script Debugging
|
|
624
|
+
|
|
625
|
+
**Interactive script debugging with step-by-step execution**
|
|
626
|
+
|
|
627
|
+
```javascript
|
|
628
|
+
// Parse script from ASM or HEX
|
|
629
|
+
const unlocking = SmartContract.parseScript("OP_1 OP_2")
|
|
630
|
+
const locking = SmartContract.parseScript("OP_ADD OP_3 OP_EQUAL")
|
|
631
|
+
|
|
632
|
+
// Debug with full verification
|
|
633
|
+
SmartContract.debugScriptExecution(unlocking, locking, {
|
|
634
|
+
stepMode: false // Set to true for interactive step-by-step
|
|
635
|
+
})
|
|
636
|
+
// Outputs:
|
|
637
|
+
// š SCRIPT INTERPRETER DEBUGGER (FULL RUN)
|
|
638
|
+
// š Locking Script: OP_ADD OP_3 OP_EQUAL
|
|
639
|
+
// š Unlocking Script: OP_1 OP_2
|
|
640
|
+
// ā
Result: TRUE (Success)
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Stack State Analysis
|
|
644
|
+
|
|
645
|
+
**Manual stack inspection and formatting**
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
const stack = [Buffer.from('01', 'hex'), Buffer.from('02', 'hex')]
|
|
649
|
+
const altstack = [Buffer.from('ff', 'hex')]
|
|
650
|
+
|
|
651
|
+
SmartContract.printStack(stack, altstack)
|
|
652
|
+
// Output:
|
|
653
|
+
// Stack: ['01', '02']
|
|
654
|
+
// AltStack: ['ff']
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Advanced Script Analysis
|
|
658
|
+
|
|
659
|
+
**Comprehensive script analysis and optimization**
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
// Analyze script complexity and find optimizations
|
|
663
|
+
const script = bsv.Script.fromASM("OP_DUP OP_DUP OP_DROP OP_HASH160")
|
|
664
|
+
|
|
665
|
+
const analysis = SmartContract.analyzeComplexity(script)
|
|
666
|
+
console.log('Complexity score:', analysis.score)
|
|
667
|
+
console.log('Optimization suggestions:', analysis.suggestions)
|
|
668
|
+
|
|
669
|
+
const optimized = SmartContract.optimizeScript(script)
|
|
670
|
+
console.log('Original ASM:', script.toASM())
|
|
671
|
+
console.log('Optimized ASM:', optimized.toASM())
|
|
672
|
+
|
|
673
|
+
// Explain script in human-readable format
|
|
674
|
+
const explanation = SmartContract.explainScript(script)
|
|
675
|
+
console.log('Script explanation:', explanation)
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Batch Testing
|
|
679
|
+
|
|
680
|
+
**Test multiple scripts efficiently**
|
|
681
|
+
|
|
682
|
+
```javascript
|
|
683
|
+
const scripts = [
|
|
684
|
+
{
|
|
685
|
+
name: 'P2PKH Test',
|
|
686
|
+
unlocking: '47304402...',
|
|
687
|
+
locking: '76a914...88ac',
|
|
688
|
+
expectedResult: true
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: 'Multi-sig Test',
|
|
692
|
+
unlocking: '004730440...',
|
|
693
|
+
locking: '52210...53ae',
|
|
694
|
+
expectedResult: true
|
|
695
|
+
}
|
|
696
|
+
]
|
|
697
|
+
|
|
698
|
+
const results = SmartContract.batchTestScripts(scripts, {
|
|
699
|
+
verbose: true,
|
|
700
|
+
stopOnFailure: false
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
console.log('Batch test results:', results)
|
|
704
|
+
// { passed: 2, failed: 0, results: [...] }
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
601
709
|
## Real-World Examples
|
|
602
710
|
|
|
603
711
|
### Example 1: Value-Preserving Covenant
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
* - ScriptTester: Local script execution and debugging capabilities
|
|
16
16
|
* - CovenantBuilder: JavaScript-to-Bitcoin Script covenant generator
|
|
17
17
|
* - OpcodeMap: Comprehensive Bitcoin Script opcode mapping
|
|
18
|
+
* - ScriptUtils: Script analysis, conversion, and optimization utilities
|
|
19
|
+
* - StackExaminer: Stack state examination and step-by-step execution debugging
|
|
20
|
+
* - ScriptInterpreter: Interactive script debugging with full verification support
|
|
18
21
|
*/
|
|
19
22
|
|
|
20
23
|
'use strict'
|
|
@@ -31,7 +34,9 @@ var SmartContract = {
|
|
|
31
34
|
CovenantBuilder: require('./covenant_builder').CovenantBuilder,
|
|
32
35
|
CovenantTemplates: require('./covenant_builder').CovenantTemplates,
|
|
33
36
|
OpcodeMap: require('./opcode_map'),
|
|
34
|
-
ScriptUtils: require('./script_utils')
|
|
37
|
+
ScriptUtils: require('./script_utils'),
|
|
38
|
+
StackExaminer: require('./stack_examiner'),
|
|
39
|
+
ScriptInterpreter: require('./script_interpreter')
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
// Convenience constructor methods
|
|
@@ -184,6 +189,36 @@ SmartContract.batchTestScripts = function(scripts, options) {
|
|
|
184
189
|
return SmartContract.ScriptUtils.batchTestScripts(scripts, options)
|
|
185
190
|
}
|
|
186
191
|
|
|
192
|
+
// Debug and analysis tools
|
|
193
|
+
SmartContract.examineStack = function(lockingHex, unlockingHex) {
|
|
194
|
+
return SmartContract.StackExaminer.runScript(lockingHex, unlockingHex)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
SmartContract.debugScriptExecution = function(unlockingScript, lockingScript, options) {
|
|
198
|
+
options = options || {}
|
|
199
|
+
if (options.stepMode) {
|
|
200
|
+
return SmartContract.ScriptInterpreter.stepThroughScript(
|
|
201
|
+
SmartContract.ScriptInterpreter.parseScript(unlockingScript + ' ' + lockingScript),
|
|
202
|
+
new bsv.Transaction(),
|
|
203
|
+
true
|
|
204
|
+
)
|
|
205
|
+
} else {
|
|
206
|
+
return SmartContract.ScriptInterpreter.runFullEvaluation(
|
|
207
|
+
SmartContract.ScriptInterpreter.parseScript(unlockingScript),
|
|
208
|
+
SmartContract.ScriptInterpreter.parseScript(lockingScript),
|
|
209
|
+
new bsv.Transaction()
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
SmartContract.parseScript = function(scriptInput) {
|
|
215
|
+
return SmartContract.ScriptInterpreter.parseScript(scriptInput)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
SmartContract.printStack = function(stack, altstack) {
|
|
219
|
+
return SmartContract.ScriptInterpreter.printStack(stack, altstack)
|
|
220
|
+
}
|
|
221
|
+
|
|
187
222
|
// Quick covenant creation utility
|
|
188
223
|
SmartContract.createQuickCovenant = function(type, params) {
|
|
189
224
|
const builder = SmartContract.createCovenantBuilder()
|
|
@@ -305,6 +340,10 @@ SmartContract.features = {
|
|
|
305
340
|
BATCH_TESTING: true,
|
|
306
341
|
QUICK_COVENANTS: true,
|
|
307
342
|
SCRIPT_EXPLANATIONS: true,
|
|
343
|
+
STACK_EXAMINATION: true,
|
|
344
|
+
SCRIPT_DEBUGGING: true,
|
|
345
|
+
STEP_BY_STEP_EXECUTION: true,
|
|
346
|
+
INTERACTIVE_DEBUGGING: true,
|
|
308
347
|
PRODUCTION_READY: true
|
|
309
348
|
}
|
|
310
349
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* script_interpreter_debugger.js
|
|
3
|
+
* ---------------------------------------------------------------
|
|
4
|
+
* Universal Bitcoin Script Interpreter Debugger for BSV
|
|
5
|
+
* Browser + Node.js compatible
|
|
6
|
+
* ---------------------------------------------------------------
|
|
7
|
+
* Usage:
|
|
8
|
+
* node script_interpreter_debugger.js [options]
|
|
9
|
+
*
|
|
10
|
+
* Options:
|
|
11
|
+
* --unlocking <hex|asm> Unlocking script (input scriptSig)
|
|
12
|
+
* --locking <hex|asm> Locking script (output scriptPubKey)
|
|
13
|
+
* --combined <hex|asm> Combined script (unlocking + locking)
|
|
14
|
+
* --step Step through opcode-by-opcode
|
|
15
|
+
* --truth Run full verification (TRUE/FALSE)
|
|
16
|
+
*
|
|
17
|
+
* Examples:
|
|
18
|
+
* node script_interpreter_debugger.js --locking "76a91489abcdefabbaabbaabbaabbaabbaabbaabbaabba88ac" --unlocking "4104abcd1234...ac" --truth
|
|
19
|
+
* node script_interpreter_debugger.js --combined "OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG" --step
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// Browser-safe requires
|
|
23
|
+
let readline = null;
|
|
24
|
+
try {
|
|
25
|
+
// Only require readline in Node.js environment
|
|
26
|
+
if (typeof window === 'undefined' && typeof require !== 'undefined') {
|
|
27
|
+
readline = require("readline");
|
|
28
|
+
}
|
|
29
|
+
} catch (err) {
|
|
30
|
+
// Gracefully handle missing readline in browser
|
|
31
|
+
readline = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const bsv = (typeof window !== 'undefined' && window.bsv) ? window.bsv : require("../../index.js");
|
|
35
|
+
|
|
36
|
+
// Debug: Show Script Interpreter module structure (uncomment to inspect)
|
|
37
|
+
// const ScriptInterpreter = bsv.Script.Interpreter;
|
|
38
|
+
// console.log("Script Interpreter Module:", ScriptInterpreter);
|
|
39
|
+
/**
|
|
40
|
+
* Parse input as either ASM or HEX.
|
|
41
|
+
*/
|
|
42
|
+
function parseScript(input) {
|
|
43
|
+
if (!input) return new bsv.Script();
|
|
44
|
+
const trimmed = input.trim();
|
|
45
|
+
const isHex = /^[0-9a-fA-F]+$/.test(trimmed.replace(/\s+/g, ""));
|
|
46
|
+
try {
|
|
47
|
+
return isHex
|
|
48
|
+
? bsv.Script.fromHex(trimmed)
|
|
49
|
+
: bsv.Script.fromASM(trimmed);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error("ā Error parsing script:", err.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Display stack contents in readable format.
|
|
58
|
+
*/
|
|
59
|
+
function printStack(stack, altstack = []) {
|
|
60
|
+
const fmt = (b) => (b.length ? b.toString("hex") : "(empty)");
|
|
61
|
+
const top = stack.map(fmt);
|
|
62
|
+
console.log("Stack:", top.length ? top : ["(empty)"]);
|
|
63
|
+
if (altstack.length) console.log("AltStack:", altstack.map(fmt));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Execute script step-by-step with optional interactivity.
|
|
68
|
+
*/
|
|
69
|
+
async function stepThroughScript(combinedScript, tx, verbose = true) {
|
|
70
|
+
const interpreter = new bsv.Script.Interpreter();
|
|
71
|
+
interpreter.script = combinedScript;
|
|
72
|
+
interpreter.tx = tx;
|
|
73
|
+
interpreter.nIn = 0;
|
|
74
|
+
interpreter.flags = bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID;
|
|
75
|
+
|
|
76
|
+
const chunks = combinedScript.chunks;
|
|
77
|
+
|
|
78
|
+
// Browser compatibility check for readline
|
|
79
|
+
let rl = null;
|
|
80
|
+
if (readline && typeof process !== 'undefined' && process.stdin) {
|
|
81
|
+
rl = readline.createInterface({
|
|
82
|
+
input: process.stdin,
|
|
83
|
+
output: process.stdout,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log("===========================================");
|
|
88
|
+
console.log("š SCRIPT INTERPRETER DEBUGGER (STEP MODE)");
|
|
89
|
+
console.log("===========================================\n");
|
|
90
|
+
console.log("Script:", combinedScript.toASM());
|
|
91
|
+
console.log("-------------------------------------------");
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
94
|
+
const chunk = chunks[i];
|
|
95
|
+
const opname = bsv.Opcode.reverseMap[chunk.opcodenum] || "PUSH";
|
|
96
|
+
|
|
97
|
+
if (verbose && rl) {
|
|
98
|
+
await new Promise((resolve) =>
|
|
99
|
+
rl.question(`\n[${i + 1}/${chunks.length}] Step (${opname}) ā press Enter to execute...`, resolve)
|
|
100
|
+
);
|
|
101
|
+
} else if (verbose) {
|
|
102
|
+
console.log(`\n[${i + 1}/${chunks.length}] Step (${opname})`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Check if step method exists (compatibility check)
|
|
107
|
+
if (typeof interpreter.step === 'function') {
|
|
108
|
+
interpreter.step();
|
|
109
|
+
} else {
|
|
110
|
+
console.log("ā ļø Step-by-step execution not supported in this BSV version");
|
|
111
|
+
console.log("š” Consider using runFullEvaluation instead");
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`š§© Executed: ${opname}`);
|
|
116
|
+
printStack(interpreter.stack, interpreter.altstack);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.log(`ā ļø Error executing ${opname}: ${err.message}`);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (rl) rl.close();
|
|
124
|
+
console.log("\n===========================================");
|
|
125
|
+
console.log("ā
Final Stack State:");
|
|
126
|
+
printStack(interpreter.stack || [], interpreter.altstack || []);
|
|
127
|
+
console.log("===========================================");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Run complete truth evaluation (non-interactive)
|
|
132
|
+
*/
|
|
133
|
+
function runFullEvaluation(unlockingScript, lockingScript, tx) {
|
|
134
|
+
console.log("===========================================");
|
|
135
|
+
console.log("š SCRIPT INTERPRETER DEBUGGER (FULL RUN)");
|
|
136
|
+
console.log("===========================================\n");
|
|
137
|
+
|
|
138
|
+
const interpreter = new bsv.Script.Interpreter();
|
|
139
|
+
|
|
140
|
+
// For smartledger-bsv, need to provide satoshisBN parameter
|
|
141
|
+
const satoshisBN = new bsv.crypto.BN(100000); // 100,000 satoshis
|
|
142
|
+
|
|
143
|
+
const verified = interpreter.verify(
|
|
144
|
+
unlockingScript,
|
|
145
|
+
lockingScript,
|
|
146
|
+
tx,
|
|
147
|
+
0,
|
|
148
|
+
bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID,
|
|
149
|
+
satoshisBN
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
console.log("š Locking Script:", lockingScript.toASM());
|
|
153
|
+
console.log("š Unlocking Script:", unlockingScript.toASM());
|
|
154
|
+
console.log("-------------------------------------------");
|
|
155
|
+
console.log("ā
Result:", verified ? "TRUE (Success)" : "ā FALSE (Failure)");
|
|
156
|
+
console.log("Final Stack:", interpreter.stack.map((b) => b.toString("hex")));
|
|
157
|
+
console.log("AltStack:", interpreter.altstack.map((b) => b.toString("hex")));
|
|
158
|
+
console.log("===========================================");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Main entrypoint
|
|
163
|
+
*/
|
|
164
|
+
async function main() {
|
|
165
|
+
// Browser compatibility check
|
|
166
|
+
if (typeof process === 'undefined' || !process.argv) {
|
|
167
|
+
console.log("āļø CLI mode not available in browser environment");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const args = process.argv.slice(2);
|
|
172
|
+
const getArg = (name) => {
|
|
173
|
+
const idx = args.indexOf(name);
|
|
174
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const unlockingInput = getArg("--unlocking");
|
|
178
|
+
const lockingInput = getArg("--locking");
|
|
179
|
+
const combinedInput = getArg("--combined");
|
|
180
|
+
const stepMode = args.includes("--step");
|
|
181
|
+
const truthMode = args.includes("--truth");
|
|
182
|
+
|
|
183
|
+
// Build scripts
|
|
184
|
+
let unlockingScript, lockingScript, combinedScript;
|
|
185
|
+
if (combinedInput) {
|
|
186
|
+
combinedScript = parseScript(combinedInput);
|
|
187
|
+
} else {
|
|
188
|
+
unlockingScript = parseScript(unlockingInput);
|
|
189
|
+
lockingScript = parseScript(lockingInput);
|
|
190
|
+
const combinedBuf = Buffer.concat([
|
|
191
|
+
unlockingScript.toBuffer(),
|
|
192
|
+
lockingScript.toBuffer(),
|
|
193
|
+
]);
|
|
194
|
+
combinedScript = bsv.Script.fromBuffer(combinedBuf);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Dummy TX context for interpreter
|
|
198
|
+
const tx = new bsv.Transaction();
|
|
199
|
+
|
|
200
|
+
// Create proper input with UTXO information
|
|
201
|
+
const dummyInput = new bsv.Transaction.Input({
|
|
202
|
+
prevTxId: '0'.repeat(64),
|
|
203
|
+
outputIndex: 0,
|
|
204
|
+
script: bsv.Script.empty(),
|
|
205
|
+
satoshis: 100000,
|
|
206
|
+
output: new bsv.Transaction.Output({
|
|
207
|
+
satoshis: 100000,
|
|
208
|
+
script: bsv.Script.empty()
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
tx.addInput(dummyInput);
|
|
213
|
+
tx.addOutput(new bsv.Transaction.Output({ satoshis: 100000, script: bsv.Script.empty() }));
|
|
214
|
+
|
|
215
|
+
if (stepMode) {
|
|
216
|
+
await stepThroughScript(combinedScript, tx, true);
|
|
217
|
+
} else if (truthMode) {
|
|
218
|
+
runFullEvaluation(unlockingScript || bsv.Script.empty(), lockingScript || bsv.Script.empty(), tx);
|
|
219
|
+
} else {
|
|
220
|
+
console.log("āļø No mode specified. Use --step or --truth");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Export functions for module usage
|
|
225
|
+
module.exports = {
|
|
226
|
+
parseScript,
|
|
227
|
+
printStack,
|
|
228
|
+
stepThroughScript,
|
|
229
|
+
runFullEvaluation,
|
|
230
|
+
main
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// CLI execution
|
|
234
|
+
if (typeof require !== 'undefined' && require.main === module) {
|
|
235
|
+
main();
|
|
236
|
+
}
|
|
@@ -176,7 +176,7 @@ function scriptMetrics(script) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
const asm = validation.asm
|
|
179
|
-
const operations = asm.split(' ')
|
|
179
|
+
const operations = asm.split(' ').filter(op => op.length > 0) // Filter out empty strings
|
|
180
180
|
const size = validation.size
|
|
181
181
|
|
|
182
182
|
// Analyze operation types
|
|
@@ -199,9 +199,11 @@ function scriptMetrics(script) {
|
|
|
199
199
|
})
|
|
200
200
|
|
|
201
201
|
return {
|
|
202
|
-
|
|
202
|
+
length: size, // Changed from 'size' to 'length' for consistency
|
|
203
|
+
size: size, // Keep both for compatibility
|
|
203
204
|
operations: operations.length,
|
|
204
|
-
|
|
205
|
+
opcodeCount: opcodeCount, // Changed from 'opcodes' for clarity
|
|
206
|
+
opcodes: opcodeCount, // Keep both for compatibility
|
|
205
207
|
dataElements: dataCount,
|
|
206
208
|
estimatedCost: executionCost,
|
|
207
209
|
complexity: executionCost > 50 ? 'high' : executionCost > 10 ? 'medium' : 'low',
|
|
@@ -287,13 +289,23 @@ function optimizeScript(script) {
|
|
|
287
289
|
const originalSize = estimateScriptSize(asm)
|
|
288
290
|
const optimizedSize = estimateScriptSize(optimizedASM)
|
|
289
291
|
|
|
292
|
+
// Create script object from optimized ASM
|
|
293
|
+
let optimizedScript = null
|
|
294
|
+
try {
|
|
295
|
+
optimizedScript = bsv.Script.fromASM(optimizedASM)
|
|
296
|
+
} catch (err) {
|
|
297
|
+
// If we can't create a script object, that's okay
|
|
298
|
+
optimizedScript = null
|
|
299
|
+
}
|
|
300
|
+
|
|
290
301
|
return {
|
|
291
302
|
original: asm,
|
|
292
303
|
optimized: optimizedASM,
|
|
293
304
|
originalSize: originalSize,
|
|
294
305
|
optimizedSize: optimizedSize,
|
|
295
306
|
savings: originalSize - optimizedSize,
|
|
296
|
-
improvement: originalSize > 0 ? ((originalSize - optimizedSize) / originalSize * 100).toFixed(1) + '%' : '0%'
|
|
307
|
+
improvement: originalSize > 0 ? ((originalSize - optimizedSize) / originalSize * 100).toFixed(1) + '%' : '0%',
|
|
308
|
+
script: optimizedScript // Add the script object
|
|
297
309
|
}
|
|
298
310
|
} catch (error) {
|
|
299
311
|
return { error: error.message }
|
|
@@ -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
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartledger/bsv",
|
|
3
|
-
"version": "3.2.
|
|
4
|
-
"description": "Advanced Bitcoin SV library with JavaScript-to-Bitcoin Script framework - Complete BSV API + BIP143 preimage parsing + 121 opcode mapping + covenant builder + nChain PUSHTX techniques + custom script development",
|
|
3
|
+
"version": "3.2.1",
|
|
4
|
+
"description": "Advanced Bitcoin SV library with JavaScript-to-Bitcoin Script framework - Complete BSV API + BIP143 preimage parsing + 121 opcode mapping + covenant builder + nChain PUSHTX techniques + custom script development + integrated debugging tools",
|
|
5
5
|
"author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
|
|
6
6
|
"homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
|
|
7
7
|
"bugs": {
|