smartledger-bsv 3.0.1 → 3.0.2

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,149 @@
1
+ /**
2
+ * mock_utxo_generator.js
3
+ *
4
+ * Usage:
5
+ * # generate a new random private key and a mock utxo (default satoshis = 100000)
6
+ * node mock_utxo_generator.js
7
+ *
8
+ * # provide WIF and create a utxo
9
+ * node mock_utxo_generator.js <WIF>
10
+ *
11
+ * # provide WIF, set satoshis, and optionally create a signed tx to a recipient
12
+ * node mock_utxo_generator.js <WIF> <satoshis> <recipientAddress>
13
+ *
14
+ * Example:
15
+ * node mock_utxo_generator.js L1aW4aubDFB7yfras2S1mMEW7bZ1aW4aubD 50000 muZ... (example address)
16
+ *
17
+ * Notes:
18
+ * - Requires `bsv` package: npm install bsv
19
+ * - This is for LOCAL TESTING only. The produced txid, utxo, and tx hex are fake/mock and
20
+ * intended to be consumed by local test harnesses or unit tests (or regtest setups if you
21
+ * create a matching real TX there).
22
+ */
23
+
24
+ const bsv = require('../index.js');
25
+ const crypto = require('crypto');
26
+
27
+ function usage() {
28
+ console.log('Usage: node mock_utxo_generator.js [WIF] [satoshis] [recipientAddress]');
29
+ console.log('If WIF omitted, a random private key is generated.');
30
+ }
31
+
32
+ function randomHex(len) {
33
+ return crypto.randomBytes(len).toString('hex');
34
+ }
35
+
36
+ function createMockTxId() {
37
+ // create random 32-byte hex as fake txid (little-endian vs big-endian not important for mocks)
38
+ return randomHex(32);
39
+ }
40
+
41
+ function buildP2pkhScriptHex(address) {
42
+ return bsv.Script.buildPublicKeyHashOut(address).toHex();
43
+ }
44
+
45
+ function mkUtxo(privateKey, sats = 100000) {
46
+ const address = privateKey.toAddress().toString();
47
+ const txid = createMockTxId();
48
+ const vout = 0;
49
+ const scriptHex = buildP2pkhScriptHex(address);
50
+
51
+ // Return a utxo shape compatible with bsv.Transaction().from(...)
52
+ return {
53
+ txId: txid,
54
+ txid: txid, // alternate naming used by some code
55
+ vout: vout,
56
+ outputIndex: vout, // bsv accepts outputIndex or vout
57
+ satoshis: sats,
58
+ value: sats,
59
+ script: scriptHex,
60
+ scriptPubKey: scriptHex,
61
+ address,
62
+ };
63
+ }
64
+
65
+ async function main() {
66
+ const argv = process.argv.slice(2);
67
+ if (argv.includes('-h') || argv.includes('--help')) {
68
+ usage();
69
+ return;
70
+ }
71
+
72
+ const maybeWif = argv[0];
73
+ const maybeSatoshis = argv[1];
74
+ const recipient = argv[2];
75
+
76
+ let privateKey;
77
+ try {
78
+ if (maybeWif) {
79
+ // try to load from WIF
80
+ privateKey = bsv.PrivateKey.fromWIF(maybeWif);
81
+ } else {
82
+ privateKey = new bsv.PrivateKey(); // random
83
+ }
84
+ } catch (e) {
85
+ console.error('Invalid WIF provided. Exiting.');
86
+ process.exit(1);
87
+ }
88
+
89
+ const satoshis = maybeSatoshis ? parseInt(maybeSatoshis, 10) : 100000;
90
+ if (Number.isNaN(satoshis) || satoshis <= 0) {
91
+ console.error('Invalid satoshis amount. Must be positive integer.');
92
+ process.exit(1);
93
+ }
94
+
95
+ const utxo = mkUtxo(privateKey, satoshis);
96
+
97
+ console.log('\n=== MOCK UTXO ===');
98
+ console.log(JSON.stringify({
99
+ privateKeyWIF: privateKey.toWIF(),
100
+ privateKeyHex: privateKey.toString('hex'),
101
+ address: utxo.address,
102
+ utxo: {
103
+ txid: utxo.txid,
104
+ vout: utxo.vout,
105
+ scriptPubKey: utxo.scriptPubKey,
106
+ satoshis: utxo.satoshis
107
+ }
108
+ }, null, 2));
109
+ console.log('=================\n');
110
+
111
+ if (recipient) {
112
+ // Build a small tx that spends the mock utxo and sends everything minus a tiny fee
113
+ try {
114
+ const fee = 500; // tiny fee for local testing
115
+ const sendAmount = Math.max(0, utxo.satoshis - fee);
116
+ if (sendAmount <= 0) {
117
+ console.error('UTXO too small to cover fee. Increase satoshis or reduce fee.');
118
+ process.exit(1);
119
+ }
120
+
121
+ // Build transaction using bsv.Transaction.from style
122
+ const tx = new bsv.Transaction()
123
+ .from({
124
+ txId: utxo.txid,
125
+ outputIndex: utxo.outputIndex,
126
+ script: utxo.script,
127
+ satoshis: utxo.satoshis
128
+ })
129
+ .to(recipient, sendAmount)
130
+ // .change(utxo.address) // not necessary here because we send everything minus fee
131
+ .sign(privateKey);
132
+
133
+ console.log('=== SIGNED SPEND TX ===');
134
+ console.log('TX HEX:', tx.toString());
135
+ console.log('TX ID (hash):', tx.hash);
136
+ console.log('fee (mock):', fee);
137
+ console.log('sendAmount:', sendAmount);
138
+ console.log('========================\n');
139
+
140
+ console.log('Note: This TX is signed against our mock UTXO and can be used in local tests that accept fake txids.');
141
+ } catch (e) {
142
+ console.error('Failed to build or sign tx:', e);
143
+ }
144
+ } else {
145
+ console.log('No recipient provided. To produce a signed spending tx, run again with a recipient address as the 3rd arg.');
146
+ }
147
+ }
148
+
149
+ main();
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 🔧 BSV Raw Transaction Hex Validation Example
5
+ *
6
+ * Demonstrates sending raw transaction hex to the miner for validation
7
+ * using the BSV script interpreter for proper signature verification.
8
+ */
9
+
10
+ const bsv = require('../index.js');
11
+ const {
12
+ acceptTransaction,
13
+ acceptRawTransaction
14
+ } = require('./miner-simulator');
15
+ const {
16
+ loadConfig
17
+ } = require('./utxo-manager');
18
+
19
+ /**
20
+ * Example: Create and validate raw transaction hex
21
+ */
22
+ function exampleRawTransactionValidation() {
23
+ console.log('🔧 BSV Raw Transaction Hex Validation Example');
24
+ console.log('═'.repeat(80));
25
+
26
+ try {
27
+ // Load wallet config
28
+ const config = loadConfig();
29
+ const wallet = config.wallet;
30
+
31
+ // Create recipient
32
+ const recipientKey = new bsv.PrivateKey();
33
+ const recipientAddress = recipientKey.toAddress().toString();
34
+
35
+ // Get available UTXO
36
+ const utxo = config.utxo;
37
+
38
+ console.log('📋 Transaction Details:');
39
+ console.log(`👛 From: ${wallet.address}`);
40
+ console.log(`🎯 To: ${recipientAddress}`);
41
+ console.log(`💰 Amount: 10,000 satoshis`);
42
+ console.log(`💳 Using UTXO: ${utxo.txid}:${utxo.vout} (${utxo.satoshis} sats)\n`);
43
+
44
+ // Create transaction with proper SIGHASH_FORKID
45
+ const tx = new bsv.Transaction()
46
+ .from(utxo)
47
+ .to(recipientAddress, 10000)
48
+ .change(wallet.address)
49
+ .fee(1000)
50
+ .sign(bsv.PrivateKey.fromWIF(wallet.privateKeyWIF), bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
51
+
52
+ console.log('✅ Transaction created successfully');
53
+ console.log(`🆔 Transaction ID: ${tx.id}`);
54
+
55
+ // Get raw transaction hex
56
+ const rawHex = tx.toString();
57
+ console.log(`\n📦 Raw Transaction Hex (${rawHex.length} chars):`);
58
+ console.log(`${rawHex.substring(0, 80)}...`);
59
+ console.log(`...${rawHex.substring(rawHex.length - 80)}`);
60
+
61
+ // Method 1: Send transaction object
62
+ console.log('\n' + '─'.repeat(60));
63
+ console.log('🔄 Method 1: Sending Transaction Object');
64
+ console.log('─'.repeat(60));
65
+
66
+ const result1 = acceptTransaction(tx);
67
+ console.log(`\nResult: ${result1.accepted ? '✅ ACCEPTED' : '❌ REJECTED'}`);
68
+ if (result1.errors.length > 0) {
69
+ console.log('Errors:', result1.errors);
70
+ }
71
+
72
+ // Method 2: Send raw transaction hex
73
+ console.log('\n' + '─'.repeat(60));
74
+ console.log('🔄 Method 2: Sending Raw Transaction Hex');
75
+ console.log('─'.repeat(60));
76
+
77
+ const result2 = acceptRawTransaction(rawHex);
78
+ console.log(`\nResult: ${result2.accepted ? '✅ ACCEPTED' : '❌ REJECTED'}`);
79
+ if (result2.errors.length > 0) {
80
+ console.log('Errors:', result2.errors);
81
+ }
82
+
83
+ console.log('\n🎯 Both methods should produce identical validation results!');
84
+
85
+ } catch (error) {
86
+ console.error('❌ Error in example:', error.message);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Example: Test invalid raw transaction hex
92
+ */
93
+ function exampleInvalidRawHex() {
94
+ console.log('\n' + '═'.repeat(80));
95
+ console.log('🚫 Testing Invalid Raw Transaction Hex');
96
+ console.log('═'.repeat(80));
97
+
98
+ // Test various invalid hex scenarios
99
+ const invalidHexExamples = [
100
+ {
101
+ name: 'Too short hex',
102
+ hex: '01000000',
103
+ description: 'Transaction hex too short'
104
+ },
105
+ {
106
+ name: 'Invalid characters',
107
+ hex: '0100000001INVALID_HEX_CHARACTERS',
108
+ description: 'Contains non-hex characters'
109
+ },
110
+ {
111
+ name: 'Malformed structure',
112
+ hex: '01000000010000000000000000000000000000000000000000000000000000000000000000',
113
+ description: 'Valid hex but malformed transaction structure'
114
+ }
115
+ ];
116
+
117
+ invalidHexExamples.forEach((example, index) => {
118
+ console.log(`\n${index + 1}. ${example.name}:`);
119
+ console.log(` Description: ${example.description}`);
120
+ console.log(` Hex: ${example.hex}`);
121
+
122
+ const result = acceptRawTransaction(example.hex);
123
+ console.log(` Result: ${result.accepted ? '✅ ACCEPTED' : '❌ REJECTED'} (Expected: REJECTED)`);
124
+
125
+ if (result.errors.length > 0) {
126
+ console.log(` Error: ${result.errors[0]}`);
127
+ }
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Example: Parse and analyze transaction components
133
+ */
134
+ function exampleTransactionAnalysis() {
135
+ console.log('\n' + '═'.repeat(80));
136
+ console.log('🔍 Transaction Component Analysis');
137
+ console.log('═'.repeat(80));
138
+
139
+ try {
140
+ const config = loadConfig();
141
+ const wallet = config.wallet;
142
+ const utxo = config.utxo;
143
+
144
+ // Create simple transaction with proper SIGHASH_FORKID
145
+ const tx = new bsv.Transaction()
146
+ .from(utxo)
147
+ .to(new bsv.PrivateKey().toAddress(), 5000)
148
+ .change(wallet.address)
149
+ .fee(500)
150
+ .sign(bsv.PrivateKey.fromWIF(wallet.privateKeyWIF), bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
151
+
152
+ const rawHex = tx.toString();
153
+
154
+ console.log('📋 Transaction Analysis:');
155
+ console.log(`🆔 TXID: ${tx.id}`);
156
+ console.log(`📦 Raw Hex: ${rawHex}`);
157
+ console.log(`📏 Size: ${rawHex.length / 2} bytes`);
158
+ console.log(`💰 Fee: ${tx.getFee()} satoshis`);
159
+
160
+ console.log('\n🔍 Input Details:');
161
+ tx.inputs.forEach((input, i) => {
162
+ console.log(` Input ${i}:`);
163
+ console.log(` Previous TXID: ${input.prevTxId.toString('hex')}`);
164
+ console.log(` Output Index: ${input.outputIndex}`);
165
+ console.log(` Script: ${input.script.toHex()}`);
166
+ console.log(` Script ASM: ${input.script.toASM()}`);
167
+ });
168
+
169
+ console.log('\n📤 Output Details:');
170
+ tx.outputs.forEach((output, i) => {
171
+ console.log(` Output ${i}:`);
172
+ console.log(` Value: ${output.satoshis} satoshis`);
173
+ console.log(` Script: ${output.script.toHex()}`);
174
+ console.log(` Script ASM: ${output.script.toASM()}`);
175
+ try {
176
+ console.log(` Address: ${output.script.toAddress()}`);
177
+ } catch (e) {
178
+ console.log(` Address: [Non-standard script]`);
179
+ }
180
+ });
181
+
182
+ } catch (error) {
183
+ console.error('❌ Error in analysis:', error.message);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Run all raw transaction examples
189
+ */
190
+ function runAllExamples() {
191
+ exampleRawTransactionValidation();
192
+ exampleInvalidRawHex();
193
+ exampleTransactionAnalysis();
194
+
195
+ console.log('\n🎯 Raw transaction validation examples completed!');
196
+ console.log('💡 The miner now validates transactions using:');
197
+ console.log(' - Raw hex format validation');
198
+ console.log(' - UTXO existence checking');
199
+ console.log(' - BSV script interpreter for signature verification');
200
+ console.log(' - Transaction balance and structure validation');
201
+ }
202
+
203
+ // Run examples if called directly
204
+ if (require.main === module) {
205
+ runAllExamples();
206
+ }
207
+
208
+ module.exports = {
209
+ exampleRawTransactionValidation,
210
+ exampleInvalidRawHex,
211
+ exampleTransactionAnalysis,
212
+ runAllExamples
213
+ };
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 🎯 Simple Transaction Success Demo
5
+ *
6
+ * Demonstrates a successful transaction flow with simplified validation
7
+ * to show the complete mining process working.
8
+ */
9
+
10
+ const bsv = require('../index.js');
11
+ const {
12
+ loadBlockchainState,
13
+ saveBlockchainState,
14
+ getUTXO,
15
+ spendUTXO,
16
+ addUTXO,
17
+ getBlockchainStats
18
+ } = require('./blockchain-state');
19
+ const {
20
+ loadConfig,
21
+ updateUTXOFromTransaction
22
+ } = require('./utxo-manager');
23
+
24
+ /**
25
+ * Simplified miner that accepts valid transaction structure
26
+ */
27
+ function acceptTransactionSimplified(transaction) {
28
+ console.log('\n📡 SIMPLIFIED MINER: Transaction received');
29
+ console.log(`Transaction ID: ${transaction.id}`);
30
+ console.log(`Inputs: ${transaction.inputs.length}`);
31
+ console.log(`Outputs: ${transaction.outputs.length}`);
32
+
33
+ // Basic validation only
34
+ let isValid = true;
35
+ const errors = [];
36
+
37
+ // Check UTXO existence
38
+ for (const input of transaction.inputs) {
39
+ const prevTxId = input.prevTxId.toString('hex');
40
+ const outputIndex = input.outputIndex;
41
+ const utxoResult = getUTXO(prevTxId, outputIndex);
42
+
43
+ if (!utxoResult.exists) {
44
+ isValid = false;
45
+ errors.push(`UTXO ${prevTxId}:${outputIndex} does not exist`);
46
+ } else {
47
+ console.log(`✅ UTXO ${prevTxId}:${outputIndex} exists (${utxoResult.utxo.satoshis} sats)`);
48
+ }
49
+ }
50
+
51
+ // Check basic transaction structure
52
+ if (transaction.inputs.length === 0) {
53
+ isValid = false;
54
+ errors.push('No inputs provided');
55
+ }
56
+
57
+ if (transaction.outputs.length === 0) {
58
+ isValid = false;
59
+ errors.push('No outputs provided');
60
+ }
61
+
62
+ if (isValid) {
63
+ console.log('✅ Basic validation passed - Processing transaction...');
64
+
65
+ // Process the transaction
66
+ const state = loadBlockchainState();
67
+
68
+ // Spend input UTXOs
69
+ for (const input of transaction.inputs) {
70
+ const prevTxId = input.prevTxId.toString('hex');
71
+ const outputIndex = input.outputIndex;
72
+ spendUTXO(prevTxId, outputIndex, transaction.id);
73
+ }
74
+
75
+ // Create new UTXOs
76
+ for (let i = 0; i < transaction.outputs.length; i++) {
77
+ const output = transaction.outputs[i];
78
+ try {
79
+ const outputAddress = output.script.toAddress();
80
+ const newUTXO = {
81
+ txid: transaction.id,
82
+ vout: i,
83
+ outputIndex: i,
84
+ script: output.script.toHex(),
85
+ scriptPubKey: output.script.toHex(),
86
+ satoshis: output.satoshis,
87
+ address: outputAddress.toString()
88
+ };
89
+
90
+ addUTXO(newUTXO, outputAddress.toString());
91
+ console.log(`✅ Created UTXO ${transaction.id}:${i} -> ${outputAddress} (${output.satoshis} sats)`);
92
+ } catch (error) {
93
+ console.error(`❌ Error creating output ${i}: ${error.message}`);
94
+ }
95
+ }
96
+
97
+ // Update block height
98
+ state.metadata.blockHeight += 1;
99
+ state.transactionHistory.push({
100
+ txid: transaction.id,
101
+ processedAt: new Date().toISOString(),
102
+ inputCount: transaction.inputs.length,
103
+ outputCount: transaction.outputs.length
104
+ });
105
+
106
+ saveBlockchainState(state);
107
+
108
+ console.log('🎉 Transaction processed successfully!');
109
+ console.log(`🏗️ New block height: ${state.metadata.blockHeight}`);
110
+
111
+ return { accepted: true, txid: transaction.id, errors: [] };
112
+ } else {
113
+ console.log('❌ Transaction rejected');
114
+ errors.forEach(error => console.log(` - ${error}`));
115
+ return { accepted: false, txid: transaction.id, errors };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Demo successful transaction
121
+ */
122
+ function demoSuccessfulTransaction() {
123
+ console.log('🎯 BSV Successful Transaction Demo');
124
+ console.log('═'.repeat(80));
125
+
126
+ try {
127
+ // Load wallet config
128
+ const config = loadConfig();
129
+ const wallet = config.wallet;
130
+
131
+ // Create recipient
132
+ const recipientKey = new bsv.PrivateKey();
133
+ const recipientAddress = recipientKey.toAddress().toString();
134
+
135
+ // Get UTXO
136
+ const utxo = config.utxo;
137
+
138
+ console.log('📋 Transaction Setup:');
139
+ console.log(`👛 From: ${wallet.address}`);
140
+ console.log(`🎯 To: ${recipientAddress}`);
141
+ console.log(`💰 Amount: 15,000 satoshis`);
142
+ console.log(`💳 UTXO: ${utxo.txid}:${utxo.vout} (${utxo.satoshis} sats)`);
143
+ console.log(`💵 Fee: 1,000 satoshis`);
144
+ console.log(`🔄 Change: ${utxo.satoshis - 15000 - 1000} satoshis\n`);
145
+
146
+ // Create transaction
147
+ const tx = new bsv.Transaction()
148
+ .from(utxo)
149
+ .to(recipientAddress, 15000)
150
+ .change(wallet.address)
151
+ .fee(1000)
152
+ .sign(bsv.PrivateKey.fromWIF(wallet.privateKeyWIF));
153
+
154
+ console.log('✅ Transaction created:');
155
+ console.log(`🆔 TXID: ${tx.id}`);
156
+ console.log(`📦 Raw hex: ${tx.toString()}`);
157
+ console.log(`📏 Size: ${tx.toString().length / 2} bytes\n`);
158
+
159
+ // Show blockchain state before
160
+ console.log('📊 Blockchain state BEFORE transaction:');
161
+ getBlockchainStats();
162
+
163
+ // Process with simplified miner
164
+ const result = acceptTransactionSimplified(tx);
165
+
166
+ if (result.accepted) {
167
+ // Update local wallet too
168
+ updateUTXOFromTransaction(tx, utxo);
169
+
170
+ console.log('\n📊 Blockchain state AFTER transaction:');
171
+ getBlockchainStats();
172
+
173
+ console.log('\n💰 Local wallet state:');
174
+ const { loadConfig } = require('./utxo-manager');
175
+ const updatedConfig = loadConfig();
176
+ console.log(`Available UTXOs: ${updatedConfig.availableUTXOs?.length || 0}`);
177
+ console.log(`Total balance: ${updatedConfig.availableUTXOs?.reduce((sum, u) => sum + u.satoshis, 0) || 0} sats`);
178
+ }
179
+
180
+ } catch (error) {
181
+ console.error('❌ Error:', error.message);
182
+ }
183
+ }
184
+
185
+ // Run if called directly
186
+ if (require.main === module) {
187
+ demoSuccessfulTransaction();
188
+ }
189
+
190
+ module.exports = {
191
+ acceptTransactionSimplified,
192
+ demoSuccessfulTransaction
193
+ };