smartledger-bsv 3.4.0 ā 3.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +400 -0
- package/README.md +112 -84
- package/SECURITY.md +88 -0
- package/bin/cli.js +13 -8
- package/bsv-anchor.min.js +12 -0
- package/bsv-covenant.min.js +8 -8
- package/bsv-didweb.min.js +12 -0
- package/bsv-gdaf.min.js +9 -9
- package/bsv-ltp.min.js +9 -9
- package/bsv-mnemonic.min.js +2 -2
- package/bsv-shamir.min.js +3 -3
- package/bsv-smartcontract.min.js +5 -5
- package/bsv-statuslist.min.js +18 -0
- package/bsv-vcjwt.min.js +12 -0
- package/bsv.bundle.js +9 -9
- package/bsv.d.ts +486 -9
- package/bsv.min.js +5 -5
- package/build/webpack.anchor.config.js +9 -13
- package/build/webpack.didweb.config.js +10 -14
- package/build/webpack.statuslist.config.js +9 -14
- package/build/webpack.vcjwt.config.js +9 -13
- package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +2 -2
- package/docs/MODULE_REFERENCE_COMPLETE.md +61 -58
- package/docs/advanced/LEGAL_TOKEN_PROTOCOL.md +3 -3
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +30 -30
- package/docs/getting-started/QUICK_START.md +18 -18
- package/docs/migration/FROM_BSV_1_5_6.md +16 -10
- package/gdaf-entry.js +1 -2
- package/index.js +44 -13
- package/lib/browser-utxo-manager-es5.js +11 -4
- package/lib/browser-utxo-manager.js +15 -8
- package/lib/ltp/claim.js +1 -0
- package/lib/ltp/obligation.js +1 -0
- package/lib/ltp/registry.js +2 -0
- package/lib/ltp/right.js +1 -0
- package/lib/smart_contract/covenant.js +10 -1
- package/lib/smartutxo.js +20 -12
- package/lib/transaction/transaction.js +8 -1
- package/lib/util/_.js +7 -1
- package/ltp-entry.js +1 -2
- package/package.json +11 -13
- package/utilities/blockchain-state.js +32 -23
- package/demos/README.md +0 -188
- package/demos/architecture_demo.js +0 -247
- package/demos/browser-test.html +0 -1208
- package/demos/bsv_wallet_demo.js +0 -242
- package/demos/complete_ltp_demo.js +0 -511
- package/demos/debug_tools_demo.js +0 -87
- package/demos/demo_features.js +0 -123
- package/demos/easy_interface_demo.js +0 -109
- package/demos/ecies_demo.js +0 -182
- package/demos/gdaf_core_test.js +0 -131
- package/demos/gdaf_demo.js +0 -237
- package/demos/ltp_demo.js +0 -361
- package/demos/ltp_primitives_demo.js +0 -403
- package/demos/message_demo.js +0 -209
- package/demos/preimage_separation_demo.js +0 -383
- package/demos/script_helper_demo.js +0 -289
- package/demos/security_demo.js +0 -287
- package/demos/shamir_demo.js +0 -121
- package/demos/simple_demo.js +0 -204
- package/demos/simple_p2pkh_demo.js +0 -169
- package/demos/simple_utxo_preimage_demo.js +0 -196
- package/demos/smart_contract_demo.html +0 -1347
- package/demos/smart_contract_demo.js +0 -910
- package/demos/utxo_generator_demo.js +0 -244
- package/demos/validation_pipeline_demo.js +0 -155
- package/demos/web3keys.html +0 -740
- package/examples/README.md +0 -200
- package/examples/basic/transaction-creation.js +0 -534
- package/examples/basic/transaction_signature_api_gap.js +0 -178
- package/examples/complete_workflow_demo.js +0 -783
- package/examples/covenants/advanced_covenant_demo.js +0 -219
- package/examples/covenants/covenant_interface_demo.js +0 -270
- package/examples/covenants/covenant_manual_signature_resolved.js +0 -212
- package/examples/covenants/covenant_signature_template.js +0 -117
- package/examples/covenants2/covenant_bidirectional_example.js +0 -262
- package/examples/covenants2/covenant_utils_demo.js +0 -120
- package/examples/covenants2/preimage_covenant_utils.js +0 -287
- package/examples/covenants2/production_integration.js +0 -256
- package/examples/data/covenant_utxos.json +0 -28
- package/examples/data/utxos.json +0 -26
- package/examples/definitive_working_demo.js +0 -261
- package/examples/final_working_contracts.js +0 -338
- package/examples/preimage/README.md +0 -178
- package/examples/preimage/extract_preimage_bidirectional.js +0 -421
- package/examples/preimage/generate_sample_preimage.js +0 -208
- package/examples/preimage/generate_sighash_examples.js +0 -152
- package/examples/preimage/parse_preimage.js +0 -117
- package/examples/preimage/test_preimage_extractor.js +0 -53
- package/examples/preimage/test_varint_extraction.js +0 -95
- package/examples/scripts/custom_script_helper_example.js +0 -273
- package/examples/scripts/custom_script_signature_test.js +0 -344
- package/examples/scripts/script_interpreter.js +0 -193
- package/examples/smart_contract/complete_workflow_demo.js +0 -343
- package/examples/smart_contract/covenant_builder_demo.js +0 -176
- package/examples/smart_contract/script_testing_integration.js +0 -198
- package/examples/smart_contract_templates.js +0 -718
- package/examples/working_smart_contracts.js +0 -348
- package/lib/smart_contract/test_integration.js +0 -269
- package/tests/browser-compatibility/README.md +0 -35
- package/tests/browser-compatibility/test-cdn-vs-local.html +0 -186
- package/tests/browser-compatibility/test-pbkdf2.html +0 -51
- package/tests/bundle-completeness-test.html +0 -131
- package/tests/bundle-demo.html +0 -476
- package/tests/smartcontract-test.html +0 -239
- package/tests/standalone-modules-test.html +0 -260
- package/tests/test.html +0 -612
- package/tests/test_builtin_verify.js +0 -117
- package/tests/test_debug_integration.js +0 -71
- package/tests/test_ecdsa_little.js +0 -70
- package/tests/test_shamir.js +0 -221
- package/tests/test_smartverify_der.js +0 -110
- package/tests/test_standalone_shamir.html +0 -83
- package/tests/unpkg-demo.html +0 -194
- package/utilities/blockchain-state.json +0 -118565
|
@@ -1,178 +0,0 @@
|
|
|
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.
|
|
@@ -1,421 +0,0 @@
|
|
|
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");
|