@smartledger/bsv 3.3.4 → 3.4.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +30 -21
  2. package/README.md +169 -40
  3. package/anchor-entry.js +1 -0
  4. package/bin/cli.js +349 -0
  5. package/bsv-covenant.min.js +5 -5
  6. package/bsv-gdaf.min.js +6 -6
  7. package/bsv-ltp.min.js +6 -6
  8. package/bsv-smartcontract.min.js +9 -9
  9. package/bsv.bundle.js +5 -5
  10. package/bsv.min.js +8 -8
  11. package/build/webpack.anchor.config.js +21 -0
  12. package/build/webpack.didweb.config.js +21 -0
  13. package/build/webpack.statuslist.config.js +22 -0
  14. package/build/webpack.vcjwt.config.js +21 -0
  15. package/demos/README.md +1 -1
  16. package/demos/browser-test.html +1208 -0
  17. package/demos/smart_contract_demo.html +1 -1
  18. package/demos/smart_contract_demo.js +1 -1
  19. package/demos/web3keys.html +3 -3
  20. package/didweb-entry.js +1 -0
  21. package/docs/DOCUMENTATION_REVIEW_REPORT.md +11 -11
  22. package/docs/FIX_CREATEHMAC_ISSUE.md +1 -1
  23. package/docs/MODULE_REFERENCE_COMPLETE.md +28 -28
  24. package/docs/SMARTLEDGER_BSV_USAGE_ANSWERS.md +4 -4
  25. package/docs/SMARTLEDGER_BSV_USAGE_EXAMPLES.js +2 -2
  26. package/docs/SMARTLEDGER_BSV_USAGE_GUIDE.md +3 -3
  27. package/docs/SMART_CONTRACT_DEVELOPMENT_GUIDE.md +1 -1
  28. package/docs/advanced/UTXO_MANAGER_GUIDE.md +2 -2
  29. package/docs/getting-started/INSTALLATION.md +25 -25
  30. package/docs/getting-started/QUICK_START.md +7 -7
  31. package/docs/migration/FROM_BSV_1_5_6.md +5 -5
  32. package/docs/technical/roadmap.md +3 -3
  33. package/index.js +35 -0
  34. package/lib/anchor/index.js +102 -0
  35. package/lib/browser-utxo-manager-es5.js +316 -0
  36. package/lib/browser-utxo-manager.js +533 -0
  37. package/lib/didweb/index.js +177 -0
  38. package/lib/statuslist/index.js +164 -0
  39. package/lib/vcjwt/index.js +189 -0
  40. package/package.json +13 -5
  41. package/statuslist-entry.js +1 -0
  42. package/tests/browser-compatibility/test-cdn-vs-local.html +2 -2
  43. package/vcjwt-entry.js +1 -0
