@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.
@@ -0,0 +1,332 @@
1
+ /**
2
+ * 🌐 BSV Blockchain State Manager
3
+ *
4
+ * Simulates a global blockchain state with multiple wallets and UTXO tracking.
5
+ * Acts as the "blockchain database" for our miner simulator.
6
+ */
7
+
8
+ // Browser-compatible imports
9
+ let bsv, fs, path, BLOCKCHAIN_STATE_PATH;
10
+
11
+ // Only require Node.js modules in Node.js environment
12
+ if (typeof window === 'undefined' && typeof require === 'function') {
13
+ try {
14
+ bsv = require('../index.js');
15
+ fs = require('fs');
16
+ path = require('path');
17
+ BLOCKCHAIN_STATE_PATH = path.join(__dirname, 'blockchain-state.json');
18
+ } catch (e) {
19
+ console.warn('BlockchainState: Running in browser mode - persistence disabled');
20
+ }
21
+ } else {
22
+ // In browser, try to get bsv from global scope or fallback
23
+ bsv = (typeof window !== 'undefined' && window.bsv) || require('../index.js');
24
+ }
25
+
26
+ /**
27
+ * Initialize empty blockchain state
28
+ */
29
+ function initializeBlockchainState() {
30
+ return {
31
+ metadata: {
32
+ createdAt: new Date().toISOString(),
33
+ lastUpdated: new Date().toISOString(),
34
+ totalWallets: 0,
35
+ totalUTXOs: 0,
36
+ totalValue: 0,
37
+ blockHeight: 0
38
+ },
39
+ wallets: {}, // keyed by address
40
+ globalUTXOSet: {}, // keyed by "txid:vout"
41
+ spentUTXOs: {}, // keyed by "txid:vout"
42
+ transactionHistory: []
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Load blockchain state from file
48
+ */
49
+ function loadBlockchainState() {
50
+ try {
51
+ // In browser, use localStorage or return initial state
52
+ if (!fs || !BLOCKCHAIN_STATE_PATH) {
53
+ return initializeBlockchainState();
54
+ }
55
+
56
+ if (!fs.existsSync(BLOCKCHAIN_STATE_PATH)) {
57
+ console.log('🆕 Creating new blockchain state...');
58
+ const initialState = initializeBlockchainState();
59
+ saveBlockchainState(initialState);
60
+ return initialState;
61
+ }
62
+
63
+ const state = JSON.parse(fs.readFileSync(BLOCKCHAIN_STATE_PATH, 'utf8'));
64
+ console.log('📖 Loaded existing blockchain state');
65
+ return state;
66
+ } catch (error) {
67
+ console.error('❌ Error loading blockchain state:', error.message);
68
+ return initializeBlockchainState();
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Save blockchain state to file
74
+ */
75
+ function saveBlockchainState(state) {
76
+ try {
77
+ state.metadata.lastUpdated = new Date().toISOString();
78
+
79
+ // Only save to file in Node.js environment
80
+ if (fs && BLOCKCHAIN_STATE_PATH) {
81
+ fs.writeFileSync(BLOCKCHAIN_STATE_PATH, JSON.stringify(state, null, 2));
82
+ console.log('💾 Blockchain state saved');
83
+ }
84
+ } catch (error) {
85
+ console.error('❌ Error saving blockchain state:', error.message);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Register a new wallet in the blockchain state
91
+ */
92
+ function registerWallet(walletAddress, walletData) {
93
+ console.log(`📝 Registering wallet: ${walletAddress}`);
94
+
95
+ const state = loadBlockchainState();
96
+
97
+ if (state.wallets[walletAddress]) {
98
+ console.log('â„šī¸ Wallet already exists, updating...');
99
+ }
100
+
101
+ state.wallets[walletAddress] = {
102
+ address: walletAddress,
103
+ registeredAt: walletData.registeredAt || new Date().toISOString(),
104
+ utxos: walletData.utxos || [],
105
+ totalValue: 0
106
+ };
107
+
108
+ // Add UTXOs to global set
109
+ if (walletData.utxos) {
110
+ walletData.utxos.forEach(utxo => {
111
+ const utxoKey = `${utxo.txid}:${utxo.vout}`;
112
+ state.globalUTXOSet[utxoKey] = {
113
+ ...utxo,
114
+ ownerAddress: walletAddress
115
+ };
116
+ });
117
+ }
118
+
119
+ // Update metadata
120
+ state.metadata.totalWallets = Object.keys(state.wallets).length;
121
+ updateBlockchainMetadata(state);
122
+
123
+ saveBlockchainState(state);
124
+
125
+ console.log(`✅ Wallet registered: ${walletAddress}`);
126
+ return state;
127
+ }
128
+
129
+ /**
130
+ * Get UTXO by key (txid:vout)
131
+ */
132
+ function getUTXO(txid, vout) {
133
+ const state = loadBlockchainState();
134
+ const utxoKey = `${txid}:${vout}`;
135
+
136
+ if (state.spentUTXOs[utxoKey]) {
137
+ return { exists: false, spent: true, utxo: state.spentUTXOs[utxoKey] };
138
+ }
139
+
140
+ if (state.globalUTXOSet[utxoKey]) {
141
+ return { exists: true, spent: false, utxo: state.globalUTXOSet[utxoKey] };
142
+ }
143
+
144
+ return { exists: false, spent: false, utxo: null };
145
+ }
146
+
147
+ /**
148
+ * Check if UTXO exists and is unspent
149
+ */
150
+ function isUTXOAvailable(txid, vout) {
151
+ const result = getUTXO(txid, vout);
152
+ return result.exists && !result.spent;
153
+ }
154
+
155
+ /**
156
+ * Spend a UTXO (move from available to spent)
157
+ */
158
+ function spendUTXO(txid, vout, spentInTx) {
159
+ const state = loadBlockchainState();
160
+ const utxoKey = `${txid}:${vout}`;
161
+
162
+ if (!state.globalUTXOSet[utxoKey]) {
163
+ throw new Error(`UTXO ${utxoKey} does not exist`);
164
+ }
165
+
166
+ if (state.spentUTXOs[utxoKey]) {
167
+ throw new Error(`UTXO ${utxoKey} already spent`);
168
+ }
169
+
170
+ // Move UTXO from available to spent
171
+ const utxo = state.globalUTXOSet[utxoKey];
172
+ state.spentUTXOs[utxoKey] = {
173
+ ...utxo,
174
+ spentInTx,
175
+ spentAt: new Date().toISOString()
176
+ };
177
+
178
+ delete state.globalUTXOSet[utxoKey];
179
+
180
+ // Update wallet's UTXO list
181
+ const wallet = state.wallets[utxo.ownerAddress];
182
+ if (wallet) {
183
+ wallet.utxos = wallet.utxos.filter(u => !(u.txid === txid && u.vout === vout));
184
+ }
185
+
186
+ updateBlockchainMetadata(state);
187
+ saveBlockchainState(state);
188
+ console.log(`❌ UTXO spent: ${utxoKey} in tx ${spentInTx}`);
189
+ }
190
+
191
+ /**
192
+ * Add new UTXO to the global set
193
+ */
194
+ function addUTXO(utxo, ownerAddress) {
195
+ const state = loadBlockchainState();
196
+ const utxoKey = `${utxo.txid}:${utxo.vout}`;
197
+
198
+ // Check if UTXO already exists
199
+ if (state.globalUTXOSet[utxoKey]) {
200
+ console.log(`âš ī¸ UTXO ${utxoKey} already exists, skipping`);
201
+ return;
202
+ }
203
+
204
+ state.globalUTXOSet[utxoKey] = {
205
+ ...utxo,
206
+ ownerAddress,
207
+ createdAt: new Date().toISOString()
208
+ };
209
+
210
+ // Add to wallet's UTXO list
211
+ if (!state.wallets[ownerAddress]) {
212
+ state.wallets[ownerAddress] = {
213
+ address: ownerAddress,
214
+ registeredAt: new Date().toISOString(),
215
+ utxos: [],
216
+ totalValue: 0
217
+ };
218
+ }
219
+
220
+ // Check if UTXO already exists in wallet's list
221
+ const exists = state.wallets[ownerAddress].utxos.some(existingUTXO =>
222
+ existingUTXO.txid === utxo.txid && existingUTXO.vout === utxo.vout
223
+ );
224
+
225
+ if (!exists) {
226
+ state.wallets[ownerAddress].utxos.push(utxo);
227
+ }
228
+
229
+ updateBlockchainMetadata(state);
230
+ saveBlockchainState(state);
231
+
232
+ console.log(`✅ UTXO added: ${utxoKey} for ${ownerAddress}`);
233
+ }
234
+
235
+ /**
236
+ * Update blockchain metadata
237
+ */
238
+ function updateBlockchainMetadata(state) {
239
+ const totalUTXOs = Object.keys(state.globalUTXOSet).length;
240
+ const totalValue = Object.values(state.globalUTXOSet)
241
+ .reduce((sum, utxo) => sum + utxo.satoshis, 0);
242
+
243
+ state.metadata.totalUTXOs = totalUTXOs;
244
+ state.metadata.totalValue = totalValue;
245
+
246
+ // Update wallet totals
247
+ Object.values(state.wallets).forEach(wallet => {
248
+ wallet.totalValue = wallet.utxos.reduce((sum, utxo) => sum + utxo.satoshis, 0);
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Get blockchain statistics
254
+ */
255
+ function getBlockchainStats() {
256
+ const state = loadBlockchainState();
257
+
258
+ console.log('🌐 Blockchain State Statistics:');
259
+ console.log('═══════════════════════════════════════════');
260
+ console.log(`📊 Total Wallets: ${state.metadata.totalWallets}`);
261
+ console.log(`💰 Total UTXOs: ${state.metadata.totalUTXOs}`);
262
+ console.log(`💎 Total Value: ${state.metadata.totalValue} satoshis`);
263
+ console.log(`đŸ—ī¸ Block Height: ${state.metadata.blockHeight}`);
264
+ console.log(`🕐 Last Updated: ${state.metadata.lastUpdated}\n`);
265
+
266
+ if (Object.keys(state.wallets).length > 0) {
267
+ console.log('👛 Registered Wallets:');
268
+ Object.entries(state.wallets).forEach(([address, wallet]) => {
269
+ console.log(` ${address}: ${wallet.utxos.length} UTXOs, ${wallet.totalValue} sats`);
270
+ });
271
+ }
272
+
273
+ return state;
274
+ }
275
+
276
+ /**
277
+ * Import existing wallet from wallet.json
278
+ */
279
+ function importWalletFromFile() {
280
+ const walletPath = path.join(__dirname, 'wallet.json');
281
+
282
+ if (!fs.existsSync(walletPath)) {
283
+ console.log('❌ No wallet.json found to import');
284
+ return false;
285
+ }
286
+
287
+ try {
288
+ const walletData = JSON.parse(fs.readFileSync(walletPath, 'utf8'));
289
+
290
+ console.log('đŸ“Ĩ Importing wallet from wallet.json...');
291
+
292
+ const walletInfo = {
293
+ registeredAt: new Date().toISOString(),
294
+ utxos: walletData.availableUTXOs || [walletData.utxo]
295
+ };
296
+
297
+ registerWallet(walletData.wallet.address, walletInfo);
298
+
299
+ console.log('✅ Wallet imported successfully');
300
+ return true;
301
+ } catch (error) {
302
+ console.error('❌ Error importing wallet:', error.message);
303
+ return false;
304
+ }
305
+ }
306
+
307
+ // If called directly, show stats or import wallet
308
+ if (require.main === module) {
309
+ const args = process.argv.slice(2);
310
+
311
+ if (args[0] === 'import') {
312
+ importWalletFromFile();
313
+ } else if (args[0] === 'init') {
314
+ const state = initializeBlockchainState();
315
+ saveBlockchainState(state);
316
+ console.log('🆕 Initialized new blockchain state');
317
+ }
318
+
319
+ getBlockchainStats();
320
+ }
321
+
322
+ module.exports = {
323
+ loadBlockchainState,
324
+ saveBlockchainState,
325
+ registerWallet,
326
+ getUTXO,
327
+ isUTXOAvailable,
328
+ spendUTXO,
329
+ addUTXO,
330
+ getBlockchainStats,
331
+ importWalletFromFile
332
+ };
@@ -0,0 +1,41 @@
1
+ {
2
+ "metadata": {
3
+ "createdAt": "2025-10-19T16:31:36.266Z",
4
+ "lastUpdated": "2025-10-19T16:31:36.893Z",
5
+ "totalWallets": 0,
6
+ "totalUTXOs": 1,
7
+ "totalValue": 43425,
8
+ "blockHeight": 0
9
+ },
10
+ "wallets": {
11
+ "11gECtvDapMj5ZuwpvnP6Wv9MTRGxnFRs": {
12
+ "address": "11gECtvDapMj5ZuwpvnP6Wv9MTRGxnFRs",
13
+ "registeredAt": "2025-10-19T16:31:36.893Z",
14
+ "utxos": [
15
+ {
16
+ "txid": "44c099bee41c7ffe853e4310e413781e1f543f554bafb9e46cad44f89ce3447e",
17
+ "vout": 1,
18
+ "address": "11gECtvDapMj5ZuwpvnP6Wv9MTRGxnFRs",
19
+ "satoshis": 43425,
20
+ "script": "76a9140020bee080cfdeb430cf723d952dc88b6bb7424188ac",
21
+ "height": 0
22
+ }
23
+ ],
24
+ "totalValue": 43425
25
+ }
26
+ },
27
+ "globalUTXOSet": {
28
+ "44c099bee41c7ffe853e4310e413781e1f543f554bafb9e46cad44f89ce3447e:1": {
29
+ "txid": "44c099bee41c7ffe853e4310e413781e1f543f554bafb9e46cad44f89ce3447e",
30
+ "vout": 1,
31
+ "address": "11gECtvDapMj5ZuwpvnP6Wv9MTRGxnFRs",
32
+ "satoshis": 43425,
33
+ "script": "76a9140020bee080cfdeb430cf723d952dc88b6bb7424188ac",
34
+ "height": 0,
35
+ "ownerAddress": "11gECtvDapMj5ZuwpvnP6Wv9MTRGxnFRs",
36
+ "createdAt": "2025-10-19T16:31:36.893Z"
37
+ }
38
+ },
39
+ "spentUTXOs": {},
40
+ "transactionHistory": []
41
+ }