@smartledger/bsv 8.1.0 → 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.
@@ -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
+ };
@@ -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
+ })
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Simple Real BSV Transaction Test
5
+ *
6
+ * Uses only real UTXOs to create and broadcast a transaction
7
+ */
8
+
9
+ const bsv = require('./index.js');
10
+ const https = require('https');
11
+
12
+ console.log('šŸš€ Simple Real BSV Transaction Test');
13
+ console.log('===================================\n');
14
+
15
+ const privateKey = new bsv.PrivateKey('L1aW4aubDFB7yfras2S1mN3bqg9nwySY8nkoLmJebSLD5BWv3ENZ');
16
+ const address = privateKey.toAddress().toString();
17
+
18
+ console.log(`Address: ${address}`);
19
+ console.log(`Private Key: ${privateKey.toString()}\n`);
20
+
21
+ // Simple API call
22
+ function makeRequest(url) {
23
+ return new Promise((resolve, reject) => {
24
+ https.get(url, (res) => {
25
+ let data = '';
26
+ res.on('data', chunk => data += chunk);
27
+ res.on('end', () => {
28
+ try {
29
+ resolve(JSON.parse(data));
30
+ } catch (error) {
31
+ reject(new Error(`Failed to parse response: ${error.message}`));
32
+ }
33
+ });
34
+ }).on('error', reject);
35
+ });
36
+ }
37
+
38
+ async function createSimpleTransaction() {
39
+ try {
40
+ // Get real UTXO
41
+ console.log('šŸ” Fetching real UTXO...');
42
+ const utxos = await makeRequest(`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`);
43
+
44
+ if (utxos.length === 0) {
45
+ throw new Error('No UTXOs found');
46
+ }
47
+
48
+ const utxo = utxos[0]; // Use the first UTXO
49
+ console.log(`Found UTXO: ${utxo.tx_hash}:${utxo.tx_pos} = ${utxo.value} sats`);
50
+
51
+ // Create P2PKH script for the UTXO
52
+ const addr = bsv.Address.fromString(address);
53
+ const p2pkhScript = bsv.Script.buildPublicKeyHashOut(address);
54
+
55
+ // Format UTXO for BSV transaction
56
+ const formattedUTXO = {
57
+ txid: utxo.tx_hash,
58
+ vout: utxo.tx_pos,
59
+ address: address,
60
+ satoshis: utxo.value,
61
+ script: p2pkhScript.toHex()
62
+ };
63
+
64
+ console.log(`UTXO Script: ${p2pkhScript.toHex()}`);
65
+
66
+ // Create transaction - send smaller amount since we now have the result of previous tx
67
+ const sendAmount = Math.min(500, Math.floor(utxo.value * 0.8)); // Send 80% or 500 sats, whichever is smaller
68
+ const feePerKb = 10; // 10 sats per KB
69
+
70
+ // Estimate transaction size (typical P2PKH transaction is ~225 bytes)
71
+ const estimatedSize = 225;
72
+ const fee = Math.ceil((estimatedSize * feePerKb) / 1000);
73
+ const changeAmount = utxo.value - sendAmount - fee;
74
+
75
+ if (changeAmount < 0) {
76
+ throw new Error(`Insufficient funds. Need ${sendAmount + fee}, have ${utxo.value}`);
77
+ }
78
+
79
+ console.log(`\nšŸ’ø Creating transaction:`);
80
+ console.log(`Send: ${sendAmount} sats`);
81
+ console.log(`Fee rate: ${feePerKb} sats/KB`);
82
+ console.log(`Estimated fee: ${fee} sats`);
83
+ console.log(`Change: ${changeAmount} sats`);
84
+
85
+ const transaction = new bsv.Transaction()
86
+ .from(formattedUTXO)
87
+ .to(address, sendAmount) // Send to ourselves
88
+ .change(address) // Change back to ourselves
89
+ .feePerKb(feePerKb) // Set fee rate
90
+ .sign(privateKey);
91
+
92
+ console.log(`\nāœ… Transaction created successfully!`);
93
+ console.log(`Transaction ID: ${transaction.id}`);
94
+ console.log(`Transaction size: ${transaction.toBuffer().length} bytes`);
95
+
96
+ // Validation
97
+ console.log(`\nšŸ” Validation:`);
98
+ const isValid = transaction.verify();
99
+ console.log(`Basic BSV validation: ${isValid ? 'āœ… VALID' : 'āŒ INVALID'}`);
100
+
101
+ if (isValid) {
102
+ console.log(`Raw transaction: ${transaction.toString()}`);
103
+
104
+ // Test broadcast
105
+ if (process.argv.includes('--broadcast')) {
106
+ console.log('\nšŸ“” Broadcasting transaction...');
107
+
108
+ try {
109
+ const response = await fetch('https://api.whatsonchain.com/v1/bsv/main/tx/raw', {
110
+ method: 'POST',
111
+ headers: { 'Content-Type': 'application/json' },
112
+ body: JSON.stringify({ txhex: transaction.toString() })
113
+ });
114
+
115
+ if (response.ok) {
116
+ const result = await response.text();
117
+ console.log(`āœ… Transaction broadcast successful!`);
118
+ console.log(`Transaction ID: ${result}`);
119
+ } else {
120
+ const error = await response.text();
121
+ console.log(`āŒ Broadcast failed: ${response.status} "${error}"`);
122
+ }
123
+ } catch (error) {
124
+ console.log(`āŒ Broadcast failed: ${error.message}`);
125
+ }
126
+ } else {
127
+ console.log('\nšŸ’” To broadcast: node simple_real_tx.js --broadcast');
128
+ }
129
+ }
130
+
131
+ } catch (error) {
132
+ console.log(`āŒ Error: ${error.message}`);
133
+ }
134
+ }
135
+
136
+ createSimpleTransaction();