@smartledger/bsv 8.0.1 → 8.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.
- package/CHANGELOG.md +118 -0
- package/README.md +38 -38
- package/bsv-gdaf.min.js +47 -47
- package/bsv-ltp.min.js +1 -1
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +48 -48
- package/bsv.min.js +48 -48
- package/docs/AUDIT_SCOPE.md +13 -13
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/lib/gdaf/attestation-signer.js +90 -2
- package/lib/gdaf/attestation-verifier.js +41 -25
- package/lib/gdaf/zk-prover.js +143 -46
- package/lib/script/interpreter.js +20 -1
- package/package.json +3 -2
- package/test/gdaf/canonicalization.js +106 -0
- package/test/gdaf/zk_prover.js +204 -0
- package/test/script/genesis_limits.js +37 -6
- package/tools/minimal_reproduction.js +119 -0
- package/tools/opcode_map.js +342 -0
- package/tools/server.js +97 -0
- package/tools/simple_real_tx.js +136 -0
- package/tools/sv-sighash-harness.js +123 -0
- package/tools/sv-sighash-report.js +71 -0
- package/tools/sv-tx-harness.js +172 -0
- package/tools/sv-tx-report.js +43 -0
- package/tools/sv-vector-harness.js +304 -0
- package/tools/sv-vector-report.js +88 -0
- package/version.js +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* global describe, it, beforeEach, afterEach */
|
|
4
4
|
var should = require('chai').should()
|
|
5
5
|
var bsv = require('../..')
|
|
6
|
+
var BN = bsv.crypto.BN
|
|
6
7
|
var Interpreter = bsv.Script.Interpreter
|
|
7
8
|
var Script = bsv.Script
|
|
8
9
|
var Opcode = bsv.Opcode
|
|
@@ -28,11 +29,30 @@ describe('Interpreter post-Genesis limits', function () {
|
|
|
28
29
|
Interpreter.MAX_OPS_PER_SCRIPT.should.equal(500)
|
|
29
30
|
})
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// MAXIMUM_ELEMENT_SIZE is no longer among them. It is CScriptNum's max_length, and
|
|
33
|
+
// since the limits became era-derived it is only the PRE-Genesis fallback — raising it
|
|
34
|
+
// could not enable post-Genesis arithmetic, only corrupt pre-Genesis validation.
|
|
35
|
+
it('useGenesisLimits() lifts the size caps but not the script-number bound', function () {
|
|
32
36
|
Interpreter.useGenesisLimits()
|
|
33
37
|
Interpreter.MAX_SCRIPT_ELEMENT_SIZE.should.equal(0x7fffffff)
|
|
34
|
-
Interpreter.MAXIMUM_ELEMENT_SIZE.should.equal(0x7fffffff)
|
|
35
38
|
Interpreter.MAX_OPS_PER_SCRIPT.should.equal(0x7fffffff)
|
|
39
|
+
Interpreter.MAXIMUM_ELEMENT_SIZE.should.equal(4)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// The reason it must not be raised, stated as a test: a 5-byte operand is an overflow
|
|
43
|
+
// before Genesis, and no process-wide call may turn that into an accept.
|
|
44
|
+
it('cannot be made to accept a pre-Genesis script-number overflow', function () {
|
|
45
|
+
var overflow = new Script()
|
|
46
|
+
.add(Buffer.from('1234567890', 'hex'))
|
|
47
|
+
.add(Buffer.from([2]))
|
|
48
|
+
.add(Opcode.OP_MUL)
|
|
49
|
+
var flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
50
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES | Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES
|
|
51
|
+
Interpreter.useGenesisLimits()
|
|
52
|
+
var interp = new Interpreter()
|
|
53
|
+
interp.verify(new Script(), overflow, new bsv.Transaction(), 0, flags, new BN(0))
|
|
54
|
+
.should.equal(false)
|
|
55
|
+
interp.errstr.should.equal('SCRIPT_ERR_SCRIPTNUM_OVERFLOW')
|
|
36
56
|
})
|
|
37
57
|
|
|
38
58
|
// <2^32> <2^32> OP_ADD <2^33> OP_NUMEQUAL — operands exceed the 4-byte cap.
|
|
@@ -54,9 +74,18 @@ describe('Interpreter post-Genesis limits', function () {
|
|
|
54
74
|
run(bigAdd).should.equal(false)
|
|
55
75
|
})
|
|
56
76
|
|
|
57
|
-
|
|
77
|
+
// Post-Genesis arithmetic is reached through the ERA, not through the static. Before
|
|
78
|
+
// the limits became era-derived, useGenesisLimits() was the only route; it no longer
|
|
79
|
+
// is, and using it for this would corrupt pre-Genesis validation elsewhere.
|
|
80
|
+
it('allows >4-byte arithmetic when the post-Genesis era is asked for', function () {
|
|
81
|
+
var interp = new Interpreter()
|
|
82
|
+
interp.verify(new Script(), bigAdd, new bsv.Transaction(), 0,
|
|
83
|
+
Interpreter.mainnetFlags(), new BN(0)).should.equal(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('still rejects it under pre-Genesis rules, whatever the statics say', function () {
|
|
58
87
|
Interpreter.useGenesisLimits()
|
|
59
|
-
run(bigAdd).should.equal(
|
|
88
|
+
run(bigAdd).should.equal(false)
|
|
60
89
|
})
|
|
61
90
|
|
|
62
91
|
// 220 OP_NOPs then OP_1 — more non-push opcodes than the 201 cap allows.
|
|
@@ -156,14 +185,16 @@ describe('Interpreter post-Genesis limits', function () {
|
|
|
156
185
|
})
|
|
157
186
|
})
|
|
158
187
|
|
|
159
|
-
it('useGenesisLimits raises
|
|
188
|
+
it('useGenesisLimits raises the size caps, and getLimits/setLimits round-trip', function () {
|
|
160
189
|
var before = Interpreter.getLimits()
|
|
161
190
|
before.maxScriptSize.should.equal(10000)
|
|
162
191
|
Interpreter.useGenesisLimits(64 * 1024)
|
|
163
192
|
var after = Interpreter.getLimits()
|
|
164
193
|
after.maxScriptElementSize.should.equal(64 * 1024)
|
|
165
|
-
after.maximumElementSize.should.equal(64 * 1024)
|
|
166
194
|
after.maxOpsPerScript.should.equal(64 * 1024)
|
|
195
|
+
// Not raised — see the note on useGenesisLimits. setLimits() can still set it
|
|
196
|
+
// explicitly, which is what the round-trip below checks.
|
|
197
|
+
after.maximumElementSize.should.equal(4)
|
|
167
198
|
after.maxScriptSize.should.equal(64 * 1024)
|
|
168
199
|
Interpreter.setLimits(before)
|
|
169
200
|
Interpreter.getLimits().maxScriptSize.should.equal(10000)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SmartLedger-BSV v3.0.2 - Signature Verification Test
|
|
5
|
+
*
|
|
6
|
+
* This script demonstrates the WORKING signature verification
|
|
7
|
+
* in SmartLedger-BSV v3.0.2 after successful fixes.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const bsv = require('./index.js');
|
|
11
|
+
|
|
12
|
+
console.log('🔬 SmartLedger-BSV v3.0.2 - Signature Verification Test');
|
|
13
|
+
console.log('======================================================\n');
|
|
14
|
+
|
|
15
|
+
// Generate a random private key using the BSV library
|
|
16
|
+
const privateKey = new bsv.PrivateKey(); // This generates a random private key
|
|
17
|
+
const publicKey = privateKey.publicKey;
|
|
18
|
+
const message = Buffer.from('hello world', 'utf8');
|
|
19
|
+
const hash = bsv.crypto.Hash.sha256(message);
|
|
20
|
+
|
|
21
|
+
console.log('Test Data:');
|
|
22
|
+
console.log('- Private Key:', privateKey.toString());
|
|
23
|
+
console.log('- Public Key:', publicKey.toString());
|
|
24
|
+
console.log('- Message:', message.toString());
|
|
25
|
+
console.log('- Hash:', hash.toString('hex'));
|
|
26
|
+
console.log('');
|
|
27
|
+
|
|
28
|
+
// Test standard signature flow
|
|
29
|
+
console.log('Testing Standard Signature Flow:');
|
|
30
|
+
console.log('================================');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Create signature
|
|
34
|
+
const signature = bsv.crypto.ECDSA.sign(hash, privateKey);
|
|
35
|
+
console.log('✅ Signature created successfully');
|
|
36
|
+
console.log('- Is canonical:', signature.isCanonical ? signature.isCanonical() : 'method unavailable');
|
|
37
|
+
|
|
38
|
+
// Test DER format
|
|
39
|
+
const derSig = signature.toDER();
|
|
40
|
+
console.log('✅ DER conversion successful, length:', derSig.length);
|
|
41
|
+
|
|
42
|
+
// Test canonical DER
|
|
43
|
+
const canonicalSig = signature.toCanonical();
|
|
44
|
+
const canonicalDer = canonicalSig.toDER();
|
|
45
|
+
console.log('✅ Canonical DER conversion successful, length:', canonicalDer.length);
|
|
46
|
+
console.log('- Is canonical:', canonicalSig.isCanonical ? canonicalSig.isCanonical() : 'method unavailable');
|
|
47
|
+
|
|
48
|
+
// CRITICAL TEST: Verify the signature we just created
|
|
49
|
+
console.log('\nVerification Tests:');
|
|
50
|
+
console.log('==================');
|
|
51
|
+
|
|
52
|
+
// Test 1: Standard ECDSA verify
|
|
53
|
+
const verified1 = bsv.crypto.ECDSA.verify(hash, derSig, publicKey);
|
|
54
|
+
console.log(`${verified1 ? '✅' : '❌'} ECDSA.verify(hash, derSig, publicKey):`, verified1);
|
|
55
|
+
|
|
56
|
+
// Test 2: Canonical ECDSA verify
|
|
57
|
+
const verified2 = bsv.crypto.ECDSA.verify(hash, canonicalDer, publicKey);
|
|
58
|
+
console.log(`${verified2 ? '✅' : '❌'} ECDSA.verify(hash, canonicalDer, publicKey):`, verified2);
|
|
59
|
+
|
|
60
|
+
// Test 3: SmartVerify if available
|
|
61
|
+
if (bsv.SmartVerify) {
|
|
62
|
+
console.log('\nSmartVerify Tests:');
|
|
63
|
+
console.log('=================');
|
|
64
|
+
|
|
65
|
+
if (bsv.SmartVerify.smartVerify) {
|
|
66
|
+
const smartVerified1 = bsv.SmartVerify.smartVerify(hash, derSig, publicKey);
|
|
67
|
+
console.log(`${smartVerified1 ? '✅' : '❌'} SmartVerify.smartVerify(hash, derSig, publicKey):`, smartVerified1);
|
|
68
|
+
|
|
69
|
+
const smartVerified2 = bsv.SmartVerify.smartVerify(hash, canonicalDer, publicKey);
|
|
70
|
+
console.log(`${smartVerified2 ? '✅' : '❌'} SmartVerify.smartVerify(hash, canonicalDer, publicKey):`, smartVerified2);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (bsv.SmartVerify.isCanonical) {
|
|
74
|
+
const canonical1 = bsv.SmartVerify.isCanonical(derSig);
|
|
75
|
+
console.log(`${canonical1 ? '✅' : '❌'} SmartVerify.isCanonical(derSig):`, canonical1);
|
|
76
|
+
|
|
77
|
+
const canonical2 = bsv.SmartVerify.isCanonical(canonicalDer);
|
|
78
|
+
console.log(`${canonical2 ? '✅' : '❌'} SmartVerify.isCanonical(canonicalDer):`, canonical2);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Dynamic success summary based on actual results
|
|
83
|
+
const allWorking = verified1 && verified2;
|
|
84
|
+
|
|
85
|
+
if (allWorking) {
|
|
86
|
+
console.log('\n🎉 SUCCESS SUMMARY:');
|
|
87
|
+
console.log('==================');
|
|
88
|
+
console.log('1. ✅ Signatures created successfully');
|
|
89
|
+
console.log('2. ✅ Canonical validation working');
|
|
90
|
+
console.log('3. ✅ ALL verification methods return TRUE');
|
|
91
|
+
console.log('4. ✅ Transaction validation ENABLED');
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log('🚀 SmartLedger-BSV v3.0.2 signature verification is WORKING!');
|
|
94
|
+
console.log('🔧 All signature verification issues have been RESOLVED!');
|
|
95
|
+
} else {
|
|
96
|
+
console.log('\n🚨 ISSUE SUMMARY:');
|
|
97
|
+
console.log('=================');
|
|
98
|
+
console.log('1. Signatures are created successfully');
|
|
99
|
+
console.log('2. .isCanonical() reports true on signature objects');
|
|
100
|
+
console.log('3. Some verification methods still failing');
|
|
101
|
+
console.log('4. This may block transaction validation');
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log('Status: Partial fix - some issues remain');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.log('❌ Test failed with error:', error.message);
|
|
108
|
+
console.log('Stack:', error.stack);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Library version info
|
|
112
|
+
console.log('\n📚 Environment Info:');
|
|
113
|
+
console.log('====================');
|
|
114
|
+
console.log('SmartLedger version:', bsv.SmartLedger?.version || 'not available');
|
|
115
|
+
console.log('Base version:', bsv.SmartLedger?.baseVersion || 'not available');
|
|
116
|
+
console.log('Hardened by:', bsv.SmartLedger?.hardenedBy || 'not available');
|
|
117
|
+
if (bsv.SmartLedger?.securityFeatures) {
|
|
118
|
+
console.log('Security features:', bsv.SmartLedger.securityFeatures);
|
|
119
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BSV Opcode Mapping and Interpreter Configuration Tool
|
|
5
|
+
*
|
|
6
|
+
* Maps all opcodes with their enabled/disabled status and required flags
|
|
7
|
+
* Provides helper functions for script execution with proper flags
|
|
8
|
+
*
|
|
9
|
+
* Usage: node opcode_map.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const bsv = require('./index.js');
|
|
13
|
+
const { Opcode, Script } = bsv;
|
|
14
|
+
const Interpreter = bsv.Script.Interpreter;
|
|
15
|
+
|
|
16
|
+
// Create reverse opcode mapping
|
|
17
|
+
const opcodeMap = {};
|
|
18
|
+
for (let key in Opcode.map) {
|
|
19
|
+
const code = Opcode.map[key];
|
|
20
|
+
opcodeMap[code] = key;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Define opcode categories and their enablement requirements
|
|
24
|
+
const OPCODE_CATEGORIES = {
|
|
25
|
+
// Always enabled opcodes
|
|
26
|
+
CORE: {
|
|
27
|
+
description: 'Core opcodes - always enabled',
|
|
28
|
+
flag: null,
|
|
29
|
+
opcodes: [
|
|
30
|
+
'OP_0', 'OP_1NEGATE', 'OP_1', 'OP_2', 'OP_3', 'OP_4', 'OP_5', 'OP_6', 'OP_7', 'OP_8', 'OP_9', 'OP_10',
|
|
31
|
+
'OP_11', 'OP_12', 'OP_13', 'OP_14', 'OP_15', 'OP_16', 'OP_NOP', 'OP_IF', 'OP_NOTIF', 'OP_ELSE', 'OP_ENDIF',
|
|
32
|
+
'OP_VERIFY', 'OP_RETURN', 'OP_TOALTSTACK', 'OP_FROMALTSTACK', 'OP_2DROP', 'OP_2DUP', 'OP_3DUP', 'OP_2OVER',
|
|
33
|
+
'OP_2ROT', 'OP_2SWAP', 'OP_IFDUP', 'OP_DEPTH', 'OP_DROP', 'OP_DUP', 'OP_NIP', 'OP_OVER', 'OP_PICK', 'OP_ROLL',
|
|
34
|
+
'OP_ROT', 'OP_SWAP', 'OP_TUCK', 'OP_SIZE', 'OP_EQUAL', 'OP_EQUALVERIFY', 'OP_1ADD', 'OP_1SUB', 'OP_NEGATE',
|
|
35
|
+
'OP_ABS', 'OP_NOT', 'OP_0NOTEQUAL', 'OP_ADD', 'OP_SUB', 'OP_BOOLAND', 'OP_BOOLOR', 'OP_NUMEQUAL',
|
|
36
|
+
'OP_NUMEQUALVERIFY', 'OP_NUMNOTEQUAL', 'OP_LESSTHAN', 'OP_GREATERTHAN', 'OP_LESSTHANOREQUAL',
|
|
37
|
+
'OP_GREATERTHANOREQUAL', 'OP_MIN', 'OP_MAX', 'OP_WITHIN', 'OP_RIPEMD160', 'OP_SHA1', 'OP_SHA256',
|
|
38
|
+
'OP_HASH160', 'OP_HASH256', 'OP_CODESEPARATOR', 'OP_CHECKSIG', 'OP_CHECKSIGVERIFY', 'OP_CHECKMULTISIG',
|
|
39
|
+
'OP_CHECKMULTISIGVERIFY', 'OP_NOP1', 'OP_PUSHDATA1', 'OP_PUSHDATA2', 'OP_PUSHDATA4'
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// Monolith opcodes (May 2018 upgrade)
|
|
44
|
+
MONOLITH: {
|
|
45
|
+
description: 'Monolith opcodes - require SCRIPT_ENABLE_MONOLITH_OPCODES',
|
|
46
|
+
flag: 'SCRIPT_ENABLE_MONOLITH_OPCODES',
|
|
47
|
+
flagValue: (1 << 18),
|
|
48
|
+
opcodes: [
|
|
49
|
+
'OP_CAT', 'OP_SPLIT', 'OP_NUM2BIN', 'OP_BIN2NUM', 'OP_AND', 'OP_OR', 'OP_XOR', 'OP_DIV', 'OP_MOD'
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Magnetic opcodes (future upgrade)
|
|
54
|
+
MAGNETIC: {
|
|
55
|
+
description: 'Magnetic opcodes - require SCRIPT_ENABLE_MAGNETIC_OPCODES',
|
|
56
|
+
flag: 'SCRIPT_ENABLE_MAGNETIC_OPCODES',
|
|
57
|
+
flagValue: (1 << 19),
|
|
58
|
+
opcodes: [
|
|
59
|
+
'OP_INVERT', 'OP_MUL', 'OP_LSHIFT', 'OP_RSHIFT'
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Always disabled opcodes
|
|
64
|
+
DISABLED: {
|
|
65
|
+
description: 'Permanently disabled opcodes',
|
|
66
|
+
flag: null,
|
|
67
|
+
opcodes: [
|
|
68
|
+
'OP_RESERVED', 'OP_VER', 'OP_VERIF', 'OP_VERNOTIF', 'OP_RESERVED1', 'OP_RESERVED2', 'OP_2MUL', 'OP_2DIV'
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// NOP opcodes
|
|
73
|
+
NOPS: {
|
|
74
|
+
description: 'NOP opcodes - may be discouraged with SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS',
|
|
75
|
+
flag: 'SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS',
|
|
76
|
+
flagValue: (1 << 7),
|
|
77
|
+
opcodes: [
|
|
78
|
+
'OP_NOP2', 'OP_NOP3', 'OP_NOP4', 'OP_NOP5', 'OP_NOP6', 'OP_NOP7', 'OP_NOP8', 'OP_NOP9', 'OP_NOP10'
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get opcode information including enablement status
|
|
85
|
+
*/
|
|
86
|
+
function getOpcodeInfo(opcodeValue) {
|
|
87
|
+
const opcodeName = opcodeMap[opcodeValue];
|
|
88
|
+
if (!opcodeName) {
|
|
89
|
+
return {
|
|
90
|
+
value: opcodeValue,
|
|
91
|
+
name: `UNKNOWN_${opcodeValue}`,
|
|
92
|
+
enabled: false,
|
|
93
|
+
category: 'UNKNOWN',
|
|
94
|
+
requiresFlag: null
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Find which category this opcode belongs to
|
|
99
|
+
for (const [category, info] of Object.entries(OPCODE_CATEGORIES)) {
|
|
100
|
+
if (info.opcodes.includes(opcodeName)) {
|
|
101
|
+
return {
|
|
102
|
+
value: opcodeValue,
|
|
103
|
+
hex: `0x${opcodeValue.toString(16).padStart(2, '0')}`,
|
|
104
|
+
name: opcodeName,
|
|
105
|
+
enabled: category !== 'DISABLED',
|
|
106
|
+
category: category,
|
|
107
|
+
requiresFlag: info.flag,
|
|
108
|
+
flagValue: info.flagValue,
|
|
109
|
+
description: info.description
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Default to core if not found in any category
|
|
115
|
+
return {
|
|
116
|
+
value: opcodeValue,
|
|
117
|
+
hex: `0x${opcodeValue.toString(16).padStart(2, '0')}`,
|
|
118
|
+
name: opcodeName,
|
|
119
|
+
enabled: true,
|
|
120
|
+
category: 'CORE',
|
|
121
|
+
requiresFlag: null,
|
|
122
|
+
description: 'Core opcode - always enabled'
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get recommended interpreter flags for different use cases
|
|
128
|
+
*/
|
|
129
|
+
function getInterpreterFlags() {
|
|
130
|
+
return {
|
|
131
|
+
// Minimal flags for basic scripts
|
|
132
|
+
BASIC: Interpreter.SCRIPT_VERIFY_P2SH,
|
|
133
|
+
|
|
134
|
+
// Standard flags for most scripts
|
|
135
|
+
STANDARD: Interpreter.SCRIPT_VERIFY_P2SH |
|
|
136
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
137
|
+
Interpreter.SCRIPT_VERIFY_MINIMALDATA,
|
|
138
|
+
|
|
139
|
+
// Modern BSV with monolith opcodes (recommended for new scripts)
|
|
140
|
+
MODERN: Interpreter.SCRIPT_VERIFY_P2SH |
|
|
141
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
142
|
+
Interpreter.SCRIPT_VERIFY_MINIMALDATA |
|
|
143
|
+
Interpreter.SCRIPT_VERIFY_SIGPUSHONLY |
|
|
144
|
+
Interpreter.SCRIPT_VERIFY_CLEANSTACK |
|
|
145
|
+
Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
146
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES,
|
|
147
|
+
|
|
148
|
+
// Full BSV with all opcodes enabled
|
|
149
|
+
FULL: Interpreter.SCRIPT_VERIFY_P2SH |
|
|
150
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
151
|
+
Interpreter.SCRIPT_VERIFY_MINIMALDATA |
|
|
152
|
+
Interpreter.SCRIPT_VERIFY_SIGPUSHONLY |
|
|
153
|
+
Interpreter.SCRIPT_VERIFY_CLEANSTACK |
|
|
154
|
+
Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
155
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES |
|
|
156
|
+
Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES,
|
|
157
|
+
|
|
158
|
+
// Genesis rules (most permissive)
|
|
159
|
+
GENESIS: Interpreter.SCRIPT_VERIFY_P2SH |
|
|
160
|
+
Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
161
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES |
|
|
162
|
+
Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Analyze a script and determine required flags
|
|
168
|
+
*/
|
|
169
|
+
function analyzeScriptRequirements(script) {
|
|
170
|
+
let requiredFlags = Interpreter.SCRIPT_VERIFY_P2SH; // Base requirement
|
|
171
|
+
const usedOpcodes = [];
|
|
172
|
+
const issues = [];
|
|
173
|
+
|
|
174
|
+
// Parse script
|
|
175
|
+
let chunks;
|
|
176
|
+
if (typeof script === 'string') {
|
|
177
|
+
// ASM format
|
|
178
|
+
chunks = script.split(' ').map(token => {
|
|
179
|
+
if (token.startsWith('OP_')) {
|
|
180
|
+
const opcodeValue = Object.keys(opcodeMap).find(key => opcodeMap[key] === token);
|
|
181
|
+
return { opcodenum: parseInt(opcodeValue) };
|
|
182
|
+
}
|
|
183
|
+
return { buf: Buffer.from(token, 'hex') };
|
|
184
|
+
});
|
|
185
|
+
} else if (script instanceof Script) {
|
|
186
|
+
chunks = script.chunks;
|
|
187
|
+
} else {
|
|
188
|
+
throw new Error('Invalid script format');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Analyze each chunk
|
|
192
|
+
chunks.forEach((chunk, index) => {
|
|
193
|
+
if (chunk.opcodenum !== undefined) {
|
|
194
|
+
const info = getOpcodeInfo(chunk.opcodenum);
|
|
195
|
+
usedOpcodes.push(info);
|
|
196
|
+
|
|
197
|
+
if (!info.enabled) {
|
|
198
|
+
issues.push(`Opcode ${info.name} at position ${index} is permanently disabled`);
|
|
199
|
+
} else if (info.requiresFlag) {
|
|
200
|
+
if (info.flag === 'SCRIPT_ENABLE_MONOLITH_OPCODES') {
|
|
201
|
+
requiredFlags |= Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES;
|
|
202
|
+
} else if (info.flag === 'SCRIPT_ENABLE_MAGNETIC_OPCODES') {
|
|
203
|
+
requiredFlags |= Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Add FORKID if using any signature opcodes
|
|
210
|
+
const hasSignatureOpcodes = usedOpcodes.some(op =>
|
|
211
|
+
op.name === 'OP_CHECKSIG' || op.name === 'OP_CHECKSIGVERIFY' ||
|
|
212
|
+
op.name === 'OP_CHECKMULTISIG' || op.name === 'OP_CHECKMULTISIGVERIFY'
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (hasSignatureOpcodes) {
|
|
216
|
+
requiredFlags |= Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
requiredFlags,
|
|
221
|
+
usedOpcodes,
|
|
222
|
+
issues,
|
|
223
|
+
recommendations: {
|
|
224
|
+
basic: requiredFlags,
|
|
225
|
+
safe: requiredFlags | Interpreter.SCRIPT_VERIFY_STRICTENC | Interpreter.SCRIPT_VERIFY_MINIMALDATA,
|
|
226
|
+
strict: requiredFlags | Interpreter.SCRIPT_VERIFY_STRICTENC | Interpreter.SCRIPT_VERIFY_MINIMALDATA |
|
|
227
|
+
Interpreter.SCRIPT_VERIFY_SIGPUSHONLY | Interpreter.SCRIPT_VERIFY_CLEANSTACK
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Print opcode analysis
|
|
234
|
+
*/
|
|
235
|
+
function printOpcodeAnalysis() {
|
|
236
|
+
console.log('🔧 BSV Opcode Mapping and Interpreter Configuration');
|
|
237
|
+
console.log('═'.repeat(80));
|
|
238
|
+
|
|
239
|
+
// Show interpreter flags
|
|
240
|
+
console.log('\n⚙️ Available Interpreter Flags:');
|
|
241
|
+
const flagNames = Object.keys(Interpreter).filter(key => key.startsWith('SCRIPT_'));
|
|
242
|
+
flagNames.forEach(flagName => {
|
|
243
|
+
const value = Interpreter[flagName];
|
|
244
|
+
console.log(` ${flagName.padEnd(40)}: 0x${value.toString(16).padStart(8, '0').toUpperCase()}`);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Show recommended flag combinations
|
|
248
|
+
console.log('\n🎯 Recommended Flag Combinations:');
|
|
249
|
+
const flags = getInterpreterFlags();
|
|
250
|
+
Object.entries(flags).forEach(([name, value]) => {
|
|
251
|
+
console.log(` ${name.padEnd(10)}: 0x${value.toString(16).padStart(8, '0').toUpperCase()}`);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Show opcode categories
|
|
255
|
+
console.log('\n📋 Opcode Categories:');
|
|
256
|
+
Object.entries(OPCODE_CATEGORIES).forEach(([category, info]) => {
|
|
257
|
+
console.log(`\n ${category} - ${info.description}`);
|
|
258
|
+
if (info.flag) {
|
|
259
|
+
console.log(` Required flag: ${info.flag} (0x${info.flagValue.toString(16).toUpperCase()})`);
|
|
260
|
+
}
|
|
261
|
+
console.log(` Opcodes: ${info.opcodes.length}`);
|
|
262
|
+
|
|
263
|
+
// Show first few opcodes
|
|
264
|
+
const sample = info.opcodes.slice(0, 5);
|
|
265
|
+
console.log(` Sample: ${sample.join(', ')}${info.opcodes.length > 5 ? '...' : ''}`);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Show total counts
|
|
269
|
+
const totalOpcodes = Object.keys(opcodeMap).length;
|
|
270
|
+
const enabledOpcodes = Object.keys(opcodeMap).filter(code => {
|
|
271
|
+
const info = getOpcodeInfo(parseInt(code));
|
|
272
|
+
return info.enabled;
|
|
273
|
+
}).length;
|
|
274
|
+
|
|
275
|
+
console.log(`\n📊 Summary:`);
|
|
276
|
+
console.log(` Total opcodes defined: ${totalOpcodes}`);
|
|
277
|
+
console.log(` Enabled opcodes: ${enabledOpcodes}`);
|
|
278
|
+
console.log(` Disabled opcodes: ${totalOpcodes - enabledOpcodes}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Create a properly configured interpreter
|
|
283
|
+
*/
|
|
284
|
+
function createInterpreter(flagLevel = 'MODERN') {
|
|
285
|
+
const flags = getInterpreterFlags();
|
|
286
|
+
return {
|
|
287
|
+
interpreter: new Interpreter(),
|
|
288
|
+
flags: flags[flagLevel.toUpperCase()] || flags.MODERN,
|
|
289
|
+
|
|
290
|
+
verify: function(scriptSig, scriptPubkey, tx, nin, satoshisBN) {
|
|
291
|
+
return this.interpreter.verify(
|
|
292
|
+
scriptSig,
|
|
293
|
+
scriptPubkey,
|
|
294
|
+
tx,
|
|
295
|
+
nin,
|
|
296
|
+
this.flags,
|
|
297
|
+
satoshisBN
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// CLI execution
|
|
304
|
+
if (require.main === module) {
|
|
305
|
+
printOpcodeAnalysis();
|
|
306
|
+
|
|
307
|
+
// Test with sample nLockTime script
|
|
308
|
+
console.log('\n🧪 Testing nLockTime Script Requirements:');
|
|
309
|
+
const sampleScript = 'OP_OVER OP_HASH256 a7a9bc2c7389fecf7d5279e0909bcaac1b0f7d0ee81713adfb7e97fad95322ad OP_EQUALVERIFY OP_OVER 6a OP_SPLIT OP_NIP 04 OP_SPLIT OP_NIP OP_BIN2NUM e803 OP_NUMEQUALVERIFY 03433a1466b09a27e0067efa9c58ec12ae5e26071a413cfe918b49656c75d7fb2d OP_CHECKSIGVERIFY';
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const analysis = analyzeScriptRequirements(sampleScript);
|
|
313
|
+
console.log(` Required flags: 0x${analysis.requiredFlags.toString(16).toUpperCase()}`);
|
|
314
|
+
console.log(` Used opcodes: ${analysis.usedOpcodes.length}`);
|
|
315
|
+
|
|
316
|
+
if (analysis.issues.length > 0) {
|
|
317
|
+
console.log(` Issues:`);
|
|
318
|
+
analysis.issues.forEach(issue => console.log(` ⚠️ ${issue}`));
|
|
319
|
+
} else {
|
|
320
|
+
console.log(` ✅ No issues found`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
console.log(` Recommendations:`);
|
|
324
|
+
console.log(` Basic: 0x${analysis.recommendations.basic.toString(16).toUpperCase()}`);
|
|
325
|
+
console.log(` Safe: 0x${analysis.recommendations.safe.toString(16).toUpperCase()}`);
|
|
326
|
+
console.log(` Strict: 0x${analysis.recommendations.strict.toString(16).toUpperCase()}`);
|
|
327
|
+
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.log(` ❌ Error analyzing script: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Export functions and data
|
|
334
|
+
module.exports = {
|
|
335
|
+
opcodeMap,
|
|
336
|
+
OPCODE_CATEGORIES,
|
|
337
|
+
getOpcodeInfo,
|
|
338
|
+
getInterpreterFlags,
|
|
339
|
+
analyzeScriptRequirements,
|
|
340
|
+
createInterpreter,
|
|
341
|
+
printOpcodeAnalysis
|
|
342
|
+
};
|
package/tools/server.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const http = require('http')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const port = 8080
|
|
6
|
+
|
|
7
|
+
// MIME types for different file extensions
|
|
8
|
+
const mimeTypes = {
|
|
9
|
+
'.html': 'text/html',
|
|
10
|
+
'.js': 'application/javascript',
|
|
11
|
+
'.css': 'text/css',
|
|
12
|
+
'.json': 'application/json',
|
|
13
|
+
'.png': 'image/png',
|
|
14
|
+
'.jpg': 'image/jpg',
|
|
15
|
+
'.gif': 'image/gif',
|
|
16
|
+
'.ico': 'image/x-icon',
|
|
17
|
+
'.svg': 'image/svg+xml',
|
|
18
|
+
'.woff': 'font/woff',
|
|
19
|
+
'.woff2': 'font/woff2'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const server = http.createServer((req, res) => {
|
|
23
|
+
console.log(`Request for ${req.url}`)
|
|
24
|
+
|
|
25
|
+
// Parse URL and remove query string
|
|
26
|
+
let filePath = req.url.split('?')[0]
|
|
27
|
+
|
|
28
|
+
// Default to index.html if no specific file requested
|
|
29
|
+
if (filePath === '/') {
|
|
30
|
+
filePath = '/test.html'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Build full file path
|
|
34
|
+
const fullPath = path.join(__dirname, filePath)
|
|
35
|
+
|
|
36
|
+
// Get file extension
|
|
37
|
+
const extname = path.extname(fullPath)
|
|
38
|
+
const contentType = mimeTypes[extname] || 'application/octet-stream'
|
|
39
|
+
|
|
40
|
+
// Check if file exists
|
|
41
|
+
fs.readFile(fullPath, (err, content) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
if (err.code === 'ENOENT') {
|
|
44
|
+
// File not found
|
|
45
|
+
res.writeHead(404, { 'Content-Type': 'text/html' })
|
|
46
|
+
res.end(`
|
|
47
|
+
<html>
|
|
48
|
+
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
|
|
49
|
+
<h1>404 - File Not Found</h1>
|
|
50
|
+
<p>The requested file <strong>${req.url}</strong> was not found.</p>
|
|
51
|
+
<p><a href="/test.html">Go to BSV Security Test</a></p>
|
|
52
|
+
</body>
|
|
53
|
+
</html>
|
|
54
|
+
`)
|
|
55
|
+
} else {
|
|
56
|
+
// Server error
|
|
57
|
+
res.writeHead(500)
|
|
58
|
+
res.end(`Server Error: ${err.code}`)
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
// Success - serve the file
|
|
62
|
+
res.writeHead(200, {
|
|
63
|
+
'Content-Type': contentType,
|
|
64
|
+
'Cache-Control': 'no-cache' // Prevent caching for development
|
|
65
|
+
})
|
|
66
|
+
res.end(content)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
server.listen(port, () => {
|
|
72
|
+
console.log(`
|
|
73
|
+
🚀 SmartLedger BSV Test Server Started!
|
|
74
|
+
|
|
75
|
+
📍 Server running at: http://localhost:${port}
|
|
76
|
+
🧪 Test page: http://localhost:${port}/test.html
|
|
77
|
+
📁 Serving files from: ${__dirname}
|
|
78
|
+
|
|
79
|
+
📊 Available files:
|
|
80
|
+
- test.html (Main test page)
|
|
81
|
+
- bsv.min.js (${Math.round(fs.statSync('bsv.min.js').size / 1024)} KB)
|
|
82
|
+
- bsv-message.min.js (${Math.round(fs.statSync('bsv-message.min.js').size / 1024)} KB)
|
|
83
|
+
- bsv-ecies.min.js (${Math.round(fs.statSync('bsv-ecies.min.js').size / 1024)} KB)
|
|
84
|
+
- bsv-mnemonic.min.js (${Math.round(fs.statSync('bsv-mnemonic.min.js').size / 1024)} KB)
|
|
85
|
+
|
|
86
|
+
Press Ctrl+C to stop the server
|
|
87
|
+
`)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Handle server shutdown gracefully
|
|
91
|
+
process.on('SIGINT', () => {
|
|
92
|
+
console.log('\n👋 Shutting down server...')
|
|
93
|
+
server.close(() => {
|
|
94
|
+
console.log('✅ Server stopped')
|
|
95
|
+
process.exit(0)
|
|
96
|
+
})
|
|
97
|
+
})
|