@smartledger/bsv 1.5.6-fix1 → 3.1.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 +111 -0
- package/README.md +38 -10
- package/bsv.min.js +8 -8
- package/index.js +11 -0
- package/lib/crypto/ecdsa.js +57 -38
- package/lib/crypto/smartledger_verify.js +42 -11
- package/lib/custom-script-helper.js +249 -0
- package/lib/script/interpreter.js +8 -8
- package/lib/smartminer.js +169 -0
- package/lib/smartutxo.js +200 -0
- package/lib/transaction/transaction.js +39 -0
- package/package.json +30 -5
- package/utilities/README.md +132 -0
- package/utilities/blockchain-state.js +332 -0
- package/utilities/blockchain-state.json +41 -0
- package/utilities/miner-simulator.js +620 -0
- package/utilities/mock-utxo-generator.js +149 -0
- package/utilities/raw-tx-examples.js +213 -0
- package/utilities/success-demo.js +193 -0
- package/utilities/transaction-examples.js +328 -0
- package/utilities/utxo-manager.js +162 -0
- package/utilities/wallet-setup.js +167 -0
- package/utilities/wallet.json +30 -0
- package/utilities/working-signature-demo.js +181 -0
- package/validation_test.js +97 -0
|
@@ -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
|
+
};
|