smartledger-bsv 3.0.0 ā 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.
- package/CHANGELOG.md +111 -0
- package/README.md +31 -3
- package/bsv.min.js +8 -8
- package/index.js +10 -0
- package/lib/crypto/ecdsa.js +57 -38
- package/lib/crypto/smartledger_verify.js +42 -11
- 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 +29 -1
- 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,328 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* š BSV Transaction Examples
|
|
5
|
+
*
|
|
6
|
+
* Demonstrates the complete transaction flow:
|
|
7
|
+
* 1. Create transaction
|
|
8
|
+
* 2. Broadcast to miner
|
|
9
|
+
* 3. Miner validates and processes
|
|
10
|
+
* 4. UTXO set updates
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const bsv = require('../index.js');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
// Import our utilities
|
|
18
|
+
const {
|
|
19
|
+
loadBlockchainState,
|
|
20
|
+
importWalletFromFile,
|
|
21
|
+
getBlockchainStats
|
|
22
|
+
} = require('./blockchain-state');
|
|
23
|
+
const {
|
|
24
|
+
acceptTransaction,
|
|
25
|
+
getMempoolStatus
|
|
26
|
+
} = require('./miner-simulator');
|
|
27
|
+
const {
|
|
28
|
+
loadConfig,
|
|
29
|
+
updateUTXOFromTransaction
|
|
30
|
+
} = require('./utxo-manager');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the blockchain with our test wallet
|
|
34
|
+
*/
|
|
35
|
+
async function initializeBlockchain() {
|
|
36
|
+
console.log('š Initializing blockchain with test wallet...\n');
|
|
37
|
+
|
|
38
|
+
// Import wallet from wallet.json into blockchain state
|
|
39
|
+
const imported = importWalletFromFile();
|
|
40
|
+
|
|
41
|
+
if (!imported) {
|
|
42
|
+
console.log('ā Failed to import wallet. Run wallet-setup.js first!');
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('');
|
|
47
|
+
getBlockchainStats();
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a simple P2PKH transaction
|
|
53
|
+
*/
|
|
54
|
+
function createSimpleTransaction(fromAddress, toAddress, amount, privateKey, utxo) {
|
|
55
|
+
console.log(`\nšø Creating P2PKH transaction:`);
|
|
56
|
+
console.log(`From: ${fromAddress}`);
|
|
57
|
+
console.log(`To: ${toAddress}`);
|
|
58
|
+
console.log(`Amount: ${amount} satoshis\n`);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// Create transaction with proper SIGHASH_FORKID
|
|
62
|
+
const tx = new bsv.Transaction()
|
|
63
|
+
.from(utxo)
|
|
64
|
+
.to(toAddress, amount)
|
|
65
|
+
.change(fromAddress)
|
|
66
|
+
.fee(1000) // 1000 sat fee
|
|
67
|
+
.sign(privateKey, bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
|
|
68
|
+
|
|
69
|
+
console.log(`ā
Transaction created: ${tx.id}`);
|
|
70
|
+
console.log(`Inputs: ${tx.inputs.length}`);
|
|
71
|
+
console.log(`Outputs: ${tx.outputs.length}`);
|
|
72
|
+
console.log(`Fee: ${tx.getFee()} satoshis\n`);
|
|
73
|
+
|
|
74
|
+
return tx;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error('ā Error creating transaction:', error.message);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Example 1: Simple P2PKH payment
|
|
83
|
+
*/
|
|
84
|
+
async function exampleSimplePayment() {
|
|
85
|
+
console.log('\n' + '='.repeat(80));
|
|
86
|
+
console.log('š EXAMPLE 1: Simple P2PKH Payment');
|
|
87
|
+
console.log('='.repeat(80));
|
|
88
|
+
|
|
89
|
+
// Load wallet config
|
|
90
|
+
const config = loadConfig();
|
|
91
|
+
const wallet = config.wallet;
|
|
92
|
+
|
|
93
|
+
// Create recipient address (just generate a new one for demo)
|
|
94
|
+
const recipientKey = new bsv.PrivateKey();
|
|
95
|
+
const recipientAddress = recipientKey.toAddress().toString();
|
|
96
|
+
|
|
97
|
+
console.log(`š Sender: ${wallet.address}`);
|
|
98
|
+
console.log(`šÆ Recipient: ${recipientAddress}`);
|
|
99
|
+
|
|
100
|
+
// Use the primary UTXO
|
|
101
|
+
const utxo = config.utxo;
|
|
102
|
+
console.log(`š° Using UTXO: ${utxo.txid}:${utxo.vout} (${utxo.satoshis} sats)`);
|
|
103
|
+
|
|
104
|
+
// Create transaction (send 15,000 sats)
|
|
105
|
+
const amount = 15000;
|
|
106
|
+
const tx = createSimpleTransaction(
|
|
107
|
+
wallet.address,
|
|
108
|
+
recipientAddress,
|
|
109
|
+
amount,
|
|
110
|
+
bsv.PrivateKey.fromWIF(wallet.privateKeyWIF),
|
|
111
|
+
utxo
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (!tx) {
|
|
115
|
+
console.log('ā Failed to create transaction');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Broadcast to miner
|
|
120
|
+
console.log('š” Broadcasting transaction to miner...');
|
|
121
|
+
const result = acceptTransaction(tx);
|
|
122
|
+
|
|
123
|
+
if (result.accepted) {
|
|
124
|
+
console.log('\nš Transaction accepted and processed!');
|
|
125
|
+
|
|
126
|
+
// Update local wallet
|
|
127
|
+
updateUTXOFromTransaction(tx, utxo);
|
|
128
|
+
|
|
129
|
+
console.log('\nš Updated blockchain state:');
|
|
130
|
+
getBlockchainStats();
|
|
131
|
+
} else {
|
|
132
|
+
console.log('\nā Transaction rejected by miner');
|
|
133
|
+
console.log('Errors:', result.errors);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Example 2: Chain multiple transactions
|
|
139
|
+
*/
|
|
140
|
+
async function exampleTransactionChain() {
|
|
141
|
+
console.log('\n' + '='.repeat(80));
|
|
142
|
+
console.log('š EXAMPLE 2: Transaction Chain');
|
|
143
|
+
console.log('='.repeat(80));
|
|
144
|
+
|
|
145
|
+
const config = loadConfig();
|
|
146
|
+
const wallet = config.wallet;
|
|
147
|
+
|
|
148
|
+
// Find an available UTXO for the second transaction
|
|
149
|
+
const availableUTXOs = config.availableUTXOs || [];
|
|
150
|
+
|
|
151
|
+
if (availableUTXOs.length === 0) {
|
|
152
|
+
console.log('ā No available UTXOs for chaining. Run example 1 first.');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Use the first available UTXO
|
|
157
|
+
const utxo = availableUTXOs[0];
|
|
158
|
+
console.log(`š° Using UTXO: ${utxo.txid}:${utxo.vout} (${utxo.satoshis} sats)`);
|
|
159
|
+
|
|
160
|
+
// Create another recipient
|
|
161
|
+
const recipientKey = new bsv.PrivateKey();
|
|
162
|
+
const recipientAddress = recipientKey.toAddress().toString();
|
|
163
|
+
|
|
164
|
+
console.log(`š Sender: ${wallet.address}`);
|
|
165
|
+
console.log(`šÆ Recipient: ${recipientAddress}`);
|
|
166
|
+
|
|
167
|
+
// Create second transaction
|
|
168
|
+
const amount = Math.min(5000, utxo.satoshis - 1000); // Leave room for fee
|
|
169
|
+
const tx = createSimpleTransaction(
|
|
170
|
+
wallet.address,
|
|
171
|
+
recipientAddress,
|
|
172
|
+
amount,
|
|
173
|
+
bsv.PrivateKey.fromWIF(wallet.privateKeyWIF),
|
|
174
|
+
utxo
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (!tx) {
|
|
178
|
+
console.log('ā Failed to create transaction');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Broadcast to miner
|
|
183
|
+
console.log('š” Broadcasting transaction to miner...');
|
|
184
|
+
const result = acceptTransaction(tx);
|
|
185
|
+
|
|
186
|
+
if (result.accepted) {
|
|
187
|
+
console.log('\nš Transaction accepted and processed!');
|
|
188
|
+
|
|
189
|
+
// Update local wallet
|
|
190
|
+
updateUTXOFromTransaction(tx, utxo);
|
|
191
|
+
|
|
192
|
+
console.log('\nš Updated blockchain state:');
|
|
193
|
+
getBlockchainStats();
|
|
194
|
+
} else {
|
|
195
|
+
console.log('\nā Transaction rejected by miner');
|
|
196
|
+
console.log('Errors:', result.errors);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Example 3: Multi-output transaction
|
|
202
|
+
*/
|
|
203
|
+
async function exampleMultiOutput() {
|
|
204
|
+
console.log('\n' + '='.repeat(80));
|
|
205
|
+
console.log('š EXAMPLE 3: Multi-Output Transaction');
|
|
206
|
+
console.log('='.repeat(80));
|
|
207
|
+
|
|
208
|
+
const config = loadConfig();
|
|
209
|
+
const wallet = config.wallet;
|
|
210
|
+
|
|
211
|
+
// Find an available UTXO
|
|
212
|
+
const availableUTXOs = config.availableUTXOs || [];
|
|
213
|
+
|
|
214
|
+
if (availableUTXOs.length === 0) {
|
|
215
|
+
console.log('ā No available UTXOs. Run example 1 first.');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Use the largest available UTXO
|
|
220
|
+
const utxo = availableUTXOs.reduce((max, current) =>
|
|
221
|
+
current.satoshis > max.satoshis ? current : max
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
console.log(`š° Using UTXO: ${utxo.txid}:${utxo.vout} (${utxo.satoshis} sats)`);
|
|
225
|
+
|
|
226
|
+
// Create multiple recipients
|
|
227
|
+
const recipient1 = new bsv.PrivateKey().toAddress().toString();
|
|
228
|
+
const recipient2 = new bsv.PrivateKey().toAddress().toString();
|
|
229
|
+
const recipient3 = new bsv.PrivateKey().toAddress().toString();
|
|
230
|
+
|
|
231
|
+
console.log(`š Sender: ${wallet.address}`);
|
|
232
|
+
console.log(`šÆ Recipients: ${recipient1.slice(0, 10)}... ${recipient2.slice(0, 10)}... ${recipient3.slice(0, 10)}...`);
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// Create multi-output transaction with proper SIGHASH_FORKID
|
|
236
|
+
const tx = new bsv.Transaction()
|
|
237
|
+
.from(utxo)
|
|
238
|
+
.to(recipient1, 3000)
|
|
239
|
+
.to(recipient2, 4000)
|
|
240
|
+
.to(recipient3, 5000)
|
|
241
|
+
.change(wallet.address)
|
|
242
|
+
.fee(1000)
|
|
243
|
+
.sign(bsv.PrivateKey.fromWIF(wallet.privateKeyWIF), bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
|
|
244
|
+
|
|
245
|
+
console.log(`\nā
Multi-output transaction created: ${tx.id}`);
|
|
246
|
+
console.log(`Inputs: ${tx.inputs.length}`);
|
|
247
|
+
console.log(`Outputs: ${tx.outputs.length}`);
|
|
248
|
+
console.log(`Total sent: 12,000 satoshis to 3 recipients`);
|
|
249
|
+
|
|
250
|
+
// Broadcast to miner
|
|
251
|
+
console.log('\nš” Broadcasting transaction to miner...');
|
|
252
|
+
const result = acceptTransaction(tx);
|
|
253
|
+
|
|
254
|
+
if (result.accepted) {
|
|
255
|
+
console.log('\nš Transaction accepted and processed!');
|
|
256
|
+
|
|
257
|
+
// Update local wallet
|
|
258
|
+
updateUTXOFromTransaction(tx, utxo);
|
|
259
|
+
|
|
260
|
+
console.log('\nš Updated blockchain state:');
|
|
261
|
+
getBlockchainStats();
|
|
262
|
+
} else {
|
|
263
|
+
console.log('\nā Transaction rejected by miner');
|
|
264
|
+
console.log('Errors:', result.errors);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('ā Error creating multi-output transaction:', error.message);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Run all examples
|
|
273
|
+
*/
|
|
274
|
+
async function runAllExamples() {
|
|
275
|
+
console.log('š BSV Transaction Examples');
|
|
276
|
+
console.log('š Demonstrating complete transaction flow with miner simulation\n');
|
|
277
|
+
|
|
278
|
+
// Initialize blockchain state
|
|
279
|
+
const initialized = await initializeBlockchain();
|
|
280
|
+
if (!initialized) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Show initial miner status
|
|
285
|
+
console.log('\nš Initial miner status:');
|
|
286
|
+
getMempoolStatus();
|
|
287
|
+
|
|
288
|
+
// Run examples
|
|
289
|
+
await exampleSimplePayment();
|
|
290
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Small delay
|
|
291
|
+
|
|
292
|
+
await exampleTransactionChain();
|
|
293
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Small delay
|
|
294
|
+
|
|
295
|
+
await exampleMultiOutput();
|
|
296
|
+
|
|
297
|
+
// Final status
|
|
298
|
+
console.log('\nš Final miner status:');
|
|
299
|
+
getMempoolStatus();
|
|
300
|
+
|
|
301
|
+
console.log('\nšÆ Examples completed!');
|
|
302
|
+
console.log('Check blockchain-state.json to see the global UTXO set');
|
|
303
|
+
console.log('Check wallet.json to see your local wallet state');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// If called directly, run examples
|
|
307
|
+
if (require.main === module) {
|
|
308
|
+
const args = process.argv.slice(2);
|
|
309
|
+
|
|
310
|
+
if (args[0] === '1') {
|
|
311
|
+
initializeBlockchain().then(() => exampleSimplePayment());
|
|
312
|
+
} else if (args[0] === '2') {
|
|
313
|
+
exampleTransactionChain();
|
|
314
|
+
} else if (args[0] === '3') {
|
|
315
|
+
exampleMultiOutput();
|
|
316
|
+
} else {
|
|
317
|
+
runAllExamples();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
module.exports = {
|
|
322
|
+
initializeBlockchain,
|
|
323
|
+
createSimpleTransaction,
|
|
324
|
+
exampleSimplePayment,
|
|
325
|
+
exampleTransactionChain,
|
|
326
|
+
exampleMultiOutput,
|
|
327
|
+
runAllExamples
|
|
328
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* š BSV UTXO Manager
|
|
5
|
+
*
|
|
6
|
+
* Updates the wallet.json file with new UTXOs after transactions.
|
|
7
|
+
* Tracks which UTXOs are spent and which are available for BSV development.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const bsv = require('../index.js');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
const configPath = path.join(__dirname, 'wallet.json');
|
|
16
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function saveConfig(config) {
|
|
20
|
+
const configPath = path.join(__dirname, 'wallet.json');
|
|
21
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
22
|
+
console.log('š¾ Updated wallet.json');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function updateUTXOFromTransaction(tx, spentUTXO = null) {
|
|
26
|
+
console.log('š Updating UTXO set from transaction...\n');
|
|
27
|
+
|
|
28
|
+
const config = loadConfig();
|
|
29
|
+
|
|
30
|
+
console.log('š Transaction Analysis:');
|
|
31
|
+
console.log(`Transaction ID: ${tx.id}`);
|
|
32
|
+
console.log(`Inputs: ${tx.inputs.length}`);
|
|
33
|
+
console.log(`Outputs: ${tx.outputs.length}\n`);
|
|
34
|
+
|
|
35
|
+
// Mark spent UTXO
|
|
36
|
+
if (spentUTXO) {
|
|
37
|
+
console.log('ā Spent UTXO:');
|
|
38
|
+
console.log(` TXID: ${spentUTXO.txid}`);
|
|
39
|
+
console.log(` Vout: ${spentUTXO.vout}`);
|
|
40
|
+
console.log(` Amount: ${spentUTXO.satoshis} satoshis\n`);
|
|
41
|
+
|
|
42
|
+
// Add to spent UTXOs list
|
|
43
|
+
if (!config.spentUTXOs) config.spentUTXOs = [];
|
|
44
|
+
config.spentUTXOs.push({
|
|
45
|
+
txid: spentUTXO.txid,
|
|
46
|
+
vout: spentUTXO.vout,
|
|
47
|
+
satoshis: spentUTXO.satoshis,
|
|
48
|
+
spentInTx: tx.id,
|
|
49
|
+
spentAt: new Date().toISOString()
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add new UTXOs from outputs
|
|
54
|
+
const newUTXOs = [];
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < tx.outputs.length; i++) {
|
|
57
|
+
const output = tx.outputs[i];
|
|
58
|
+
const outputAddress = output.script.toAddress();
|
|
59
|
+
|
|
60
|
+
// Only track UTXOs that go to our wallet address
|
|
61
|
+
if (outputAddress.toString() === config.wallet.address) {
|
|
62
|
+
const newUTXO = {
|
|
63
|
+
txid: tx.id,
|
|
64
|
+
vout: i,
|
|
65
|
+
outputIndex: i,
|
|
66
|
+
script: output.script.toHex(),
|
|
67
|
+
scriptPubKey: output.script.toHex(),
|
|
68
|
+
satoshis: output.satoshis,
|
|
69
|
+
address: outputAddress.toString(),
|
|
70
|
+
createdAt: new Date().toISOString()
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
newUTXOs.push(newUTXO);
|
|
74
|
+
|
|
75
|
+
console.log(`ā
New UTXO ${i}:`);
|
|
76
|
+
console.log(` TXID: ${newUTXO.txid}`);
|
|
77
|
+
console.log(` Vout: ${newUTXO.vout}`);
|
|
78
|
+
console.log(` Amount: ${newUTXO.satoshis} satoshis`);
|
|
79
|
+
console.log(` Address: ${newUTXO.address}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Update config with new UTXOs
|
|
84
|
+
if (!config.availableUTXOs) config.availableUTXOs = [];
|
|
85
|
+
config.availableUTXOs = config.availableUTXOs.concat(newUTXOs);
|
|
86
|
+
|
|
87
|
+
// Update the main UTXO to the largest available UTXO
|
|
88
|
+
if (newUTXOs.length > 0) {
|
|
89
|
+
const largestUTXO = newUTXOs.reduce((max, utxo) =>
|
|
90
|
+
utxo.satoshis > max.satoshis ? utxo : max
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
config.utxo = largestUTXO;
|
|
94
|
+
console.log(`\nšÆ Primary UTXO updated to: ${largestUTXO.satoshis} satoshis`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Update metadata
|
|
98
|
+
config.metadata.lastUpdated = new Date().toISOString();
|
|
99
|
+
config.metadata.transactionCount = (config.metadata.transactionCount || 0) + 1;
|
|
100
|
+
|
|
101
|
+
saveConfig(config);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
newUTXOs,
|
|
105
|
+
totalValue: newUTXOs.reduce((sum, utxo) => sum + utxo.satoshis, 0),
|
|
106
|
+
config
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getAvailableBalance() {
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
|
|
113
|
+
const balance = config.availableUTXOs
|
|
114
|
+
? config.availableUTXOs.reduce((sum, utxo) => sum + utxo.satoshis, 0)
|
|
115
|
+
: config.utxo.satoshis;
|
|
116
|
+
|
|
117
|
+
return balance;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function listUTXOs() {
|
|
121
|
+
const config = loadConfig();
|
|
122
|
+
|
|
123
|
+
console.log('š° Current UTXO Set:');
|
|
124
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
125
|
+
|
|
126
|
+
console.log('šÆ Primary UTXO:');
|
|
127
|
+
console.log(` TXID: ${config.utxo.txid}`);
|
|
128
|
+
console.log(` Vout: ${config.utxo.vout}`);
|
|
129
|
+
console.log(` Amount: ${config.utxo.satoshis} satoshis\n`);
|
|
130
|
+
|
|
131
|
+
if (config.availableUTXOs && config.availableUTXOs.length > 0) {
|
|
132
|
+
console.log('š All Available UTXOs:');
|
|
133
|
+
config.availableUTXOs.forEach((utxo, index) => {
|
|
134
|
+
console.log(` ${index + 1}. ${utxo.txid}:${utxo.vout} - ${utxo.satoshis} sats`);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (config.spentUTXOs && config.spentUTXOs.length > 0) {
|
|
139
|
+
console.log('\nā Spent UTXOs:');
|
|
140
|
+
config.spentUTXOs.forEach((utxo, index) => {
|
|
141
|
+
console.log(` ${index + 1}. ${utxo.txid}:${utxo.vout} - ${utxo.satoshis} sats (spent in ${utxo.spentInTx})`);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const totalBalance = getAvailableBalance();
|
|
146
|
+
console.log(`\nš° Total Available Balance: ${totalBalance} satoshis`);
|
|
147
|
+
|
|
148
|
+
return config;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// If called directly, list current UTXOs
|
|
152
|
+
if (require.main === module) {
|
|
153
|
+
listUTXOs();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
updateUTXOFromTransaction,
|
|
158
|
+
getAvailableBalance,
|
|
159
|
+
listUTXOs,
|
|
160
|
+
loadConfig,
|
|
161
|
+
saveConfig
|
|
162
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* šÆ BSV Wallet Setup Utility
|
|
5
|
+
*
|
|
6
|
+
* Creates a test wallet with mock UTXO for BSV development and testing.
|
|
7
|
+
* Generates a consistent environment with private keys, addresses, and UTXOs.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const bsv = require('../index.js');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// Import the mock UTXO functionality from mock-utxo-generator.js
|
|
15
|
+
function randomHex(len) {
|
|
16
|
+
const crypto = require('crypto');
|
|
17
|
+
return crypto.randomBytes(len).toString('hex');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createMockTxId() {
|
|
21
|
+
return randomHex(32);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildP2pkhScriptHex(address) {
|
|
25
|
+
return bsv.Script.buildPublicKeyHashOut(address).toHex();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function mkUtxo(privateKey, sats = 100000) {
|
|
29
|
+
const address = privateKey.toAddress().toString();
|
|
30
|
+
const txid = createMockTxId();
|
|
31
|
+
const vout = 0;
|
|
32
|
+
const scriptHex = buildP2pkhScriptHex(address);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
txId: txid,
|
|
36
|
+
txid: txid,
|
|
37
|
+
vout: vout,
|
|
38
|
+
outputIndex: vout,
|
|
39
|
+
satoshis: sats,
|
|
40
|
+
value: sats,
|
|
41
|
+
script: scriptHex,
|
|
42
|
+
scriptPubKey: scriptHex,
|
|
43
|
+
address,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createTestWallet() {
|
|
48
|
+
console.log('š§ Creating test wallet...\n');
|
|
49
|
+
|
|
50
|
+
// Generate a new private key for testing
|
|
51
|
+
const privateKey = new bsv.PrivateKey();
|
|
52
|
+
const publicKey = privateKey.toPublicKey();
|
|
53
|
+
const address = privateKey.toAddress();
|
|
54
|
+
|
|
55
|
+
console.log('š Test Wallet Details:');
|
|
56
|
+
console.log(`Private Key (WIF): ${privateKey.toWIF()}`);
|
|
57
|
+
console.log(`Private Key (Hex): ${privateKey.toString('hex')}`);
|
|
58
|
+
console.log(`Public Key: ${publicKey.toString()}`);
|
|
59
|
+
console.log(`Address: ${address.toString()}`);
|
|
60
|
+
console.log(`Pubkey Hash160: ${bsv.crypto.Hash.sha256ripemd160(publicKey.toBuffer()).toString('hex')}\n`);
|
|
61
|
+
|
|
62
|
+
// Create a mock UTXO with sufficient funds for testing
|
|
63
|
+
const utxo = mkUtxo(privateKey, 50000); // 50,000 satoshis
|
|
64
|
+
|
|
65
|
+
console.log('š° Mock UTXO Created:');
|
|
66
|
+
console.log(`TXID: ${utxo.txid}`);
|
|
67
|
+
console.log(`Vout: ${utxo.vout}`);
|
|
68
|
+
console.log(`Satoshis: ${utxo.satoshis}`);
|
|
69
|
+
console.log(`Script: ${utxo.script}\n`);
|
|
70
|
+
|
|
71
|
+
// Create test environment config
|
|
72
|
+
const testConfig = {
|
|
73
|
+
wallet: {
|
|
74
|
+
privateKeyWIF: privateKey.toWIF(),
|
|
75
|
+
privateKeyHex: privateKey.toString('hex'),
|
|
76
|
+
publicKey: publicKey.toString(),
|
|
77
|
+
address: address.toString(),
|
|
78
|
+
pubkeyHash160: bsv.crypto.Hash.sha256ripemd160(publicKey.toBuffer()).toString('hex')
|
|
79
|
+
},
|
|
80
|
+
utxo: {
|
|
81
|
+
txid: utxo.txid,
|
|
82
|
+
vout: utxo.vout,
|
|
83
|
+
outputIndex: utxo.outputIndex,
|
|
84
|
+
script: utxo.script,
|
|
85
|
+
scriptPubKey: utxo.scriptPubKey,
|
|
86
|
+
satoshis: utxo.satoshis,
|
|
87
|
+
address: utxo.address
|
|
88
|
+
},
|
|
89
|
+
testParams: {
|
|
90
|
+
nLockTimeTarget: 1000,
|
|
91
|
+
sighashType: bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID,
|
|
92
|
+
covenantAmount: 40000, // Amount for covenant output
|
|
93
|
+
fee: 1000, // Transaction fee
|
|
94
|
+
changeAmount: 9000 // Remaining change
|
|
95
|
+
},
|
|
96
|
+
metadata: {
|
|
97
|
+
created: new Date().toISOString(),
|
|
98
|
+
description: 'Test wallet and UTXO for BSV preimage covenant testing',
|
|
99
|
+
version: '1.0'
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return testConfig;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function saveTestConfig(config) {
|
|
107
|
+
const configPath = path.join(__dirname, 'wallet.json');
|
|
108
|
+
|
|
109
|
+
console.log('š¾ Saving wallet configuration...');
|
|
110
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
111
|
+
|
|
112
|
+
console.log(`ā
Wallet configuration saved to: ${configPath}\n`);
|
|
113
|
+
|
|
114
|
+
return configPath;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function displaySetupSummary(config) {
|
|
118
|
+
console.log('š Test Environment Summary:');
|
|
119
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
120
|
+
console.log(`Address: ${config.wallet.address}`);
|
|
121
|
+
console.log(`Balance: ${config.utxo.satoshis} satoshis`);
|
|
122
|
+
console.log(`Mock TXID: ${config.utxo.txid.substring(0, 16)}...`);
|
|
123
|
+
console.log(`nLockTime Target: ${config.testParams.nLockTimeTarget}`);
|
|
124
|
+
console.log(`Covenant Amount: ${config.testParams.covenantAmount} satoshis`);
|
|
125
|
+
console.log(`Fee: ${config.testParams.fee} satoshis`);
|
|
126
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
127
|
+
|
|
128
|
+
console.log('\nšÆ Next Steps:');
|
|
129
|
+
console.log('1. Use the wallet credentials in your tests');
|
|
130
|
+
console.log('2. Import wallet.json in your test files');
|
|
131
|
+
console.log('3. Call updateUTXOFromTransaction() after each transaction\n');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Main execution
|
|
135
|
+
function main() {
|
|
136
|
+
console.log('š BSV Preimage Covenant - Test Wallet Setup\n');
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Create test wallet and configuration
|
|
140
|
+
const testConfig = createTestWallet();
|
|
141
|
+
|
|
142
|
+
// Save configuration to file
|
|
143
|
+
const configPath = saveTestConfig(testConfig);
|
|
144
|
+
|
|
145
|
+
// Display summary
|
|
146
|
+
displaySetupSummary(testConfig);
|
|
147
|
+
|
|
148
|
+
console.log('ā
Test wallet setup complete!');
|
|
149
|
+
console.log(`š Configuration file: ${path.basename(configPath)}`);
|
|
150
|
+
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('ā Error setting up test wallet:', error.message);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Run if called directly
|
|
158
|
+
if (require.main === module) {
|
|
159
|
+
main();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Export functions for use in other files
|
|
163
|
+
module.exports = {
|
|
164
|
+
createTestWallet,
|
|
165
|
+
mkUtxo,
|
|
166
|
+
saveTestConfig
|
|
167
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"wallet": {
|
|
3
|
+
"privateKeyWIF": "KwbaQqFUpt9MYQRrkVWk7QC799nAfcqYRYKj4ZNeZBkut3YP45rJ",
|
|
4
|
+
"privateKeyHex": "KwbaQqFUpt9MYQRrkVWk7QC799nAfcqYRYKj4ZNeZBkut3YP45rJ",
|
|
5
|
+
"publicKey": "02330726b97010d7bb1f88b1213330e644916cedfd5fee9c57153592c6b5a44fe9",
|
|
6
|
+
"address": "15XJXD7CSMqHL2ivFCu8PZTACQQ8MPbWY9",
|
|
7
|
+
"pubkeyHash160": "319b9b8eef10d00cf84b5d4772dca69b5f5a7b5c"
|
|
8
|
+
},
|
|
9
|
+
"utxo": {
|
|
10
|
+
"txid": "d6625d13f3130360dea7ebe8ccf8ccdb5e40b3cd5ea51b2094983a3789354c3e",
|
|
11
|
+
"vout": 0,
|
|
12
|
+
"outputIndex": 0,
|
|
13
|
+
"script": "76a914319b9b8eef10d00cf84b5d4772dca69b5f5a7b5c88ac",
|
|
14
|
+
"scriptPubKey": "76a914319b9b8eef10d00cf84b5d4772dca69b5f5a7b5c88ac",
|
|
15
|
+
"satoshis": 50000,
|
|
16
|
+
"address": "15XJXD7CSMqHL2ivFCu8PZTACQQ8MPbWY9"
|
|
17
|
+
},
|
|
18
|
+
"testParams": {
|
|
19
|
+
"nLockTimeTarget": 1000,
|
|
20
|
+
"sighashType": 65,
|
|
21
|
+
"covenantAmount": 40000,
|
|
22
|
+
"fee": 1000,
|
|
23
|
+
"changeAmount": 9000
|
|
24
|
+
},
|
|
25
|
+
"metadata": {
|
|
26
|
+
"created": "2025-10-19T14:33:10.987Z",
|
|
27
|
+
"description": "Test wallet and UTXO for BSV preimage covenant testing",
|
|
28
|
+
"version": "1.0"
|
|
29
|
+
}
|
|
30
|
+
}
|