@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,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
|
+
}
|