smartledger-bsv 3.4.0 → 3.4.4

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +400 -0
  2. package/README.md +112 -84
  3. package/SECURITY.md +88 -0
  4. package/bin/cli.js +13 -8
  5. package/bsv-anchor.min.js +12 -0
  6. package/bsv-covenant.min.js +8 -8
  7. package/bsv-didweb.min.js +12 -0
  8. package/bsv-gdaf.min.js +9 -9
  9. package/bsv-ltp.min.js +9 -9
  10. package/bsv-mnemonic.min.js +2 -2
  11. package/bsv-shamir.min.js +3 -3
  12. package/bsv-smartcontract.min.js +5 -5
  13. package/bsv-statuslist.min.js +18 -0
  14. package/bsv-vcjwt.min.js +12 -0
  15. package/bsv.bundle.js +9 -9
  16. package/bsv.d.ts +486 -9
  17. package/bsv.min.js +5 -5
  18. package/build/webpack.anchor.config.js +9 -13
  19. package/build/webpack.didweb.config.js +10 -14
  20. package/build/webpack.statuslist.config.js +9 -14
  21. package/build/webpack.vcjwt.config.js +9 -13
  22. package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +2 -2
  23. package/docs/MODULE_REFERENCE_COMPLETE.md +61 -58
  24. package/docs/advanced/LEGAL_TOKEN_PROTOCOL.md +3 -3
  25. package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
  26. package/docs/getting-started/INSTALLATION.md +30 -30
  27. package/docs/getting-started/QUICK_START.md +18 -18
  28. package/docs/migration/FROM_BSV_1_5_6.md +16 -10
  29. package/gdaf-entry.js +1 -2
  30. package/index.js +44 -13
  31. package/lib/browser-utxo-manager-es5.js +11 -4
  32. package/lib/browser-utxo-manager.js +15 -8
  33. package/lib/ltp/claim.js +1 -0
  34. package/lib/ltp/obligation.js +1 -0
  35. package/lib/ltp/registry.js +2 -0
  36. package/lib/ltp/right.js +1 -0
  37. package/lib/smart_contract/covenant.js +10 -1
  38. package/lib/smartutxo.js +20 -12
  39. package/lib/transaction/transaction.js +8 -1
  40. package/lib/util/_.js +7 -1
  41. package/ltp-entry.js +1 -2
  42. package/package.json +11 -13
  43. package/utilities/blockchain-state.js +32 -23
  44. package/demos/README.md +0 -188
  45. package/demos/architecture_demo.js +0 -247
  46. package/demos/browser-test.html +0 -1208
  47. package/demos/bsv_wallet_demo.js +0 -242
  48. package/demos/complete_ltp_demo.js +0 -511
  49. package/demos/debug_tools_demo.js +0 -87
  50. package/demos/demo_features.js +0 -123
  51. package/demos/easy_interface_demo.js +0 -109
  52. package/demos/ecies_demo.js +0 -182
  53. package/demos/gdaf_core_test.js +0 -131
  54. package/demos/gdaf_demo.js +0 -237
  55. package/demos/ltp_demo.js +0 -361
  56. package/demos/ltp_primitives_demo.js +0 -403
  57. package/demos/message_demo.js +0 -209
  58. package/demos/preimage_separation_demo.js +0 -383
  59. package/demos/script_helper_demo.js +0 -289
  60. package/demos/security_demo.js +0 -287
  61. package/demos/shamir_demo.js +0 -121
  62. package/demos/simple_demo.js +0 -204
  63. package/demos/simple_p2pkh_demo.js +0 -169
  64. package/demos/simple_utxo_preimage_demo.js +0 -196
  65. package/demos/smart_contract_demo.html +0 -1347
  66. package/demos/smart_contract_demo.js +0 -910
  67. package/demos/utxo_generator_demo.js +0 -244
  68. package/demos/validation_pipeline_demo.js +0 -155
  69. package/demos/web3keys.html +0 -740
  70. package/examples/README.md +0 -200
  71. package/examples/basic/transaction-creation.js +0 -534
  72. package/examples/basic/transaction_signature_api_gap.js +0 -178
  73. package/examples/complete_workflow_demo.js +0 -783
  74. package/examples/covenants/advanced_covenant_demo.js +0 -219
  75. package/examples/covenants/covenant_interface_demo.js +0 -270
  76. package/examples/covenants/covenant_manual_signature_resolved.js +0 -212
  77. package/examples/covenants/covenant_signature_template.js +0 -117
  78. package/examples/covenants2/covenant_bidirectional_example.js +0 -262
  79. package/examples/covenants2/covenant_utils_demo.js +0 -120
  80. package/examples/covenants2/preimage_covenant_utils.js +0 -287
  81. package/examples/covenants2/production_integration.js +0 -256
  82. package/examples/data/covenant_utxos.json +0 -28
  83. package/examples/data/utxos.json +0 -26
  84. package/examples/definitive_working_demo.js +0 -261
  85. package/examples/final_working_contracts.js +0 -338
  86. package/examples/preimage/README.md +0 -178
  87. package/examples/preimage/extract_preimage_bidirectional.js +0 -421
  88. package/examples/preimage/generate_sample_preimage.js +0 -208
  89. package/examples/preimage/generate_sighash_examples.js +0 -152
  90. package/examples/preimage/parse_preimage.js +0 -117
  91. package/examples/preimage/test_preimage_extractor.js +0 -53
  92. package/examples/preimage/test_varint_extraction.js +0 -95
  93. package/examples/scripts/custom_script_helper_example.js +0 -273
  94. package/examples/scripts/custom_script_signature_test.js +0 -344
  95. package/examples/scripts/script_interpreter.js +0 -193
  96. package/examples/smart_contract/complete_workflow_demo.js +0 -343
  97. package/examples/smart_contract/covenant_builder_demo.js +0 -176
  98. package/examples/smart_contract/script_testing_integration.js +0 -198
  99. package/examples/smart_contract_templates.js +0 -718
  100. package/examples/working_smart_contracts.js +0 -348
  101. package/lib/smart_contract/test_integration.js +0 -269
  102. package/tests/browser-compatibility/README.md +0 -35
  103. package/tests/browser-compatibility/test-cdn-vs-local.html +0 -186
  104. package/tests/browser-compatibility/test-pbkdf2.html +0 -51
  105. package/tests/bundle-completeness-test.html +0 -131
  106. package/tests/bundle-demo.html +0 -476
  107. package/tests/smartcontract-test.html +0 -239
  108. package/tests/standalone-modules-test.html +0 -260
  109. package/tests/test.html +0 -612
  110. package/tests/test_builtin_verify.js +0 -117
  111. package/tests/test_debug_integration.js +0 -71
  112. package/tests/test_ecdsa_little.js +0 -70
  113. package/tests/test_shamir.js +0 -221
  114. package/tests/test_smartverify_der.js +0 -110
  115. package/tests/test_standalone_shamir.html +0 -83
  116. package/tests/unpkg-demo.html +0 -194
  117. package/utilities/blockchain-state.json +0 -118565
