@smartledger/bsv 3.3.5 → 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.
- package/README.md +147 -20
- package/anchor-entry.js +1 -0
- package/bin/cli.js +349 -0
- package/bsv-smartcontract.min.js +9 -9
- package/bsv.min.js +8 -8
- package/build/webpack.anchor.config.js +21 -0
- package/build/webpack.didweb.config.js +21 -0
- package/build/webpack.statuslist.config.js +22 -0
- package/build/webpack.vcjwt.config.js +21 -0
- package/demos/browser-test.html +1 -1
- package/didweb-entry.js +1 -0
- package/docs/technical/roadmap.md +3 -3
- package/index.js +28 -0
- package/lib/anchor/index.js +102 -0
- package/lib/didweb/index.js +177 -0
- package/lib/statuslist/index.js +164 -0
- package/lib/vcjwt/index.js +189 -0
- package/package.json +11 -3
- package/statuslist-entry.js +1 -0
- package/vcjwt-entry.js +1 -0
package/README.md
CHANGED
|
@@ -1,47 +1,174 @@
|
|
|
1
1
|
# SmartLedger-BSV
|
|
2
2
|
|
|
3
|
-
**🚀 Complete Bitcoin SV Development Framework with
|
|
3
|
+
**🚀 Complete Bitcoin SV Development Framework with W3C Verifiable Credentials, DID:web, Legal Compliance, and 16 Flexible Loading Options**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@smartledger/bsv)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://bitcoinsv.com/)
|
|
8
8
|
[](#loading-options)
|
|
9
|
+
[](#verifiable-credentials)
|
|
9
10
|
|
|
10
|
-
The most comprehensive and flexible Bitcoin SV library available.
|
|
11
|
+
The most comprehensive and flexible Bitcoin SV library available. **NEW in v3.4.0**: Legally-recognizable DID:web + VC-JWT toolkit with ES256/ES256K support, StatusList2021 revocation, and BSV anchoring. Choose from 16 different distribution methods: standalone modules, complete bundle, or mix-and-match approach.
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## 🆕 **v3.4.0 - Legally-Recognizable Credentials**
|
|
14
|
+
|
|
15
|
+
### **Why This Matters**
|
|
16
|
+
- ✅ **W3C Standards**: Full VC-JWT and DID:web compliance for legal recognition
|
|
17
|
+
- ✅ **Enterprise Ready**: ES256 (P-256 NIST curve) for regulated industries
|
|
18
|
+
- ✅ **Blockchain Native**: ES256K (secp256k1) for BSV integration
|
|
19
|
+
- ✅ **Revocation Built-in**: StatusList2021 standard for credential management
|
|
20
|
+
- ✅ **Privacy Preserving**: Hash-only BSV anchoring (no PII on-chain)
|
|
21
|
+
- ✅ **CLI Tools**: Complete command-line interface for credential operations
|
|
22
|
+
|
|
23
|
+
### **Quick Start - Issue Your First Verifiable Credential**
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install SmartLedger BSV v3.4.0
|
|
27
|
+
npm install @smartledger/bsv@3.4.0
|
|
28
|
+
|
|
29
|
+
# Initialize DID:web issuer (generates ES256 keys)
|
|
30
|
+
npx smartledger-bsv didweb init --domain example.com --alg ES256
|
|
31
|
+
|
|
32
|
+
# Issue a credential
|
|
33
|
+
npx smartledger-bsv vc issue \
|
|
34
|
+
--issuer did:web:example.com \
|
|
35
|
+
--subject did:example:alice \
|
|
36
|
+
--types "VerifiableCredential,DriversLicense" \
|
|
37
|
+
--claims '{"licenseNumber":"DL123456","class":"C"}' \
|
|
38
|
+
> credential.jwt
|
|
39
|
+
|
|
40
|
+
# Verify the credential
|
|
41
|
+
npx smartledger-bsv vc verify credential.jwt
|
|
42
|
+
|
|
43
|
+
# Anchor hash to BSV (privacy-preserving)
|
|
44
|
+
npx smartledger-bsv anchor hash credential.jwt
|
|
45
|
+
|
|
46
|
+
# Create revocation list
|
|
47
|
+
npx smartledger-bsv status create --issuer did:web:example.com > status-list.jwt
|
|
48
|
+
|
|
49
|
+
# Revoke a credential
|
|
50
|
+
npx smartledger-bsv status set --list status-list.jwt --index 42 --status revoked
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### **Programmatic Usage**
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
const bsv = require('@smartledger/bsv')
|
|
57
|
+
|
|
58
|
+
// Generate DID:web issuer keys
|
|
59
|
+
const keys = await bsv.DIDWeb.generateIssuerKeys({ alg: 'ES256' })
|
|
60
|
+
|
|
61
|
+
// Build DID documents (.well-known/did.json and jwks.json)
|
|
62
|
+
const docs = bsv.DIDWeb.buildDidWebDocuments({
|
|
63
|
+
domain: 'example.com',
|
|
64
|
+
p256: { jwk: keys.publicJwk, kid: keys.kid },
|
|
65
|
+
controllerName: 'Example Corp'
|
|
66
|
+
})
|
|
67
|
+
// Deploy docs.didDocument to https://example.com/.well-known/did.json
|
|
68
|
+
// Deploy docs.jwks to https://example.com/.well-known/jwks.json
|
|
69
|
+
|
|
70
|
+
// Issue a Verifiable Credential as JWT
|
|
71
|
+
const result = await bsv.VcJwt.issueVcJwt({
|
|
72
|
+
issuerDid: docs.did,
|
|
73
|
+
subjectId: 'did:example:alice',
|
|
74
|
+
types: ['VerifiableCredential', 'AgeCredential'],
|
|
75
|
+
credentialSubject: {
|
|
76
|
+
ageOver: 18,
|
|
77
|
+
country: 'US'
|
|
78
|
+
},
|
|
79
|
+
privateJwk: keys.privateJwk,
|
|
80
|
+
alg: 'ES256',
|
|
81
|
+
kid: keys.kid
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
console.log('VC-JWT:', result.jwt)
|
|
85
|
+
|
|
86
|
+
// Verify the credential
|
|
87
|
+
const verification = await bsv.VcJwt.verifyVcJwt(result.jwt, {
|
|
88
|
+
didResolver: async (did) => {
|
|
89
|
+
// In production, fetch https://example.com/.well-known/jwks.json
|
|
90
|
+
return { jwks: docs.jwks }
|
|
91
|
+
},
|
|
92
|
+
expectedIssuerDid: docs.did
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
console.log('Valid:', verification.valid)
|
|
96
|
+
|
|
97
|
+
// Anchor hash to BSV (no PII on-chain)
|
|
98
|
+
const hash = bsv.Anchor.sha256Hex(result.jwt)
|
|
99
|
+
const anchorPayload = bsv.Anchor.buildAnchorPayload({
|
|
100
|
+
kind: 'VC_ANCHOR_SHA256',
|
|
101
|
+
hash: hash,
|
|
102
|
+
issuerDid: docs.did
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Include anchorPayload.json in OP_RETURN
|
|
106
|
+
// Later: verify with bsv.Anchor.verifyAnchorHash(originalData, anchorHash)
|
|
107
|
+
|
|
108
|
+
// Create revocation list (100k credentials)
|
|
109
|
+
const statusList = await bsv.StatusList.createStatusList({
|
|
110
|
+
issuerDid: docs.did,
|
|
111
|
+
privateJwk: keys.privateJwk
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Revoke a credential
|
|
115
|
+
const updated = await bsv.StatusList.updateStatusList({
|
|
116
|
+
listVcJwt: statusList.listVcJwt,
|
|
117
|
+
index: 42,
|
|
118
|
+
status: 'revoked',
|
|
119
|
+
privateJwk: keys.privateJwk
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// Check revocation status
|
|
123
|
+
const status = bsv.StatusList.getCredentialStatusEntry({
|
|
124
|
+
listVcJwt: updated.listVcJwt,
|
|
125
|
+
index: 42
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
console.log('Status:', status) // 'revoked'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 🎯 **16 Loading Options - Choose Your Approach**
|
|
13
132
|
|
|
14
133
|
### **Core Modules**
|
|
15
134
|
| Module | Size | Use Case | CDN |
|
|
16
135
|
|--------|------|----------|-----|
|
|
17
|
-
| **bsv.min.js** | 449KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@3.
|
|
18
|
-
| **bsv.bundle.js** | 885KB | Everything in one file | `unpkg.com/@smartledger/bsv@3.
|
|
136
|
+
| **bsv.min.js** | 449KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@3.4.0/bsv.min.js` |
|
|
137
|
+
| **bsv.bundle.js** | 885KB | Everything in one file | `unpkg.com/@smartledger/bsv@3.4.0/bsv.bundle.js` |
|
|
138
|
+
|
|
139
|
+
### **🆕 W3C Verifiable Credentials (v3.4.0)**
|
|
140
|
+
| Module | Size | Use Case | CDN |
|
|
141
|
+
|--------|------|----------|-----|
|
|
142
|
+
| **🟢 bsv-didweb.min.js** | 418KB | **DID:web generation** | `unpkg.com/@smartledger/bsv@3.4.0/bsv-didweb.min.js` |
|
|
143
|
+
| **🟢 bsv-vcjwt.min.js** | 418KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@3.4.0/bsv-vcjwt.min.js` |
|
|
144
|
+
| **🟢 bsv-statuslist.min.js** | 486KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@3.4.0/bsv-statuslist.min.js` |
|
|
145
|
+
| **🟢 bsv-anchor.min.js** | 417KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@3.4.0/bsv-anchor.min.js` |
|
|
19
146
|
|
|
20
147
|
### **Smart Contract & Development**
|
|
21
148
|
| Module | Size | Use Case | CDN |
|
|
22
149
|
|--------|------|----------|-----|
|
|
23
|
-
| **bsv-smartcontract.min.js** | 451KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@3.
|
|
24
|
-
| **bsv-covenant.min.js** | 32KB | Covenant operations | `unpkg.com/@smartledger/bsv@3.
|
|
25
|
-
| **bsv-script-helper.min.js** | 27KB | Custom script tools | `unpkg.com/@smartledger/bsv@3.
|
|
26
|
-
| **bsv-security.min.js** | 290KB | Security enhancements | `unpkg.com/@smartledger/bsv@3.
|
|
150
|
+
| **bsv-smartcontract.min.js** | 451KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@3.4.0/bsv-smartcontract.min.js` |
|
|
151
|
+
| **bsv-covenant.min.js** | 32KB | Covenant operations | `unpkg.com/@smartledger/bsv@3.4.0/bsv-covenant.min.js` |
|
|
152
|
+
| **bsv-script-helper.min.js** | 27KB | Custom script tools | `unpkg.com/@smartledger/bsv@3.4.0/bsv-script-helper.min.js` |
|
|
153
|
+
| **bsv-security.min.js** | 290KB | Security enhancements | `unpkg.com/@smartledger/bsv@3.4.0/bsv-security.min.js` |
|
|
27
154
|
|
|
28
|
-
###
|
|
155
|
+
### **Legal & Compliance**
|
|
29
156
|
| Module | Size | Use Case | CDN |
|
|
30
157
|
|--------|------|----------|-----|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
158
|
+
| **bsv-ltp.min.js** | 817KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@3.4.0/bsv-ltp.min.js` |
|
|
159
|
+
| **bsv-gdaf.min.js** | 604KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@3.4.0/bsv-gdaf.min.js` |
|
|
33
160
|
|
|
34
|
-
###
|
|
161
|
+
### **Advanced Cryptography**
|
|
35
162
|
| Module | Size | Use Case | CDN |
|
|
36
163
|
|--------|------|----------|-----|
|
|
37
|
-
|
|
|
164
|
+
| **bsv-shamir.min.js** | 433KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@3.4.0/bsv-shamir.min.js` |
|
|
38
165
|
|
|
39
166
|
### **Utilities**
|
|
40
167
|
| Module | Size | Use Case | CDN |
|
|
41
168
|
|--------|------|----------|-----|
|
|
42
|
-
| **bsv-ecies.min.js** | 71KB | Encryption | `unpkg.com/@smartledger/bsv@3.
|
|
43
|
-
| **bsv-message.min.js** | 26KB | Message signing | `unpkg.com/@smartledger/bsv@3.
|
|
44
|
-
| **bsv-mnemonic.min.js** | 670KB | HD wallets | `unpkg.com/@smartledger/bsv@3.
|
|
169
|
+
| **bsv-ecies.min.js** | 71KB | Encryption | `unpkg.com/@smartledger/bsv@3.4.0/bsv-ecies.min.js` |
|
|
170
|
+
| **bsv-message.min.js** | 26KB | Message signing | `unpkg.com/@smartledger/bsv@3.4.0/bsv-message.min.js` |
|
|
171
|
+
| **bsv-mnemonic.min.js** | 670KB | HD wallets | `unpkg.com/@smartledger/bsv@3.4.0/bsv-mnemonic.min.js` |
|
|
45
172
|
|
|
46
173
|
## ⚡ **2-Minute Quick Start**
|
|
47
174
|
|
|
@@ -52,10 +179,10 @@ Get started with Bitcoin SV development in under 2 minutes:
|
|
|
52
179
|
npm install @smartledger/bsv
|
|
53
180
|
|
|
54
181
|
# Or include in HTML
|
|
55
|
-
<script src="https://unpkg.com/@smartledger/bsv@3.
|
|
182
|
+
<script src="https://unpkg.com/@smartledger/bsv@3.4.0/bsv.min.js"></script>
|
|
56
183
|
```
|
|
57
184
|
|
|
58
|
-
> **🔧 v3.
|
|
185
|
+
> **🔧 v3.4.0 Update:** Added legally-recognizable W3C Verifiable Credentials with DID:web + VC-JWT toolkit. ES256/ES256K support, StatusList2021 revocation, and privacy-preserving BSV anchoring. Complete CLI tooling included!
|
|
59
186
|
|
|
60
187
|
**Basic Transaction (30 seconds):**
|
|
61
188
|
```javascript
|
package/anchor-entry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/anchor')
|
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
|
+
})
|