package/bin/cli.js ADDED
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ /**
5
+ * SmartLedger BSV CLI
6
+ * Command-line tools for DID:web, VC-JWT, and StatusList2021
7
+ */
8
+
9
+ var fs = require('fs')
10
+ var path = require('path')
11
+ var didweb = require('../lib/didweb')
12
+ var vcjwt = require('../lib/vcjwt')
13
+ var statuslist = require('../lib/statuslist')
14
+ var anchor = require('../lib/anchor')
15
+
16
+ var args = process.argv.slice(2)
17
+ var command = args[0]
18
+ var subcommand = args[1]
19
+
20
+ // Helper to parse command-line arguments
21
+ function parseArgs(args) {
22
+ var opts = {}
23
+ for (var i = 0; i < args.length; i++) {
24
+ if (args[i].startsWith('--')) {
25
+ var key = args[i].slice(2)
26
+ var value = args[i + 1]
27
+ opts[key] = value
28
+ i++
29
+ }
30
+ }
31
+ return opts
32
+ }
33
+
34
+ // Helper to read JSON file
35
+ function readJsonFile(filepath) {
36
+ var content = fs.readFileSync(filepath, 'utf8')
37
+ return JSON.parse(content)
38
+ }
39
+
40
+ // Helper to write JSON file
41
+ function writeJsonFile(filepath, data) {
42
+ fs.writeFileSync(filepath, JSON.stringify(data, null, 2))
43
+ }
44
+
45
+ async function main() {
46
+ if (!command) {
47
+ console.log('SmartLedger BSV CLI v3.4.0')
48
+ console.log('')
49
+ console.log('Usage:')
50
+ console.log(' smartledger-bsv didweb <subcommand> [options]')
51
+ console.log(' smartledger-bsv vc <subcommand> [options]')
52
+ console.log(' smartledger-bsv status <subcommand> [options]')
53
+ console.log(' smartledger-bsv anchor <subcommand> [options]')
54
+ console.log('')
55
+ console.log('DID:web Commands:')
56
+ console.log(' didweb init --domain <domain> [--alg ES256|ES256K]')
57
+ console.log(' didweb rotate --domain <domain> --key <key-file>')
58
+ console.log('')
59
+ console.log('VC Commands:')
60
+ console.log(' vc issue --issuer <did> --subject <did> --types <types> --claims <json>')
61
+ console.log(' vc verify <jwt-file>')
62
+ console.log('')
63
+ console.log('Status List Commands:')
64
+ console.log(' status create --issuer <did>')
65
+ console.log(' status set --list <file> --index <n> --status <revoked|suspended|valid>')
66
+ console.log(' status check --list <file> --index <n>')
67
+ console.log('')
68
+ console.log('Anchor Commands:')
69
+ console.log(' anchor hash <data-file>')
70
+ console.log(' anchor build --kind <type> --hash <hash> --issuer <did>')
71
+ process.exit(0)
72
+ }
73
+
74
+ var opts = parseArgs(args.slice(2))
75
+
76
+ try {
77
+ if (command === 'didweb') {
78
+ await handleDidWeb(subcommand, opts)
79
+ } else if (command === 'vc') {
80
+ await handleVc(subcommand, opts)
81
+ } else if (command === 'status') {
82
+ await handleStatus(subcommand, opts)
83
+ } else if (command === 'anchor') {
84
+ await handleAnchor(subcommand, opts)
85
+ } else {
86
+ console.error('Unknown command:', command)
87
+ process.exit(1)
88
+ }
89
+ } catch (error) {
90
+ console.error('Error:', error.message)
91
+ process.exit(1)
92
+ }
93
+ }
94
+
95
+ async function handleDidWeb(subcommand, opts) {
96
+ if (subcommand === 'init') {
97
+ if (!opts.domain) {
98
+ console.error('--domain is required')
99
+ process.exit(1)
100
+ }
101
+
102
+ var alg = opts.alg || 'ES256'
103
+ console.error('Generating ' + alg + ' keys for domain: ' + opts.domain)
104
+
105
+ // Generate keys
106
+ var keys = await didweb.generateIssuerKeys({ alg: alg })
107
+
108
+ // Build DID documents
109
+ var docs = didweb.buildDidWebDocuments({
110
+ domain: opts.domain,
111
+ p256: alg === 'ES256' ? { jwk: keys.publicJwk, kid: keys.kid } : undefined,
112
+ k1: alg === 'ES256K' ? { jwk: keys.publicJwk, kid: keys.kid } : undefined,
113
+ controllerName: opts.name || 'SmartLedger Issuer'
114
+ })
115
+
116
+ // Create .well-known directory
117
+ var wellKnownDir = path.join(process.cwd(), '.well-known')
118
+ if (!fs.existsSync(wellKnownDir)) {
119
+ fs.mkdirSync(wellKnownDir, { recursive: true })
120
+ }
121
+
122
+ // Write did.json
123
+ var didPath = path.join(wellKnownDir, 'did.json')
124
+ writeJsonFile(didPath, docs.didDocument)
125
+ console.error('✅ Created:', didPath)
126
+
127
+ // Write jwks.json
128
+ var jwksPath = path.join(wellKnownDir, 'jwks.json')
129
+ writeJsonFile(jwksPath, docs.jwks)
130
+ console.error('✅ Created:', jwksPath)
131
+
132
+ // Write private key to secure file
133
+ var keyPath = path.join(process.cwd(), 'issuer-key-' + keys.kid + '.json')
134
+ writeJsonFile(keyPath, {
135
+ kid: keys.kid,
136
+ alg: keys.alg,
137
+ privateJwk: keys.privateJwk,
138
+ publicJwk: keys.publicJwk,
139
+ did: docs.did
140
+ })
141
+ console.error('✅ Created private key:', keyPath)
142
+ console.error('⚠️ KEEP THIS FILE SECURE AND ENCRYPTED!')
143
+
144
+ console.error('')
145
+ console.error('DID:', docs.did)
146
+ console.error('')
147
+ console.error('Next steps:')
148
+ console.error('1. Host .well-known/did.json and .well-known/jwks.json at https://' + opts.domain)
149
+ console.error('2. Encrypt and securely store ' + keyPath)
150
+ console.error('3. Issue credentials with: smartledger-bsv vc issue ...')
151
+
152
+ } else if (subcommand === 'rotate') {
153
+ console.error('Key rotation coming soon')
154
+ } else {
155
+ console.error('Unknown didweb subcommand:', subcommand)
156
+ process.exit(1)
157
+ }
158
+ }
159
+
160
+ async function handleVc(subcommand, opts) {
161
+ if (subcommand === 'issue') {
162
+ if (!opts.issuer || !opts.subject || !opts.claims) {
163
+ console.error('--issuer, --subject, and --claims are required')
164
+ process.exit(1)
165
+ }
166
+
167
+ // Load issuer key
168
+ var keyFile = opts.key || 'issuer-key.json'
169
+ if (!fs.existsSync(keyFile)) {
170
+ console.error('Issuer key file not found:', keyFile)
171
+ console.error('Use --key to specify the key file')
172
+ process.exit(1)
173
+ }
174
+
175
+ var keyData = readJsonFile(keyFile)
176
+ var claims = JSON.parse(opts.claims)
177
+ var types = opts.types ? opts.types.split(',') : ['VerifiableCredential']
178
+
179
+ console.error('Issuing credential...')
180
+ console.error(' Issuer:', opts.issuer)
181
+ console.error(' Subject:', opts.subject)
182
+ console.error(' Types:', types.join(', '))
183
+
184
+ var result = await vcjwt.issueVcJwt({
185
+ issuerDid: opts.issuer,
186
+ subjectId: opts.subject,
187
+ types: types,
188
+ credentialSubject: claims,
189
+ privateJwk: keyData.privateJwk,
190
+ alg: keyData.alg || 'ES256',
191
+ kid: keyData.kid
192
+ })
193
+
194
+ console.log(result.jwt)
195
+ console.error('✅ Credential issued successfully')
196
+
197
+ } else if (subcommand === 'verify') {
198
+ var jwtFile = opts.jwt || args[2]
199
+ if (!jwtFile) {
200
+ console.error('JWT file required')
201
+ process.exit(1)
202
+ }
203
+
204
+ var jwt = fs.readFileSync(jwtFile, 'utf8').trim()
205
+
206
+ console.error('Verifying credential...')
207
+
208
+ // Simple resolver that reads from .well-known
209
+ var didResolver = async function(did) {
210
+ var domain = did.replace('did:web:', '').replace(/%3A/g, ':')
211
+ var jwksPath = path.join(process.cwd(), '.well-known', 'jwks.json')
212
+
213
+ if (fs.existsSync(jwksPath)) {
214
+ return readJsonFile(jwksPath)
215
+ }
216
+
217
+ throw new Error('Cannot resolve DID: ' + did)
218
+ }
219
+
220
+ var result = await vcjwt.verifyVcJwt(jwt, { didResolver: didResolver })
221
+
222
+ if (result.valid) {
223
+ console.error('✅ Credential is VALID')
224
+ console.log(JSON.stringify(result.payload, null, 2))
225
+ } else {
226
+ console.error('❌ Credential is INVALID')
227
+ console.error('Error:', result.error)
228
+ process.exit(1)
229
+ }
230
+
231
+ } else {
232
+ console.error('Unknown vc subcommand:', subcommand)
233
+ process.exit(1)
234
+ }
235
+ }
236
+
237
+ async function handleStatus(subcommand, opts) {
238
+ if (subcommand === 'create') {
239
+ if (!opts.issuer) {
240
+ console.error('--issuer is required')
241
+ process.exit(1)
242
+ }
243
+
244
+ var keyFile = opts.key || 'issuer-key.json'
245
+ if (!fs.existsSync(keyFile)) {
246
+ console.error('Issuer key file not found:', keyFile)
247
+ process.exit(1)
248
+ }
249
+
250
+ var keyData = readJsonFile(keyFile)
251
+
252
+ console.error('Creating status list...')
253
+
254
+ var result = await statuslist.createStatusList({
255
+ issuerDid: opts.issuer,
256
+ privateJwk: keyData.privateJwk
257
+ })
258
+
259
+ console.log(result.listVcJwt)
260
+ console.error('✅ Status list created')
261
+ console.error('List ID:', result.listId)
262
+
263
+ } else if (subcommand === 'set') {
264
+ if (!opts.list || opts.index === undefined || !opts.status) {
265
+ console.error('--list, --index, and --status are required')
266
+ process.exit(1)
267
+ }
268
+
269
+ var listJwt = fs.readFileSync(opts.list, 'utf8').trim()
270
+ var keyFile = opts.key || 'issuer-key.json'
271
+ var keyData = readJsonFile(keyFile)
272
+
273
+ console.error('Updating status list...')
274
+ console.error(' Index:', opts.index)
275
+ console.error(' Status:', opts.status)
276
+
277
+ var result = await statuslist.updateStatusList({
278
+ listVcJwt: listJwt,
279
+ index: parseInt(opts.index),
280
+ status: opts.status,
281
+ privateJwk: keyData.privateJwk
282
+ })
283
+
284
+ console.log(result.listVcJwt)
285
+ console.error('✅ Status list updated')
286
+
287
+ } else if (subcommand === 'check') {
288
+ if (!opts.list || opts.index === undefined) {
289
+ console.error('--list and --index are required')
290
+ process.exit(1)
291
+ }
292
+
293
+ var listJwt = fs.readFileSync(opts.list, 'utf8').trim()
294
+
295
+ var status = statuslist.getCredentialStatusEntry({
296
+ listVcJwt: listJwt,
297
+ index: parseInt(opts.index)
298
+ })
299
+
300
+ console.log(status)
301
+ console.error('Status at index', opts.index + ':', status)
302
+
303
+ } else {
304
+ console.error('Unknown status subcommand:', subcommand)
305
+ process.exit(1)
306
+ }
307
+ }
308
+
309
+ async function handleAnchor(subcommand, opts) {
310
+ if (subcommand === 'hash') {
311
+ var dataFile = args[2]
312
+ if (!dataFile) {
313
+ console.error('Data file required')
314
+ process.exit(1)
315
+ }
316
+
317
+ var data = fs.readFileSync(dataFile)
318
+ var hash = anchor.sha256Hex(data)
319
+
320
+ console.log(hash)
321
+ console.error('SHA-256 hash:', hash)
322
+
323
+ } else if (subcommand === 'build') {
324
+ if (!opts.kind || !opts.hash || !opts.issuer) {
325
+ console.error('--kind, --hash, and --issuer are required')
326
+ process.exit(1)
327
+ }
328
+
329
+ var payload = anchor.buildAnchorPayload({
330
+ kind: opts.kind,
331
+ hash: opts.hash,
332
+ issuerDid: opts.issuer,
333
+ issuedAt: opts.timestamp
334
+ })
335
+
336
+ console.log(payload.json)
337
+ console.error('✅ Anchor payload created')
338
+ console.error('Size:', payload.json.length, 'bytes')
339
+
340
+ } else {
341
+ console.error('Unknown anchor subcommand:', subcommand)
342
+ process.exit(1)
343
+ }
344
+ }
345
+
346
+ main().catch(function(error) {
347
+ console.error('Fatal error:', error)
348
+ process.exit(1)
349
+ })