@smartledger/bsv 3.1.1 → 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,26 @@
1
+ [
2
+ {
3
+ "txid": "98697480789ca50f967b9b324b44838d6b256e0db22206bf5c58f02fa652c864",
4
+ "vout": 1,
5
+ "satoshis": 917852,
6
+ "script": "76a914c0a3c16fca8d5c0b6a5e7c09e2c12c5a7d8b6e9f88ac",
7
+ "status": "confirmed",
8
+ "height": 850234
9
+ },
10
+ {
11
+ "txid": "76543210abcdef0123456789abcdef0123456789abcdef0123456789abcdef01",
12
+ "vout": 0,
13
+ "satoshis": 250000,
14
+ "script": "76a914f0e9c5a4d8b6e7c9a8b7d6e5f4c3a2b1d9e8f7c688ac",
15
+ "status": "confirmed",
16
+ "height": 850235
17
+ },
18
+ {
19
+ "txid": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
20
+ "vout": 2,
21
+ "satoshis": 500000,
22
+ "script": "76a914a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b088ac",
23
+ "status": "confirmed",
24
+ "height": 850236
25
+ }
26
+ ]
@@ -0,0 +1,178 @@
1
+ # BIP-143 Preimage Tools
2
+
3
+ This directory contains tools and examples for working with Bitcoin BIP-143 transaction preimages.
4
+
5
+ ## Files
6
+
7
+ ### `extract_preimage_bidirectional.js`
8
+ **Advanced bidirectional preimage field extractor with optimal ASM generation**
9
+
10
+ Intelligently extracts any BIP-143 preimage field using the most efficient strategy:
11
+ - **LEFT extraction** for early fields (nVersion, hashPrevouts, etc.)
12
+ - **RIGHT extraction** for late fields (value, nLocktime, sighashType, etc.)
13
+ - **DYNAMIC extraction** for variable-length scriptCode
14
+
15
+ #### Features
16
+ - āœ… Complete BIP-143 specification compliance
17
+ - āœ… Optimal operation count for each field
18
+ - āœ… Generates production-ready Bitcoin Script ASM
19
+ - āœ… Handles variable scriptCode dynamically
20
+ - āœ… Robust error handling and validation
21
+ - āœ… Field interpretation (satoshis, version numbers, etc.)
22
+
23
+ #### Usage
24
+ ```bash
25
+ # Extract any preimage field
26
+ node extract_preimage_bidirectional.js <preimage_hex> <field_name>
27
+
28
+ # Examples
29
+ node extract_preimage_bidirectional.js 01000000ab12cd... scriptCode
30
+ node extract_preimage_bidirectional.js 01000000ab12cd... value
31
+ node extract_preimage_bidirectional.js 01000000ab12cd... sighashType
32
+ ```
33
+
34
+ #### Available Fields
35
+
36
+ **LEFT Fields (fixed offsets from start):**
37
+ - `nVersion` (4 bytes) - Transaction version
38
+ - `hashPrevouts` (32 bytes) - Hash of all input outpoints
39
+ - `hashSequence` (32 bytes) - Hash of all input sequences
40
+ - `outpoint_txid` (32 bytes) - Current input's previous transaction ID
41
+ - `outpoint_vout` (4 bytes) - Current input's previous output index
42
+ - `scriptLen` (1 byte) - Length of the scriptCode
43
+
44
+ **DYNAMIC Field (uses internal length):**
45
+ - `scriptCode` (variable) - The script being executed
46
+
47
+ **RIGHT Fields (fixed offsets from end):**
48
+ - `value` (8 bytes) - Input amount in satoshis
49
+ - `nSequence` (4 bytes) - Current input's sequence number
50
+ - `hashOutputs` (32 bytes) - Hash of all outputs
51
+ - `nLocktime` (4 bytes) - Transaction lock time
52
+ - `sighashType` (4 bytes) - Signature hash type
53
+
54
+ #### Integration with Covenant Framework
55
+
56
+ The generated ASM can be used directly in covenant locking scripts:
57
+
58
+ ```javascript
59
+ const { CovenantInterface } = require('../../lib/covenant-interface');
60
+
61
+ // Use bidirectional extraction in covenant
62
+ const covenant = new CovenantInterface();
63
+ const script = covenant.createAdvancedCovenant({
64
+ type: 'custom',
65
+ rules: {
66
+ // Insert generated ASM for preimage field extraction
67
+ extractValue: `
68
+ # Generated by extract_preimage_bidirectional.js
69
+ OP_SIZE
70
+ 52 OP_SUB
71
+ OP_SPLIT
72
+ OP_DROP
73
+ 8 OP_SPLIT
74
+ OP_DROP
75
+ `
76
+ }
77
+ });
78
+ ```
79
+
80
+ #### BIP-143 Structure Reference
81
+
82
+ ```
83
+ Total: LEFT(105) + scriptCode(variable) + RIGHT(52) bytes
84
+
85
+ LEFT ZONE (105 bytes): DYNAMIC: RIGHT ZONE (52 bytes):
86
+ ā”œā”€ nVersion (4) ā”œā”€ scriptCode ā”œā”€ value (8)
87
+ ā”œā”€ hashPrevouts (32) └─ (scriptLen bytes) ā”œā”€ nSequence (4)
88
+ ā”œā”€ hashSequence (32) ā”œā”€ hashOutputs (32)
89
+ ā”œā”€ outpoint_txid (32) ā”œā”€ nLocktime (4)
90
+ ā”œā”€ outpoint_vout (4) └─ sighashType (4)
91
+ └─ scriptLen (1)
92
+ ```
93
+
94
+ ## See Also
95
+ - `../../docs/preimage.md` - BIP-143 technical specification
96
+ - `../../docs/ADVANCED_COVENANT_DEVELOPMENT.md` - Covenant implementation guide
97
+ - `../covenants/` - Complete covenant examples using preimage extraction
98
+ - `../../lib/covenant-interface.js` - CovenantPreimage class implementation
99
+
100
+ ### `generate_sighash_examples.js`
101
+ **SIGHASH flag preimage generator demonstrating "zero hash" behavior**
102
+
103
+ Explains why multi-input transactions appear to have "extra zeros" - they're not bugs but required by BIP-143 SIGHASH rules:
104
+
105
+ #### Features
106
+ - āœ… Complete SIGHASH flag support (ALL, NONE, SINGLE, ANYONECANPAY)
107
+ - āœ… Demonstrates zero hash field behavior
108
+ - āœ… Educational tool for multi-input transaction analysis
109
+ - āœ… Integration with bidirectional extractor
110
+
111
+ #### Usage
112
+ ```bash
113
+ # Generate SIGHASH examples
114
+ node generate_sighash_examples.js [sighash_type]
115
+
116
+ # Examples showing zero hash behavior
117
+ node generate_sighash_examples.js ALL_ANYONECANPAY_FORKID # Zero hashPrevouts + hashSequence
118
+ node generate_sighash_examples.js NONE_FORKID # Zero hashOutputs
119
+ node generate_sighash_examples.js NONE_ANYONECANPAY_FORKID # Zero all hash fields
120
+ ```
121
+
122
+ #### SIGHASH Flag Behavior
123
+ | SIGHASH Type | hashPrevouts | hashSequence | hashOutputs | Use Case |
124
+ |--------------|-------------|-------------|-------------|-----------|
125
+ | ALL_FORKID | Normal | Normal | Normal | Standard signing |
126
+ | NONE_FORKID | Normal | Normal | **Zero** | Blank check |
127
+ | SINGLE_FORKID | Normal | Normal | **Zero** | One-to-one |
128
+ | ALL_ANYONECANPAY_FORKID | **Zero** | **Zero** | Normal | Crowdfunding |
129
+ | NONE_ANYONECANPAY_FORKID | **Zero** | **Zero** | **Zero** | Maximum flexibility |
130
+
131
+ ### `test_varint_extraction.js`
132
+ **Comprehensive CompactSize varint test suite**
133
+
134
+ Validates proper parsing of 1-3 byte CompactSize varints in real-world scenarios:
135
+ - 1-byte encoding: 0-252 bytes (most common)
136
+ - 3-byte encoding: 253-65535 bytes (large scripts)
137
+ - 5-byte encoding: 65536+ bytes (huge scripts)
138
+
139
+ ## 🧠 **Understanding the "Extra Zero" Mystery**
140
+
141
+ ### The Problem
142
+ Developers often see patterns like this in BSV preimages and think it's a bug:
143
+ ```
144
+ ...1976a914000000000000...
145
+ ```
146
+
147
+ ### The Reality
148
+ These zeros are **intentional** and required by Bitcoin's BIP-143 specification:
149
+
150
+ 1. **CompactSize Varint Encoding**
151
+ - Scripts ≄253 bytes use 3-byte encoding: `0xFD + 2-byte length`
152
+ - This adds "extra bytes" that look like zeros to developers expecting 1-byte
153
+
154
+ 2. **SIGHASH Flag Behavior**
155
+ - `ANYONECANPAY`: Zeros out `hashPrevouts` and `hashSequence` (64 zero bytes)
156
+ - `NONE`: Zeros out `hashOutputs` (32 zero bytes)
157
+ - `SINGLE`: Zeros out `hashOutputs` for unmatched outputs
158
+
159
+ 3. **Multi-Input Concatenation Issues**
160
+ - Wrong byte boundary slicing between inputs
161
+ - Reusing preimage buffers without proper re-serialization
162
+
163
+ ### The Solution
164
+ Our bidirectional extractor handles all these cases:
165
+ - āœ… **Auto-detects** CompactSize varint size (1-3 bytes)
166
+ - āœ… **Warns about** zero hashes with SIGHASH flag context
167
+ - āœ… **Validates** preimage structure against BIP-143
168
+ - āœ… **Generates** optimal ASM regardless of complexity
169
+
170
+ ## Performance Notes
171
+
172
+ The enhanced bidirectional strategy minimizes script operations:
173
+ - **LEFT fields**: Direct offset splitting (2-3 operations)
174
+ - **RIGHT fields**: Size-based splitting (3-4 operations)
175
+ - **scriptCode**: Dynamic extraction using CompactSize varint (3-4 operations)
176
+ - **Zero hash detection**: Automatic with contextual warnings
177
+
178
+ This is significantly more efficient than linear parsing and handles all BIP-143 edge cases that break traditional parsers.
@@ -0,0 +1,421 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * extract_preimage_bidirectional.js
4
+ * ---------------------------------------------------------------------
5
+ * Extract any part of the transaction preimage intelligently by slicing
6
+ * from either LEFT or RIGHT based on what is known.
7
+ * Handles variable scriptCode length via dynamic slicing.
8
+ *
9
+ * Part of the Preimage Covenant Tools - implements bidirectional slicing
10
+ * strategy for optimal preimage field extraction in Bitcoin Script.
11
+ *
12
+ * Usage:
13
+ * node extract_preimage_bidirectional.js <raw_preimage_hex> <field_name>
14
+ *
15
+ * Example:
16
+ * node extract_preimage_bidirectional.js 01000000ab12... scriptCode
17
+ */
18
+
19
+ const { Buffer } = require("buffer");
20
+
21
+ // Fixed field definitions - LEFT side (known offsets from start)
22
+ const LEFT_FIXED_FIELDS = [
23
+ { name: "nVersion", len: 4 },
24
+ { name: "hashPrevouts", len: 32 },
25
+ { name: "hashSequence", len: 32 },
26
+ { name: "outpoint_txid", len: 32 },
27
+ { name: "outpoint_vout", len: 4 },
28
+ // Note: scriptLen is VARIABLE (1-3 bytes CompactSize varint) - handled separately
29
+ ];
30
+
31
+ // Fixed field definitions - RIGHT side (known offsets from end)
32
+ const RIGHT_FIXED_FIELDS = [
33
+ { name: "value", len: 8 },
34
+ { name: "nSequence", len: 4 },
35
+ { name: "hashOutputs", len: 32 },
36
+ { name: "nLocktime", len: 4 },
37
+ { name: "sighashType", len: 4 },
38
+ ];
39
+
40
+ // Calculate total bytes on each side
41
+ const LEFT_FIXED_TOTAL = LEFT_FIXED_FIELDS.reduce((a, f) => a + f.len, 0); // 104 bytes (without scriptLen varint)
42
+ const RIGHT_TOTAL = RIGHT_FIXED_FIELDS.reduce((a, f) => a + f.len, 0); // 52 bytes
43
+
44
+ // CompactSize varint decoder for scriptLen
45
+ function decodeCompactSize(buf, offset) {
46
+ if (offset >= buf.length) return { value: 0, size: 1 };
47
+
48
+ const firstByte = buf[offset];
49
+
50
+ if (firstByte < 0xfd) {
51
+ // 1-byte encoding: 0-252
52
+ return { value: firstByte, size: 1 };
53
+ } else if (firstByte === 0xfd) {
54
+ // 3-byte encoding: 0xfd + 2 bytes little-endian
55
+ if (offset + 2 >= buf.length) return { value: 0, size: 1 };
56
+ const value = buf.readUInt16LE(offset + 1);
57
+ return { value, size: 3 };
58
+ } else if (firstByte === 0xfe) {
59
+ // 5-byte encoding: 0xfe + 4 bytes little-endian
60
+ if (offset + 4 >= buf.length) return { value: 0, size: 1 };
61
+ const value = buf.readUInt32LE(offset + 1);
62
+ return { value, size: 5 };
63
+ } else {
64
+ // 0xff = 9-byte encoding (not used for script lengths)
65
+ throw new Error('Invalid CompactSize varint: 8-byte integers not supported for scriptLen');
66
+ }
67
+ }
68
+
69
+ // Helper functions
70
+ function safeSlice(buf, start, end) {
71
+ if (start >= buf.length) return Buffer.alloc(0);
72
+ if (end > buf.length) end = buf.length;
73
+ return buf.slice(start, end);
74
+ }
75
+
76
+ function parsePreimage(hex) {
77
+ const buf = Buffer.from(hex, "hex");
78
+ let offset = 0;
79
+ const parsed = {};
80
+
81
+ // Parse LEFT fixed fields (104 bytes)
82
+ for (const f of LEFT_FIXED_FIELDS) {
83
+ const part = safeSlice(buf, offset, offset + f.len);
84
+ parsed[f.name] = part.toString("hex");
85
+ offset += f.len;
86
+ }
87
+
88
+ // Parse CompactSize varint for scriptLen
89
+ const scriptLenInfo = decodeCompactSize(buf, offset);
90
+ parsed.scriptLen = scriptLenInfo.value;
91
+ parsed.scriptLenSize = scriptLenInfo.size; // Track varint encoding size
92
+ parsed.scriptLenRaw = safeSlice(buf, offset, offset + scriptLenInfo.size).toString("hex");
93
+ offset += scriptLenInfo.size;
94
+
95
+ // Parse variable scriptCode using decoded scriptLen
96
+ const scriptCode = safeSlice(buf, offset, offset + parsed.scriptLen);
97
+ parsed.scriptCode = scriptCode.toString("hex");
98
+ offset += parsed.scriptLen;
99
+
100
+ // Parse RIGHT fields
101
+ for (const f of RIGHT_FIXED_FIELDS) {
102
+ const part = safeSlice(buf, offset, offset + f.len);
103
+ parsed[f.name] = part.toString("hex");
104
+ offset += f.len;
105
+ }
106
+
107
+ // Add structure info for analysis
108
+ parsed._structure = {
109
+ leftFixed: LEFT_FIXED_TOTAL,
110
+ scriptLenVarint: scriptLenInfo.size,
111
+ scriptCode: parsed.scriptLen,
112
+ rightFixed: RIGHT_TOTAL,
113
+ totalCalculated: LEFT_FIXED_TOTAL + scriptLenInfo.size + parsed.scriptLen + RIGHT_TOTAL,
114
+ totalActual: buf.length
115
+ };
116
+
117
+ return parsed;
118
+ }
119
+
120
+ function generateBidirectionalASM(field, preimageLength, parsed) {
121
+ console.log(`\n🧠 Bidirectional Analysis for "${field}"`);
122
+ console.log('='.repeat(60));
123
+
124
+ // Determine extraction strategy
125
+ const rightFields = RIGHT_FIXED_FIELDS.map(f => f.name);
126
+ const leftFields = LEFT_FIXED_FIELDS.map(f => f.name);
127
+
128
+ const isRightField = rightFields.includes(field);
129
+ const isLeftField = leftFields.includes(field);
130
+ const isDynamic = field === 'scriptCode';
131
+ const isScriptLen = field === 'scriptLen';
132
+
133
+ if (isRightField) {
134
+ return generateRightExtractionASM(field, preimageLength);
135
+ } else if (isLeftField) {
136
+ return generateLeftExtractionASM(field);
137
+ } else if (isDynamic) {
138
+ return generateDynamicExtractionASM(field, parsed);
139
+ } else if (isScriptLen) {
140
+ return generateScriptLenExtractionASM(parsed);
141
+ } else {
142
+ throw new Error(`Unknown field: ${field}`);
143
+ }
144
+ }
145
+
146
+ function generateRightExtractionASM(field, preimageLength) {
147
+ // Calculate offset from end
148
+ let offsetFromEnd = 0;
149
+ let targetLen = 0;
150
+
151
+ for (const f of RIGHT_FIXED_FIELDS) {
152
+ if (f.name === field) {
153
+ targetLen = f.len;
154
+ break;
155
+ }
156
+ offsetFromEnd += f.len;
157
+ }
158
+
159
+ console.log(`šŸ“ Strategy: Extract from RIGHT side`);
160
+ console.log(` - Total preimage: ${preimageLength} bytes`);
161
+ console.log(` - Right zone: ${RIGHT_TOTAL} bytes`);
162
+ console.log(` - Offset from end: ${offsetFromEnd} bytes`);
163
+ console.log(` - Field length: ${targetLen} bytes`);
164
+
165
+ const asm = [
166
+ `# šŸ”„ Extract ${field} from RIGHT side (bidirectional strategy)`,
167
+ `OP_SIZE # Push preimage size: [preimage, size]`,
168
+ `${RIGHT_TOTAL - offsetFromEnd} OP_SUB # Calculate split point: [preimage, split_point]`,
169
+ `OP_SPLIT # Split: [left_part, right_part]`,
170
+ `OP_DROP # Drop left: [right_part]`,
171
+ `${targetLen} OP_SPLIT # Extract field: [remaining, ${field}]`,
172
+ `OP_DROP # Clean up: [${field}]`,
173
+ `# āœ… Result: ${field} is now on top of stack`
174
+ ].join('\n');
175
+
176
+ return asm;
177
+ }
178
+
179
+ function generateLeftExtractionASM(field) {
180
+ // Calculate offset from start
181
+ let offsetFromStart = 0;
182
+ let targetLen = 0;
183
+
184
+ for (const f of LEFT_FIXED_FIELDS) {
185
+ if (f.name === field) {
186
+ targetLen = f.len;
187
+ break;
188
+ }
189
+ offsetFromStart += f.len;
190
+ }
191
+
192
+ console.log(`šŸ“ Strategy: Extract from LEFT side`);
193
+ console.log(` - Left fixed zone: ${LEFT_FIXED_TOTAL} bytes`);
194
+ console.log(` - Offset from start: ${offsetFromStart} bytes`);
195
+ console.log(` - Field length: ${targetLen} bytes`);
196
+
197
+ const asm = [
198
+ `# šŸ”„ Extract ${field} from LEFT side (bidirectional strategy)`,
199
+ `${offsetFromStart} OP_SPLIT # Skip to field: [prefix, remainder]`,
200
+ `OP_DROP # Drop prefix: [remainder]`,
201
+ `${targetLen} OP_SPLIT # Extract field: [${field}, suffix]`,
202
+ `OP_DROP # Clean up: [${field}]`,
203
+ `# āœ… Result: ${field} is now on top of stack`
204
+ ].join('\n');
205
+
206
+ return asm;
207
+ }
208
+
209
+ function generateDynamicExtractionASM(field, parsed) {
210
+ const leftZone = LEFT_FIXED_TOTAL + parsed.scriptLenSize;
211
+
212
+ console.log(`šŸ“ Strategy: Extract DYNAMIC field (uses CompactSize varint)`);
213
+ console.log(` - Left fixed zone: ${LEFT_FIXED_TOTAL} bytes`);
214
+ console.log(` - ScriptLen varint: ${parsed.scriptLenSize} bytes (${parsed.scriptLenRaw})`);
215
+ console.log(` - Script length: ${parsed.scriptLen} bytes`);
216
+ console.log(` - Skip zone total: ${leftZone} bytes`);
217
+
218
+ const asm = [
219
+ `# šŸŽÆ Extract ${field} DYNAMICALLY with CompactSize varint support`,
220
+ `${leftZone} OP_SPLIT # Skip left zone + scriptLen varint: [left_zone, remainder]`,
221
+ `OP_DROP # Drop left: [remainder]`,
222
+ `${parsed.scriptLen} OP_SPLIT # Extract scriptCode: [scriptCode, right_zone]`,
223
+ `OP_DROP # Clean up: [scriptCode]`,
224
+ `# āœ… Result: scriptCode extracted with ${parsed.scriptLenSize}-byte varint awareness`
225
+ ].join('\n');
226
+
227
+ return asm;
228
+ }
229
+
230
+ function generateScriptLenExtractionASM(parsed) {
231
+ console.log(`šŸ“ Strategy: Extract CompactSize scriptLen varint`);
232
+ console.log(` - Left fixed zone: ${LEFT_FIXED_TOTAL} bytes`);
233
+ console.log(` - Varint encoding: ${parsed.scriptLenSize} bytes`);
234
+ console.log(` - Raw varint: ${parsed.scriptLenRaw}`);
235
+ console.log(` - Decoded value: ${parsed.scriptLen}`);
236
+
237
+ const asm = [
238
+ `# šŸŽÆ Extract scriptLen CompactSize varint (${parsed.scriptLenSize} bytes)`,
239
+ `${LEFT_FIXED_TOTAL} OP_SPLIT # Skip left fixed fields: [left_zone, remainder]`,
240
+ `OP_DROP # Drop left: [remainder]`,
241
+ `${parsed.scriptLenSize} OP_SPLIT # Extract varint: [scriptLen_varint, suffix]`,
242
+ `OP_DROP # Clean up: [scriptLen_varint]`,
243
+ `# āœ… Result: CompactSize varint (decode off-chain to get ${parsed.scriptLen})`
244
+ ].join('\n');
245
+
246
+ return asm;
247
+ }
248
+
249
+ function simulateExtraction(buf, field) {
250
+ console.log(`\nšŸŽ¬ Simulating stack execution for "${field}"`);
251
+ console.log('='.repeat(60));
252
+
253
+ const parsed = parsePreimage(buf.toString('hex'));
254
+
255
+ if (parsed[field] !== undefined) {
256
+ const value = parsed[field];
257
+ console.log(`šŸ“¦ Extracted value: ${value}`);
258
+
259
+ // Add interpretations with CompactSize awareness
260
+ if (field === 'nVersion') {
261
+ const version = Buffer.from(value, 'hex').readUInt32LE(0);
262
+ console.log(` šŸ“ Interpreted: Version ${version}`);
263
+ } else if (field === 'value') {
264
+ if (value.length === 16) { // 8 bytes = 16 hex chars
265
+ const satoshis = Buffer.from(value, 'hex').readBigUInt64LE(0);
266
+ console.log(` šŸ“ Interpreted: ${satoshis} satoshis`);
267
+ }
268
+ } else if (field === 'sighashType') {
269
+ const sighashInt = Buffer.from(value, 'hex').readUInt32LE(0);
270
+ const types = {
271
+ 1: 'SIGHASH_ALL',
272
+ 65: 'SIGHASH_ALL | FORKID',
273
+ 2: 'SIGHASH_NONE',
274
+ 66: 'SIGHASH_NONE | FORKID',
275
+ 3: 'SIGHASH_SINGLE',
276
+ 67: 'SIGHASH_SINGLE | FORKID',
277
+ 129: 'SIGHASH_ALL | ANYONECANPAY',
278
+ 193: 'SIGHASH_ALL | ANYONECANPAY | FORKID'
279
+ };
280
+ console.log(` šŸ“ Interpreted: ${types[sighashInt] || `Custom (${sighashInt})`}`);
281
+ } else if (field === 'outpoint_vout') {
282
+ const vout = Buffer.from(value, 'hex').readUInt32LE(0);
283
+ console.log(` šŸ“ Interpreted: Output index ${vout}`);
284
+ } else if (field === 'scriptLen') {
285
+ console.log(` šŸ“ Interpreted: CompactSize varint (${parsed.scriptLenSize} bytes)`);
286
+ console.log(` šŸ“ Raw varint: ${parsed.scriptLenRaw}`);
287
+ console.log(` šŸ“ Decoded value: ${parsed.scriptLen} bytes`);
288
+
289
+ if (parsed.scriptLenSize === 1) {
290
+ console.log(` šŸ’” Standard encoding: value < 253 (0xFD)`);
291
+ } else if (parsed.scriptLenSize === 3) {
292
+ console.log(` šŸ’” Extended encoding: 0xFD + 2-byte little-endian`);
293
+ } else if (parsed.scriptLenSize === 5) {
294
+ console.log(` šŸ’” Long encoding: 0xFE + 4-byte little-endian`);
295
+ }
296
+ } else if (field === 'scriptCode') {
297
+ const scriptBuf = Buffer.from(value, 'hex');
298
+ if (scriptBuf.length === 25 && scriptBuf[0] === 0x76 && scriptBuf[1] === 0xa9) {
299
+ console.log(` šŸ“ Interpreted: Standard P2PKH script (25 bytes)`);
300
+ } else if (scriptBuf.length > 70 && scriptBuf[0] >= 0x51 && scriptBuf[0] <= 0x60) {
301
+ console.log(` šŸ“ Interpreted: Multisig script (${scriptBuf.length} bytes)`);
302
+ } else if (scriptBuf.length > 0 && scriptBuf[0] === 0x6a) {
303
+ console.log(` šŸ“ Interpreted: OP_RETURN data script (${scriptBuf.length} bytes)`);
304
+ } else {
305
+ console.log(` šŸ“ Interpreted: Custom script (${scriptBuf.length} bytes)`);
306
+ }
307
+ } else if (['hashPrevouts', 'hashSequence', 'hashOutputs'].includes(field)) {
308
+ if (value === '00'.repeat(32)) {
309
+ console.log(` āš ļø Zero hash detected - check SIGHASH flags (ANYONECANPAY, NONE, SINGLE)`);
310
+ } else {
311
+ console.log(` šŸ“ Interpreted: 32-byte hash (${value.substring(0, 16)}...)`);
312
+ }
313
+ }
314
+ } else {
315
+ console.log(`āŒ Field "${field}" not found in parsed preimage`);
316
+ }
317
+ }
318
+
319
+ // CLI Interface
320
+ if (process.argv.length < 4) {
321
+ console.log("🧠 Bidirectional Preimage Field Extractor v2.0");
322
+ console.log("=============================================");
323
+ console.log("✨ Now with CompactSize varint support for multi-input transactions!");
324
+ console.log("");
325
+ console.log("Usage: node extract_preimage_bidirectional.js <preimage_hex> <field_name>");
326
+ console.log("");
327
+ console.log("šŸ”„ LEFT Fields (fixed offsets from start):");
328
+ LEFT_FIXED_FIELDS.forEach(f => {
329
+ console.log(` - ${f.name.padEnd(16)} (${f.len} bytes)`);
330
+ });
331
+ console.log(" - scriptLen (1-3 bytes CompactSize varint)");
332
+ console.log("");
333
+ console.log("šŸŽÆ DYNAMIC Field (uses CompactSize scriptLen):");
334
+ console.log(` - scriptCode (variable, decoded from scriptLen varint)`);
335
+ console.log("");
336
+ console.log("šŸ”„ RIGHT Fields (fixed offsets from end):");
337
+ RIGHT_FIXED_FIELDS.forEach(f => {
338
+ console.log(` - ${f.name.padEnd(16)} (${f.len} bytes)`);
339
+ });
340
+ console.log("");
341
+ console.log("šŸ“– CompactSize Encoding:");
342
+ console.log(" < 253 (0xFD) → 1 byte");
343
+ console.log(" 253-65535 → 3 bytes (0xFD + 2-byte LE)");
344
+ console.log(" 65536-4294967295 → 5 bytes (0xFE + 4-byte LE)");
345
+ console.log("");
346
+ console.log("Examples:");
347
+ console.log(" node extract_preimage_bidirectional.js 01000000ab12cd... scriptCode");
348
+ console.log(" node extract_preimage_bidirectional.js 01000000ab12cd... scriptLen");
349
+ console.log(" node extract_preimage_bidirectional.js 01000000ab12cd... value");
350
+ process.exit(1);
351
+ }
352
+
353
+ const hex = process.argv[2];
354
+ const field = process.argv[3];
355
+
356
+ // Validate input
357
+ if (!/^[0-9a-fA-F]+$/.test(hex)) {
358
+ console.error("āŒ Invalid hex string. Please provide a valid hexadecimal preimage.");
359
+ process.exit(1);
360
+ }
361
+
362
+ if (hex.length < 200) {
363
+ console.error("āŒ Preimage too short. Expected at least 100+ bytes for valid BIP-143 preimage.");
364
+ process.exit(1);
365
+ }
366
+
367
+ try {
368
+ const buf = Buffer.from(hex, "hex");
369
+ const parsed = parsePreimage(hex);
370
+
371
+ console.log(`\nšŸ” Bidirectional Preimage Analysis`);
372
+ console.log('='.repeat(60));
373
+ console.log(`šŸ“Š Total preimage: ${buf.length} bytes (${hex.length} hex chars)`);
374
+ console.log(`šŸ“‹ Structure: LEFT(${parsed._structure.leftFixed}) + scriptLen(${parsed._structure.scriptLenVarint}) + scriptCode(${parsed._structure.scriptCode}) + RIGHT(${parsed._structure.rightFixed}) = ${parsed._structure.totalCalculated} bytes`);
375
+
376
+ // Validate structure with CompactSize awareness
377
+ if (buf.length !== parsed._structure.totalCalculated) {
378
+ console.log(`āš ļø Size mismatch: expected ${parsed._structure.totalCalculated}, got ${buf.length}`);
379
+ console.log(`šŸ’” Check: CompactSize varint encoding, script serialization, SIGHASH flags`);
380
+ } else {
381
+ console.log(`āœ… Structure validated: Perfect BIP-143 compliance`);
382
+ }
383
+
384
+ // Show CompactSize details
385
+ if (parsed.scriptLenSize > 1) {
386
+ console.log(`šŸ” CompactSize Details: ${parsed.scriptLen} bytes encoded as ${parsed.scriptLenSize}-byte varint (${parsed.scriptLenRaw})`);
387
+ }
388
+
389
+ // Generate and display ASM
390
+ const asm = generateBidirectionalASM(field, buf.length, parsed);
391
+ console.log(`\nšŸ“œ Generated ASM:`);
392
+ console.log(asm);
393
+
394
+ // Simulate extraction
395
+ simulateExtraction(buf, field);
396
+
397
+ } catch (error) {
398
+ console.error("āŒ Error:", error.message);
399
+ if (error.message.includes('Invalid hex')) {
400
+ console.error("šŸ’” Tip: Make sure your preimage is valid hexadecimal");
401
+ } else if (error.message.includes('Unknown field')) {
402
+ console.error("šŸ’” Tip: Check available fields with --help");
403
+ }
404
+ process.exit(1);
405
+ }
406
+
407
+ // Add helpful footer
408
+ console.log("\n" + "=".repeat(60));
409
+ console.log("šŸŽÆ Enhanced Bidirectional Strategy Benefits:");
410
+ console.log(" āœ… Optimal extraction direction for each field");
411
+ console.log(" āœ… CompactSize varint scriptLen support (1-3 bytes)");
412
+ console.log(" āœ… Handles multi-input transaction preimages");
413
+ console.log(" āœ… SIGHASH flag awareness (zero hash detection)");
414
+ console.log(" āœ… Self-contained (no external context needed)");
415
+ console.log(" āœ… Generates minimal ASM operations");
416
+ console.log("");
417
+ console.log("šŸ”— Integration with Covenant Tools:");
418
+ console.log(" - Use generated ASM in covenant locking scripts");
419
+ console.log(" - Verify preimage components with dynamic extraction");
420
+ console.log(" - Build advanced covenant patterns with field isolation");
421
+ console.log("šŸ“– See DOCUMENTATION.md for complete covenant implementation");