@@ -1,740 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>Web3Keys Playground – All-in-One SPA (No Server)</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <!-- SmartLedger BSV Security-Hardened Library - Local Fixed v3.3.4 -->
9
- <script src="../bsv.min.js"></script>
10
- <script src="../bsv-mnemonic.min.js"></script>
11
- <script>
12
- tailwind.config = {
13
- theme: {
14
- extend: {
15
- colors: {
16
- primary: '#6B46C1',
17
- primaryDark: '#4C1D95',
18
- primaryLight: '#8B5CF6',
19
- accent: '#A855F7',
20
- }
21
- }
22
- }
23
- }
24
- </script>
25
- <style>
26
- .mono{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}
27
- .mask { -webkit-text-security: disc; text-security: disc; }
28
- </style>
29
- </head>
30
- <body class="bg-gradient-to-br from-purple-100 via-purple-50 to-indigo-100 min-h-screen">
31
- <main class="max-w-5xl mx-auto py-8 px-4">
32
- <div class="bg-white rounded-2xl shadow-xl border border-purple-200 overflow-hidden">
33
- <!-- Header -->
34
- <header class="bg-gradient-to-r from-primaryDark to-primary p-6 text-white">
35
- <div class="flex items-center justify-between">
36
- <h1 class="text-2xl md:text-3xl font-bold flex items-center">
37
- <svg class="w-8 h-8 mr-3 text-primaryLight" fill="none" stroke="currentColor" viewBox="0 0 24 24">
38
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
39
- </svg>
40
- Web3Keys Playground
41
- </h1>
42
- <div class="text-sm opacity-90">Pure browser demo • No server • Local-only crypto</div>
43
- </div>
44
- <p class="mt-2 text-purple-200">Explore SmartLedger BSV (security-hardened): generate/import mnemonics, derive keys, sign/verify, hash data, and encrypt/decrypt — all client-side with zero vulnerabilities.</p>
45
- </header>
46
-
47
- <!-- Status -->
48
- <div id="status" class="m-4 text-sm p-3 rounded-lg border bg-purple-50 text-purple-800 border-purple-200" aria-live="polite"></div>
49
-
50
- <!-- Nav -->
51
- <nav class="px-4 pb-4 border-b border-purple-200">
52
- <div class="flex flex-wrap gap-2">
53
- <button data-tab="keys" class="tab px-3 py-2 rounded-lg bg-primary text-white">1) Keys</button>
54
- <button data-tab="derive" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">2) Derive</button>
55
- <button data-tab="sign" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">3) Sign / Verify</button>
56
- <button data-tab="encrypt" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">4) Encrypt / Decrypt</button>
57
- <button data-tab="hash" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">5) Hash</button>
58
- <button data-tab="backup" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">6) Backup / Restore</button>
59
- <button data-tab="debug" class="tab px-3 py-2 rounded-lg hover:bg-purple-50">Debug</button>
60
- </div>
61
- </nav>
62
-
63
- <section class="p-6 space-y-8">
64
- <!-- KEYS -->
65
- <div id="tab-keys" class="tab-panel space-y-4">
66
- <h2 class="text-xl font-semibold text-primaryDark">Step 1: Generate or Import Keys</h2>
67
- <div class="grid md:grid-cols-2 gap-4">
68
- <div class="space-y-3">
69
- <button id="gen-mnemonic" class="w-full px-4 py-3 bg-gradient-to-r from-primary to-primaryDark text-white rounded-lg shadow hover:from-primaryDark hover:to-primary">Generate 24‑word mnemonic (256-bit)</button>
70
- <div id="mnemonic-display" class="hidden space-y-2">
71
- <label class="text-sm font-medium text-green-700">Generated/Imported Mnemonic (save securely!):</label>
72
- <textarea id="mnemonic-out" class="w-full p-3 border-2 border-yellow-300 bg-yellow-50 rounded-lg mono text-sm" rows="3" readonly></textarea>
73
- </div>
74
- </div>
75
- <div class="space-y-3">
76
- <button id="toggle-import" class="w-full px-4 py-3 bg-purple-100 text-primaryDark rounded-lg border border-purple-300 hover:bg-purple-200">Import existing mnemonic</button>
77
- <div id="import-wrap" class="hidden space-y-2">
78
- <textarea id="mnemonic-in" rows="3" placeholder="Enter your 12/24-word mnemonic" class="w-full p-3 border-2 border-purple-300 rounded-lg"></textarea>
79
- <button id="import-mnemonic" class="px-4 py-2 bg-primary text-white rounded-lg">Import</button>
80
- </div>
81
- </div>
82
- </div>
83
-
84
- <div class="grid md:grid-cols-2 gap-4">
85
- <div class="space-y-2">
86
- <label class="text-sm font-medium">Password (used only locally to encrypt secrets)</label>
87
- <input id="password" type="password" class="w-full p-3 border-2 border-purple-300 rounded-lg" placeholder="Strong password" />
88
- </div>
89
- <div class="space-y-2">
90
- <label class="text-sm font-medium">Identity Derivation Path</label>
91
- <input id="id-path" value="m/44'/236'/0'/0/0" class="w-full p-3 border-2 border-purple-300 rounded-lg mono" />
92
- </div>
93
- </div>
94
-
95
- <div class="grid md:grid-cols-3 gap-4">
96
- <button id="derive-identity" class="px-4 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700">Derive Identity</button>
97
- <button id="lock-secrets" class="px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Encrypt & store locally</button>
98
- <button id="clear-state" class="px-4 py-3 bg-red-100 text-red-700 rounded-lg border border-red-300 hover:bg-red-200">Reset All</button>
99
- </div>
100
-
101
- <div id="identity-box" class="hidden bg-gradient-to-r from-purple-50 to-indigo-50 border border-purple-200 rounded-lg p-4 space-y-2">
102
- <div class="text-sm">Public Key (hex)</div>
103
- <div id="pubkey" class="mono text-xs break-all"></div>
104
- <div class="text-sm mt-2">Address (BSV)</div>
105
- <div id="address" class="mono text-xs break-all"></div>
106
- <details class="text-xs mt-2">
107
- <summary class="cursor-pointer">Sensitive (WIF / PrivateKey) – click to reveal</summary>
108
- <div class="mt-2 space-y-1">
109
- <div class="text-xs">Private Key (WIF)</div>
110
- <div id="wif" class="mono text-xs break-all"></div>
111
- </div>
112
- </details>
113
- </div>
114
- </div>
115
-
116
- <!-- DERIVE -->
117
- <div id="tab-derive" class="tab-panel hidden space-y-4">
118
- <h2 class="text-xl font-semibold text-primaryDark">Derivation Playground</h2>
119
- <div class="grid md:grid-cols-3 gap-4">
120
- <div class="space-y-2 md:col-span-2">
121
- <label class="text-sm font-medium">Derivation Path</label>
122
- <input id="path-input" value="m/44'/236'/0'/0/0" class="w-full p-3 border-2 border-purple-300 rounded-lg mono" />
123
- </div>
124
- <div class="flex items-end">
125
- <button id="derive-path" class="w-full px-4 py-3 bg-primary text-white rounded-lg">Derive</button>
126
- </div>
127
- </div>
128
- <div class="grid md:grid-cols-2 gap-4">
129
- <div>
130
- <div class="text-sm">Public Key (hex)</div>
131
- <div id="derive-pub" class="mono text-xs break-all p-3 bg-white border rounded"></div>
132
- </div>
133
- <div>
134
- <div class="text-sm">Address (BSV)</div>
135
- <div id="derive-addr" class="mono text-xs break-all p-3 bg-white border rounded"></div>
136
- </div>
137
- </div>
138
- </div>
139
-
140
- <!-- SIGN / VERIFY -->
141
- <div id="tab-sign" class="tab-panel hidden space-y-4">
142
- <h2 class="text-xl font-semibold text-primaryDark">Message Signing & Verification</h2>
143
- <div class="space-y-2">
144
- <label class="text-sm font-medium">Message</label>
145
- <textarea id="msg" rows="3" class="w-full p-3 border-2 border-purple-300 rounded-lg" placeholder="Hello, Web3Keys!"></textarea>
146
- </div>
147
- <div class="grid md:grid-cols-3 gap-4">
148
- <div class="space-y-2">
149
- <label class="text-sm font-medium">Use WIF (optional). Leave blank to sign with current identity.</label>
150
- <input id="sign-wif" class="w-full p-3 border-2 border-purple-300 rounded-lg" placeholder="WIF for signing (optional)" />
151
- </div>
152
- <div class="flex items-end"><button id="sign-msg" class="w-full px-4 py-3 bg-emerald-600 text-white rounded-lg">Sign</button></div>
153
- <div class="flex items-end"><button id="verify-msg" class="w-full px-4 py-3 bg-blue-600 text-white rounded-lg">Verify</button></div>
154
- </div>
155
- <div class="grid md:grid-cols-2 gap-4">
156
- <div>
157
- <div class="text-sm">Signature (base64)</div>
158
- <textarea id="sig" class="w-full p-3 border rounded mono text-xs" rows="3" readonly></textarea>
159
- </div>
160
- <div class="space-y-2">
161
- <label class="text-sm font-medium">Public Key (hex) for verification (optional). Blank → use identity.</label>
162
- <textarea id="verify-pub" class="w-full p-3 border rounded mono text-xs" rows="3" placeholder="Hex public key"></textarea>
163
- <div id="verify-result" class="text-sm"></div>
164
- </div>
165
- </div>
166
- </div>
167
-
168
- <!-- ENCRYPT / DECRYPT -->
169
- <div id="tab-encrypt" class="tab-panel hidden space-y-4">
170
- <h2 class="text-xl font-semibold text-primaryDark">Encrypt / Decrypt (Password-based)</h2>
171
- <div class="grid md:grid-cols-2 gap-4">
172
- <div class="space-y-2">
173
- <label class="text-sm font-medium">Plaintext JSON</label>
174
- <textarea id="plain" rows="6" class="w-full p-3 border-2 border-purple-300 rounded-lg mono">{"hello":"world"}</textarea>
175
- <button id="do-encrypt" class="px-4 py-2 bg-primary text-white rounded">Encrypt with password</button>
176
- </div>
177
- <div class="space-y-2">
178
- <label class="text-sm font-medium">Ciphertext (paste here to decrypt)</label>
179
- <textarea id="cipher" rows="6" class="w-full p-3 border-2 border-purple-300 rounded-lg mono"></textarea>
180
- <button id="do-decrypt" class="px-4 py-2 bg-primaryDark text-white rounded">Decrypt with password</button>
181
- </div>
182
- </div>
183
- <div>
184
- <label class="text-sm font-medium">Result</label>
185
- <pre id="enc-result" class="p-3 bg-gray-50 border rounded mono text-xs overflow-auto"></pre>
186
- </div>
187
- </div>
188
-
189
- <!-- HASH -->
190
- <div id="tab-hash" class="tab-panel hidden space-y-4">
191
- <h2 class="text-xl font-semibold text-primaryDark">Hash Utilities</h2>
192
- <div class="grid md:grid-cols-2 gap-4">
193
- <div class="space-y-2">
194
- <label class="text-sm font-medium">Input (string)</label>
195
- <textarea id="hash-input" rows="4" class="w-full p-3 border-2 border-purple-300 rounded-lg" placeholder="Any text…"></textarea>
196
- <div class="flex flex-wrap gap-2">
197
- <button data-algo="SHA256" class="hash-btn px-3 py-2 bg-primary text-white rounded">SHA-256</button>
198
- <button data-algo="SHA512" class="hash-btn px-3 py-2 bg-primaryDark text-white rounded">SHA-512</button>
199
- <button data-algo="RIPEMD160" class="hash-btn px-3 py-2 bg-purple-200 rounded">RIPEMD-160</button>
200
- </div>
201
- </div>
202
- <div class="space-y-2">
203
- <label class="text-sm font-medium">Output (hex)</label>
204
- <textarea id="hash-out" rows="6" class="w-full p-3 border rounded mono text-xs" readonly></textarea>
205
- </div>
206
- </div>
207
- </div>
208
-
209
- <!-- BACKUP / RESTORE -->
210
- <div id="tab-backup" class="tab-panel hidden space-y-4">
211
- <h2 class="text-xl font-semibold text-primaryDark">Backup / Restore (Local)</h2>
212
- <p class="text-sm text-gray-700">Create an encrypted JSON bundle containing your mnemonic and current identity WIF. Keep your password safe; it cannot be recovered.</p>
213
- <div class="flex flex-wrap gap-3">
214
- <button id="export-backup" class="px-4 py-2 bg-emerald-600 text-white rounded">Export encrypted backup</button>
215
- <label class="px-4 py-2 bg-blue-600 text-white rounded cursor-pointer">
216
- Import backup JSON<input id="import-backup" type="file" accept="application/json" class="hidden" />
217
- </label>
218
- </div>
219
- <div class="space-y-2">
220
- <label class="text-sm font-medium">Preview</label>
221
- <pre id="backup-preview" class="p-3 bg-gray-50 border rounded mono text-xs overflow-auto"></pre>
222
- </div>
223
- </div>
224
-
225
- <!-- DEBUG -->
226
- <div id="tab-debug" class="tab-panel hidden space-y-4">
227
- <h2 class="text-xl font-semibold text-primaryDark">Debug</h2>
228
- <div class="space-y-4">
229
- <div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
230
- <h3 class="font-semibold text-yellow-800">CDN createHmac Issue Information</h3>
231
- <p class="text-sm text-yellow-700 mt-2">If you're seeing "createHmac is not a function" errors, this is because the CDN version uses Node.js crypto modules that don't exist in browsers.</p>
232
- <p class="text-sm text-yellow-700 mt-1"><strong>Solution:</strong> The CDN bundles need browser-compatible PBKDF2 implementation using BSV's crypto modules instead of Node.js crypto.</p>
233
- </div>
234
- <div class="grid md:grid-cols-2 gap-4">
235
- <div>
236
- <div class="text-sm">Local Storage (truncated)</div>
237
- <pre id="ls-state" class="p-3 bg-gray-50 border rounded mono text-xs"></pre>
238
- </div>
239
- <div>
240
- <div class="text-sm">SDK Info</div>
241
- <pre id="sdk-info" class="p-3 bg-gray-50 border rounded mono text-xs"></pre>
242
- </div>
243
- </div>
244
- <div>
245
- <div class="text-sm">Crypto Availability Test</div>
246
- <button id="test-crypto" class="px-3 py-2 bg-blue-600 text-white rounded">Test Crypto Functions</button>
247
- <pre id="crypto-test" class="p-3 bg-gray-50 border rounded mono text-xs mt-2"></pre>
248
- </div>
249
- </div>
250
- </div>
251
- </section>
252
-
253
- <footer class="px-6 py-5 bg-gradient-to-r from-primaryDark to-primary text-white flex flex-col md:flex-row md:items-center md:justify-between gap-2">
254
- <div class="text-sm opacity-90">Powered by <a href="https://smartledger.technology" class="underline hover:text-primaryLight">SmartLedger Technology</a> BSV v3.3.4 • Security-Hardened</div>
255
- <div class="text-sm">Client-side only • No network calls • Zero vulnerabilities</div>
256
- </footer>
257
- </div>
258
- </main>
259
-
260
- <script>
261
- // Define Buffer using BSV's implementation for cross-compatibility
262
- const Buffer = bsv.deps.Buffer;
263
- const bsvHash = bsv.crypto.Hash;
264
- console.log('bsvHash:', bsvHash);
265
- console.log('Buffer:', Buffer);
266
- // ---------- Helpers ----------
267
- const $ = (id) => document.getElementById(id);
268
- const qs = (sel) => document.querySelector(sel);
269
- const qsa = (sel) => Array.from(document.querySelectorAll(sel));
270
-
271
- const setStatus = (msg, good=true) => {
272
- const el = $('status');
273
- el.textContent = msg || '';
274
- el.className = 'm-4 text-sm p-3 rounded-lg border ' + (good ? 'bg-green-50 text-green-700 border-green-200' : 'bg-red-50 text-red-700 border-red-200');
275
- if (msg) setTimeout(()=> setStatus(''), 5000);
276
- };
277
-
278
- const canonicalStringify = (obj) => JSON.stringify(sortKeysDeep(obj));
279
- function sortKeysDeep(v){
280
- if (Array.isArray(v)) return v.map(sortKeysDeep);
281
- if (v && typeof v === 'object') return Object.keys(v).sort().reduce((a,k)=> (a[k]=sortKeysDeep(v[k]), a),{});
282
- return v;
283
- }
284
-
285
- // ---------- SDK Convenience ----------
286
- const SL = {
287
- genMnemonic: () => {
288
- // Generate 24-word mnemonic using BSV Mnemonic module
289
- const MnemonicClass = window.bsvMnemonic || window.Mnemonic || (window.bsv && window.bsv.Mnemonic);
290
- if (!MnemonicClass) throw new Error('Mnemonic module not loaded');
291
- // Use 256 bits of entropy for 24 words (default is 128 bits for 12 words)
292
- return MnemonicClass.fromRandom(256).phrase;
293
- },
294
- validateMnemonic: (m) => {
295
- try {
296
- const MnemonicClass = window.bsvMnemonic || window.Mnemonic || (window.bsv && window.bsv.Mnemonic);
297
- if (!MnemonicClass) return false;
298
- new MnemonicClass(m);
299
- return true;
300
- } catch {
301
- return false;
302
- }
303
- },
304
- derivePath: (m, path) => {
305
- // Derive key from mnemonic and path
306
- const MnemonicClass = window.bsvMnemonic || window.Mnemonic || (window.bsv && window.bsv.Mnemonic);
307
- if (!MnemonicClass) throw new Error('Mnemonic module not loaded');
308
-
309
- const mnemonic = new MnemonicClass(m);
310
- const hdPrivateKey = bsv.HDPrivateKey.fromSeed(mnemonic.toSeed());
311
- const derived = hdPrivateKey.deriveChild(path);
312
- return {
313
- wif: derived.privateKey.toWIF(),
314
- publicKey: derived.privateKey.toPublicKey().toString('hex'),
315
- address: derived.privateKey.toAddress().toString()
316
- };
317
- },
318
- encrypt: async (data, password) => {
319
- // Simple password-based encryption using Web Crypto API
320
- const encoder = new TextEncoder();
321
- const decoder = new TextDecoder();
322
- const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
323
- const salt = crypto.getRandomValues(new Uint8Array(16));
324
- const key = await crypto.subtle.deriveKey(
325
- { name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' },
326
- keyMaterial,
327
- { name: 'AES-GCM', length: 256 },
328
- false,
329
- ['encrypt']
330
- );
331
- const iv = crypto.getRandomValues(new Uint8Array(12));
332
- const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, encoder.encode(data));
333
- const result = new Uint8Array(salt.length + iv.length + encrypted.byteLength);
334
- result.set(salt, 0);
335
- result.set(iv, salt.length);
336
- result.set(new Uint8Array(encrypted), salt.length + iv.length);
337
- return btoa(String.fromCharCode(...result));
338
- },
339
- decrypt: async (encData, password) => {
340
- // Decrypt using Web Crypto API
341
- const encoder = new TextEncoder();
342
- const decoder = new TextDecoder();
343
- const data = new Uint8Array(atob(encData).split('').map(c => c.charCodeAt(0)));
344
- const salt = data.slice(0, 16);
345
- const iv = data.slice(16, 28);
346
- const encrypted = data.slice(28);
347
- const keyMaterial = await crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey']);
348
- const key = await crypto.subtle.deriveKey(
349
- { name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' },
350
- keyMaterial,
351
- { name: 'AES-GCM', length: 256 },
352
- false,
353
- ['decrypt']
354
- );
355
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, encrypted);
356
- return decoder.decode(decrypted);
357
- },
358
- hash: async (data, algo) => {
359
- // Use BSV crypto functions directly since they're available
360
- if (window.bsv && bsv.crypto && bsv.crypto.Hash) {
361
- try {
362
- // Create a proper buffer using BSV's Buffer (defined at top of script)
363
- const buffer = Buffer.from(data, 'utf8');
364
-
365
- let hashResult;
366
- switch(algo) {
367
- case 'SHA256':
368
- hashResult = bsv.crypto.Hash.sha256(buffer);
369
- break;
370
- case 'SHA512':
371
- hashResult = bsv.crypto.Hash.sha512(buffer);
372
- break;
373
- case 'RIPEMD160':
374
- hashResult = bsv.crypto.Hash.ripemd160(buffer);
375
- break;
376
- default:
377
- throw new Error('Unsupported hash algorithm: ' + algo);
378
- }
379
-
380
- // Convert result to hex string
381
- if (hashResult && typeof hashResult.toString === 'function') {
382
- return hashResult.toString('hex');
383
- } else if (hashResult && hashResult.length) {
384
- // Handle array-like results
385
- return Array.from(hashResult).map(b => b.toString(16).padStart(2, '0')).join('');
386
- } else {
387
- throw new Error('Unexpected hash result format');
388
- }
389
-
390
- } catch (e) {
391
- console.error('BSV hash failed:', e);
392
- throw new Error(`${algo} hashing failed: ${e.message}`);
393
- }
394
- } else {
395
- // Fallback to Web Crypto API for SHA algorithms only
396
- const encoder = new TextEncoder();
397
- const dataBuffer = encoder.encode(data);
398
-
399
- switch(algo) {
400
- case 'SHA256': {
401
- const hash = await crypto.subtle.digest('SHA-256', dataBuffer);
402
- return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
403
- }
404
- case 'SHA512': {
405
- const hash = await crypto.subtle.digest('SHA-512', dataBuffer);
406
- return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
407
- }
408
- case 'RIPEMD160':
409
- throw new Error('RIPEMD160 not supported - BSV library not available');
410
- default:
411
- throw new Error('Unsupported hash algorithm: ' + algo);
412
- }
413
- }
414
- },
415
- signMessage: async (msg, wif) => {
416
- const privateKey = bsv.PrivateKey.fromWIF(wif);
417
- return bsv.Message(msg).sign(privateKey);
418
- },
419
- verifySignature: async (msg, sig, pubHex) => {
420
- try {
421
- const publicKey = bsv.PublicKey.fromString(pubHex);
422
- const address = bsv.Address.fromPublicKey(publicKey);
423
- return bsv.Message(msg).verify(address, sig);
424
- } catch {
425
- return false;
426
- }
427
- },
428
- bsv: () => window.bsv,
429
- };
430
-
431
- // ---------- State ----------
432
- const LS = {
433
- encMnemonic: 'w3k_encMnemonic',
434
- encWif: 'w3k_encWif',
435
- pubKey: 'w3k_pubKey',
436
- addr: 'w3k_address',
437
- idPath: 'w3k_idPath'
438
- };
439
-
440
- let mnemonic = null;
441
- let identity = null; // { wif, publicKey, address }
442
-
443
- function saveState(){
444
- if (identity?.publicKey) localStorage.setItem(LS.pubKey, identity.publicKey);
445
- if (identity?.address) localStorage.setItem(LS.addr, identity.address);
446
- if ($('id-path').value) localStorage.setItem(LS.idPath, $('id-path').value);
447
- }
448
- function refreshDebug(){
449
- const obj = {};
450
- Object.values(LS).forEach(k=>{
451
- const v = localStorage.getItem(k);
452
- if (v) obj[k] = v.slice(0,64) + (v.length>64?'…':'');
453
- });
454
- $('ls-state').textContent = JSON.stringify(obj, null, 2);
455
- $('sdk-info').textContent = JSON.stringify({
456
- bsvPresent: !!window.bsv,
457
- hasMessage: !!(window.bsv && bsv.Message),
458
- hasMnemonic: !!(window.bsvMnemonic || window.Mnemonic || (window.bsv && bsv.Mnemonic)),
459
- hasSmartLedger: !!(window.bsv && bsv.SmartLedger),
460
- mnemonicModule: window.bsvMnemonic ? 'bsvMnemonic' : window.Mnemonic ? 'Mnemonic' : 'bsv.Mnemonic',
461
- mnemonicAvailable: {
462
- bsvMnemonic: !!window.bsvMnemonic,
463
- globalMnemonic: !!window.Mnemonic,
464
- bsvInternal: !!(window.bsv && bsv.Mnemonic)
465
- },
466
- version: window.bsv ? `SmartLedger BSV v${bsv.version || '3.3.4'}` : 'SmartLedger BSV v3.3.4'
467
- }, null, 2);
468
- }
469
-
470
- function showTab(name){
471
- qsa('.tab').forEach(b=> b.classList.remove('bg-primary','text-white'));
472
- qsa('.tab').find(b=> b.dataset.tab===name)?.classList.add('bg-primary','text-white');
473
- qsa('.tab-panel').forEach(p=> p.classList.add('hidden'));
474
- $('tab-'+name).classList.remove('hidden');
475
- }
476
-
477
- // ---------- Key / Identity ops ----------
478
- async function deriveIdentity(){
479
- if (!mnemonic) throw new Error('Generate or import a mnemonic first.');
480
- const path = $('id-path').value.trim();
481
- const k = await SL.derivePath(mnemonic, path);
482
- const bsv = SL.bsv();
483
- const priv = bsv.PrivateKey.fromWIF(k.wif);
484
- const pubHex = priv.toPublicKey().toString('hex');
485
- const address = bsv.Address.fromPrivateKey(priv).toString();
486
- identity = { wif: k.wif, publicKey: pubHex, address };
487
- $('pubkey').textContent = pubHex;
488
- $('address').textContent = address;
489
- $('wif').textContent = k.wif;
490
- $('identity-box').classList.remove('hidden');
491
- setStatus('Identity derived.');
492
- saveState();
493
- }
494
-
495
- // ---------- Event wiring ----------
496
- document.addEventListener('DOMContentLoaded', () => {
497
- // Tabs
498
- qsa('.tab').forEach(b=> b.addEventListener('click', ()=> showTab(b.dataset.tab)));
499
-
500
- // Restore path
501
- const savedPath = localStorage.getItem(LS.idPath);
502
- if (savedPath) $('id-path').value = savedPath;
503
-
504
- // Keys
505
- $('gen-mnemonic').onclick = async () => {
506
- mnemonic = await SL.genMnemonic();
507
- $('mnemonic-out').value = mnemonic;
508
- $('mnemonic-display').classList.remove('hidden');
509
- setStatus('Mnemonic generated – write it down and store offline.');
510
- };
511
- $('toggle-import').onclick = () => $('import-wrap').classList.toggle('hidden');
512
- $('import-mnemonic').onclick = () => {
513
- const m = $('mnemonic-in').value.trim();
514
- if (!SL.validateMnemonic(m)) return setStatus('Invalid mnemonic', false);
515
- mnemonic = m;
516
- $('mnemonic-out').value = mnemonic;
517
- $('mnemonic-display').classList.remove('hidden');
518
- $('import-wrap').classList.add('hidden');
519
- setStatus('Mnemonic imported.');
520
- };
521
-
522
- $('derive-identity').onclick = async () => {
523
- try { await deriveIdentity(); } catch(e){ setStatus(e.message, false); }
524
- };
525
-
526
- $('lock-secrets').onclick = async () => {
527
- try {
528
- const pw = $('password').value;
529
- if (!pw) return setStatus('Enter a password first.', false);
530
- if (!mnemonic) return setStatus('Generate/import mnemonic first.', false);
531
- if (!identity?.wif) await deriveIdentity();
532
- const encMnemonic = await SL.encrypt(mnemonic, pw);
533
- const encWif = await SL.encrypt(identity.wif, pw);
534
- localStorage.setItem(LS.encMnemonic, encMnemonic);
535
- localStorage.setItem(LS.encWif, encWif);
536
- saveState();
537
- refreshDebug();
538
- setStatus('Secrets encrypted and stored locally.');
539
- } catch(e){ setStatus(e.message||'Encrypt/store failed', false); }
540
- };
541
-
542
- $('clear-state').onclick = () => {
543
- mnemonic = null; identity = null;
544
- Object.values(LS).forEach(k=> localStorage.removeItem(k));
545
- $('mnemonic-out').value=''; $('wif').textContent=''; $('pubkey').textContent=''; $('address').textContent='';
546
- $('identity-box').classList.add('hidden');
547
- refreshDebug();
548
- setStatus('All local state cleared.');
549
- };
550
-
551
- // Derivation playground
552
- $('derive-path').onclick = async () => {
553
- try {
554
- if (!mnemonic) return setStatus('Generate/import mnemonic first.', false);
555
- const path = $('path-input').value.trim();
556
- const k = await SL.derivePath(mnemonic, path);
557
- const bsv = SL.bsv();
558
- const addr = bsv.Address.fromPrivateKey(bsv.PrivateKey.fromWIF(k.wif)).toString();
559
- $('derive-pub').textContent = bsv.PrivateKey.fromWIF(k.wif).toPublicKey().toString('hex');
560
- $('derive-addr').textContent = addr;
561
- setStatus('Path derived.');
562
- } catch(e){ setStatus(e.message, false); }
563
- };
564
-
565
- // Sign/verify
566
- $('sign-msg').onclick = async () => {
567
- try {
568
- const msg = $('msg').value;
569
- if (!msg) return setStatus('Enter a message to sign.', false);
570
- let wif = $('sign-wif').value.trim();
571
- if (!wif){
572
- if (!identity?.wif) await deriveIdentity();
573
- wif = identity.wif;
574
- }
575
- const sig = await SL.signMessage(msg, wif);
576
- $('sig').value = sig;
577
- setStatus('Message signed.');
578
- } catch(e){ setStatus(e.message, false); }
579
- };
580
-
581
- $('verify-msg').onclick = async () => {
582
- try {
583
- const msg = $('msg').value;
584
- const sig = $('sig').value.trim();
585
- let pub = $('verify-pub').value.trim();
586
- if (!pub){
587
- if (!identity?.publicKey) await deriveIdentity();
588
- pub = identity.publicKey;
589
- }
590
- const ok = await SL.verifySignature(msg, sig, pub);
591
- $('verify-result').textContent = ok ? '✅ Valid signature' : '❌ Invalid signature';
592
- $('verify-result').className = ok ? 'text-green-700' : 'text-red-700';
593
- setStatus('Verification complete.');
594
- } catch(e){ setStatus(e.message, false); }
595
- };
596
-
597
- // Encrypt / Decrypt
598
- $('do-encrypt').onclick = async () => {
599
- try {
600
- const pw = $('password').value;
601
- if (!pw) return setStatus('Enter password (top of page) to encrypt.', false);
602
- const json = $('plain').value;
603
- const enc = await SL.encrypt(json, pw);
604
- $('cipher').value = enc;
605
- $('enc-result').textContent = 'Encrypted.';
606
- setStatus('Encryption complete.');
607
- } catch(e){ setStatus(e.message, false); }
608
- };
609
- $('do-decrypt').onclick = async () => {
610
- try {
611
- const pw = $('password').value;
612
- if (!pw) return setStatus('Enter password to decrypt.', false);
613
- const enc = $('cipher').value;
614
- const dec = await SL.decrypt(enc, pw);
615
- $('enc-result').textContent = dec;
616
- setStatus('Decryption complete.');
617
- } catch(e){ setStatus(e.message||'Decryption failed', false); }
618
- };
619
-
620
- // Hash
621
- qsa('.hash-btn').forEach(b=> b.onclick = async () => {
622
- try {
623
- const algo = b.dataset.algo;
624
- const v = $('hash-input').value || '';
625
- const out = await SL.hash(v, algo);
626
- $('hash-out').value = out;
627
- setStatus(`${algo} computed.`);
628
- } catch(e){ setStatus(e.message, false); }
629
- });
630
-
631
- // Backup/Restore
632
- $('export-backup').onclick = async () => {
633
- try {
634
- const pw = $('password').value;
635
- if (!pw) return setStatus('Enter password first.', false);
636
- if (!mnemonic) return setStatus('Generate/import mnemonic first.', false);
637
- if (!identity?.wif) await deriveIdentity();
638
- const bundle = {
639
- createdAt: new Date().toISOString(),
640
- idPath: $('id-path').value.trim(),
641
- encMnemonic: await SL.encrypt(mnemonic, pw),
642
- encWif: await SL.encrypt(identity.wif, pw),
643
- pubKey: identity.publicKey,
644
- address: identity.address
645
- };
646
- const blob = new Blob([canonicalStringify(bundle)], { type: 'application/json' });
647
- const url = URL.createObjectURL(blob);
648
- const a = document.createElement('a');
649
- a.href = url; a.download = 'web3keys-backup.json'; a.click();
650
- URL.revokeObjectURL(url);
651
- $('backup-preview').textContent = canonicalStringify(bundle);
652
- setStatus('Encrypted backup exported.');
653
- } catch(e){ setStatus(e.message, false); }
654
- };
655
-
656
- $('import-backup').onchange = async (ev) => {
657
- try {
658
- const file = ev.target.files?.[0];
659
- if (!file) return;
660
- const text = await file.text();
661
- const json = JSON.parse(text);
662
- $('backup-preview').textContent = canonicalStringify(json);
663
- // Do not auto-decrypt; user can copy enc values into Encrypt/Decrypt tab to test with their password.
664
- setStatus('Backup loaded (preview shown). Use your password in Encrypt/Decrypt to open fields.');
665
- } catch(e){ setStatus(e.message, false); }
666
- };
667
-
668
- // Crypto test
669
- $('test-crypto').onclick = () => {
670
- let result = 'Testing crypto availability...\n\n';
671
-
672
- try {
673
- // Test Node.js crypto
674
- result += 'Node.js crypto module: ';
675
- if (typeof require !== 'undefined') {
676
- try {
677
- const crypto = require('crypto');
678
- result += crypto ? 'Available\n' : 'Not available\n';
679
- result += ` createHmac: ${typeof crypto.createHmac}\n`;
680
- } catch (e) {
681
- result += `Error: ${e.message}\n`;
682
- }
683
- } else {
684
- result += 'require() not available (browser environment)\n';
685
- }
686
-
687
- // Test BSV crypto
688
- result += '\nBSV crypto module: ';
689
- if (window.bsv && bsv.crypto && bsv.crypto.Hash) {
690
- result += 'Available\n';
691
- result += ` Hash.sha256: ${typeof bsv.crypto.Hash.sha256}\n`;
692
- result += ` Hash.sha512: ${typeof bsv.crypto.Hash.sha512}\n`;
693
- result += ` Hash.sha256hmac: ${typeof bsv.crypto.Hash.sha256hmac}\n`;
694
- result += ` Hash.sha512hmac: ${typeof bsv.crypto.Hash.sha512hmac}\n`;
695
-
696
- // Test HMAC functionality
697
- try {
698
- const testData = Buffer.from('test');
699
- const testKey = Buffer.from('key');
700
- const hmacResult = bsv.crypto.Hash.sha512hmac(testData, testKey);
701
- result += ` HMAC test: Success (${hmacResult.length} bytes)\n`;
702
- } catch (e) {
703
- result += ` HMAC test: Error - ${e.message}\n`;
704
- }
705
- } else {
706
- result += 'Not available\n';
707
- }
708
-
709
- // Test mnemonic PBKDF2
710
- result += '\nMnemonic PBKDF2 test: ';
711
- try {
712
- const MnemonicClass = window.bsvMnemonic || window.Mnemonic || (window.bsv && window.bsv.Mnemonic);
713
- if (MnemonicClass) {
714
- const mnemonic = MnemonicClass.fromRandom(128); // Use 128 for faster test
715
- result += 'Success\n';
716
- result += ` Generated: ${mnemonic.phrase.split(' ').slice(0, 3).join(' ')}...\n`;
717
- } else {
718
- result += 'Mnemonic class not available\n';
719
- }
720
- } catch (e) {
721
- result += `Error - ${e.message}\n`;
722
- if (e.message.includes('createHmac')) {
723
- result += ' ^ This is the createHmac issue!\n';
724
- }
725
- }
726
-
727
- } catch (e) {
728
- result += `\nUnexpected error: ${e.message}\n`;
729
- }
730
-
731
- $('crypto-test').textContent = result;
732
- };
733
-
734
- // Defaults
735
- showTab('keys');
736
- refreshDebug();
737
- });
738
- </script>
739
- </body>
740
- </html>