smartledger-bsv 3.2.1 → 3.3.1
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 +147 -0
- package/README.md +289 -55
- package/architecture_demo.js +247 -0
- package/bsv-covenant.min.js +10 -0
- package/bsv-gdaf.min.js +37 -0
- package/bsv-ltp.min.js +37 -0
- package/bsv-script-helper.min.js +10 -0
- package/bsv-security.min.js +31 -0
- package/bsv-shamir.min.js +12 -0
- package/bsv-smartcontract.min.js +37 -0
- package/bsv.bundle.js +9 -9
- package/bsv.min.js +3 -3
- package/build/bsv-covenant.min.js +10 -0
- package/build/bsv-script-helper.min.js +10 -0
- package/build/bsv-security.min.js +31 -0
- package/build/bsv-smartcontract.min.js +39 -0
- package/build/bsv.bundle.js +39 -0
- package/build/bsv.min.js +39 -0
- package/build/webpack.bundle.config.js +22 -0
- package/build/webpack.config.js +18 -0
- package/build/webpack.covenant.config.js +27 -0
- package/build/webpack.gdaf.config.js +54 -0
- package/build/webpack.ltp.config.js +17 -0
- package/build/webpack.script-helper.config.js +27 -0
- package/build/webpack.security.config.js +23 -0
- package/build/webpack.smartcontract.config.js +25 -0
- package/build/webpack.subproject.config.js +6 -0
- package/bundle-entry.js +341 -0
- package/complete_ltp_demo.js +511 -0
- package/covenant-entry.js +44 -0
- package/docs/pushtx-key-insights.md +106 -0
- package/gdaf-entry.js +54 -0
- package/index.js +272 -5
- package/lib/crypto/shamir.js +360 -0
- package/lib/gdaf/attestation-signer.js +461 -0
- package/lib/gdaf/attestation-verifier.js +600 -0
- package/lib/gdaf/did-resolver.js +382 -0
- package/lib/gdaf/index.js +471 -0
- package/lib/gdaf/schema-validator.js +682 -0
- package/lib/gdaf/smartledger-anchor.js +486 -0
- package/lib/gdaf/zk-prover.js +507 -0
- package/lib/ltp/anchor.js +438 -0
- package/lib/ltp/claim.js +1026 -0
- package/lib/ltp/index.js +470 -0
- package/lib/ltp/obligation.js +945 -0
- package/lib/ltp/proof.js +828 -0
- package/lib/ltp/registry.js +702 -0
- package/lib/ltp/right.js +765 -0
- package/lib/smart_contract/API_REFERENCE.md +1 -1
- package/lib/smart_contract/EXAMPLES.md +2 -2
- package/lib/smart_contract/QUICK_START.md +2 -2
- package/lib/smart_contract/README.md +1 -1
- package/lib/smart_contract/index.js +4 -0
- package/ltp-entry.js +92 -0
- package/package.json +91 -20
- package/script-helper-entry.js +49 -0
- package/security-entry.js +70 -0
- package/shamir-entry.js +173 -0
- package/shamir_demo.js +121 -0
- package/simple_demo.js +204 -0
- package/smartcontract-entry.js +133 -0
- package/test_shamir.js +221 -0
- package/test_standalone_shamir.html +83 -0
- package/tests/bundle-completeness-test.html +131 -0
- package/tests/bundle-demo.html +476 -0
- package/tests/smartcontract-test.html +239 -0
- package/tests/standalone-modules-test.html +260 -0
- package/tests/test.html +612 -0
- package/tests/unpkg-demo.html +194 -0
- package/docs/nchain.md +0 -958
package/lib/ltp/right.js
ADDED
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var bsv = require('../../')
|
|
4
|
+
var Hash = bsv.crypto.Hash
|
|
5
|
+
var ECDSA = bsv.crypto.ECDSA
|
|
6
|
+
var PrivateKey = bsv.PrivateKey
|
|
7
|
+
var $ = bsv.util.preconditions
|
|
8
|
+
var _ = require('../util/_')
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Legal Token Protocol - Right Creation Primitives
|
|
12
|
+
*
|
|
13
|
+
* Provides primitives for legal rights token creation, validation, and transfer
|
|
14
|
+
* without direct blockchain publishing. External systems handle token storage
|
|
15
|
+
* and blockchain anchoring operations.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Right Token Types
|
|
20
|
+
*/
|
|
21
|
+
var RightTypes = {
|
|
22
|
+
PROPERTY_TITLE: 'PropertyTitle',
|
|
23
|
+
VEHICLE_TITLE: 'VehicleTitle',
|
|
24
|
+
INTELLECTUAL_PROPERTY: 'IntellectualProperty',
|
|
25
|
+
MUSIC_LICENSE: 'MusicLicense',
|
|
26
|
+
SOFTWARE_LICENSE: 'SoftwareLicense',
|
|
27
|
+
FINANCIAL_INSTRUMENT: 'FinancialInstrument',
|
|
28
|
+
PROMISSORY_NOTE: 'PromissoryNote',
|
|
29
|
+
BOND: 'Bond',
|
|
30
|
+
EQUITY_SHARE: 'EquityShare',
|
|
31
|
+
PROFESSIONAL_LICENSE: 'ProfessionalLicense',
|
|
32
|
+
REGULATORY_PERMIT: 'RegulatoryPermit',
|
|
33
|
+
ACCESS_RIGHT: 'AccessRight',
|
|
34
|
+
VOTING_RIGHT: 'VotingRight',
|
|
35
|
+
ROYALTY_RIGHT: 'RoyaltyRight',
|
|
36
|
+
USAGE_RIGHT: 'UsageRight'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Legal Right Token
|
|
41
|
+
*/
|
|
42
|
+
var RightToken = {
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Prepare legal right token for external processing
|
|
46
|
+
* @param {String} type - Right type (from RightTypes)
|
|
47
|
+
* @param {String} issuerDID - Issuer DID
|
|
48
|
+
* @param {String} subjectDID - Subject DID
|
|
49
|
+
* @param {Object} claim - Legal claim object
|
|
50
|
+
* @param {PrivateKey} issuerPrivateKey - Issuer's private key
|
|
51
|
+
* @param {Object} options - Additional options
|
|
52
|
+
* @returns {Object} Prepared legal right token data
|
|
53
|
+
*/
|
|
54
|
+
prepareRightToken: function(type, issuerDID, subjectDID, claim, issuerPrivateKey, options) {
|
|
55
|
+
options = options || {}
|
|
56
|
+
|
|
57
|
+
$.checkArgument(typeof type === 'string', 'Right type must be string')
|
|
58
|
+
$.checkArgument(typeof issuerDID === 'string', 'Issuer DID must be string')
|
|
59
|
+
$.checkArgument(typeof subjectDID === 'string', 'Subject DID must be string')
|
|
60
|
+
$.checkArgument(claim && typeof claim === 'object', 'Claim must be object')
|
|
61
|
+
$.checkArgument(issuerPrivateKey instanceof PrivateKey, 'Invalid issuer private key')
|
|
62
|
+
|
|
63
|
+
// Validate right type
|
|
64
|
+
var validTypes = Object.values(RightTypes)
|
|
65
|
+
$.checkArgument(validTypes.includes(type), 'Invalid right type: ' + type)
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
var rightToken = {
|
|
69
|
+
'@context': [
|
|
70
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
71
|
+
'https://smartledger.technology/contexts/ltp/v1'
|
|
72
|
+
],
|
|
73
|
+
id: 'urn:uuid:' + this._generateUUID(),
|
|
74
|
+
type: ['VerifiableCredential', 'LegalRightToken', type],
|
|
75
|
+
issuer: issuerDID,
|
|
76
|
+
issuanceDate: new Date().toISOString(),
|
|
77
|
+
credentialSubject: {
|
|
78
|
+
id: subjectDID,
|
|
79
|
+
rightType: type,
|
|
80
|
+
claim: claim,
|
|
81
|
+
jurisdiction: options.jurisdiction || 'US',
|
|
82
|
+
purpose: options.purpose || 'legal_right',
|
|
83
|
+
validFrom: options.validFrom || new Date().toISOString(),
|
|
84
|
+
validUntil: options.validUntil || null,
|
|
85
|
+
transferable: options.transferable !== false,
|
|
86
|
+
revocable: options.revocable !== false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add metadata
|
|
91
|
+
if (options.metadata) {
|
|
92
|
+
rightToken.credentialSubject.metadata = options.metadata
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Sign the token
|
|
96
|
+
var signedToken = this._signToken(rightToken, issuerPrivateKey)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
rightToken: signedToken,
|
|
101
|
+
tokenHash: signedToken.tokenHash,
|
|
102
|
+
metadata: {
|
|
103
|
+
type: type,
|
|
104
|
+
issuer: issuerDID,
|
|
105
|
+
subject: subjectDID,
|
|
106
|
+
transferable: rightToken.credentialSubject.transferable,
|
|
107
|
+
revocable: rightToken.credentialSubject.revocable,
|
|
108
|
+
jurisdiction: rightToken.credentialSubject.jurisdiction
|
|
109
|
+
},
|
|
110
|
+
externalOperations: {
|
|
111
|
+
storeToken: {
|
|
112
|
+
endpoint: 'POST /rights/tokens',
|
|
113
|
+
data: {
|
|
114
|
+
token: signedToken,
|
|
115
|
+
metadata: {
|
|
116
|
+
type: type,
|
|
117
|
+
issuer: issuerDID,
|
|
118
|
+
subject: subjectDID,
|
|
119
|
+
transferable: rightToken.credentialSubject.transferable,
|
|
120
|
+
revocable: rightToken.credentialSubject.revocable,
|
|
121
|
+
jurisdiction: rightToken.credentialSubject.jurisdiction
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
indexToken: {
|
|
126
|
+
endpoint: 'POST /rights/index',
|
|
127
|
+
data: {
|
|
128
|
+
tokenId: signedToken.id,
|
|
129
|
+
tokenHash: signedToken.tokenHash,
|
|
130
|
+
type: type,
|
|
131
|
+
issuer: issuerDID,
|
|
132
|
+
subject: subjectDID,
|
|
133
|
+
issuedAt: signedToken.issuanceDate
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
notifyParties: {
|
|
137
|
+
endpoint: 'POST /notifications/right-issued',
|
|
138
|
+
data: {
|
|
139
|
+
tokenId: signedToken.id,
|
|
140
|
+
issuer: issuerDID,
|
|
141
|
+
subject: subjectDID,
|
|
142
|
+
rightType: type
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: error.message
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Prepare right token verification for external processing
|
|
158
|
+
* @param {Object} token - Right token to verify
|
|
159
|
+
* @param {Object} options - Verification options
|
|
160
|
+
* @returns {Object} Prepared verification data
|
|
161
|
+
*/
|
|
162
|
+
prepareRightTokenVerification: function(token, options) {
|
|
163
|
+
options = options || {}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
var errors = []
|
|
167
|
+
var warnings = []
|
|
168
|
+
|
|
169
|
+
// Basic structure validation
|
|
170
|
+
this._validateTokenStructure(token, errors)
|
|
171
|
+
|
|
172
|
+
// Signature verification
|
|
173
|
+
this._verifyTokenSignature(token, errors)
|
|
174
|
+
|
|
175
|
+
// Temporal validation
|
|
176
|
+
this._validateTokenTemporal(token, errors, warnings)
|
|
177
|
+
|
|
178
|
+
// Type validation
|
|
179
|
+
this._validateRightType(token, errors)
|
|
180
|
+
|
|
181
|
+
// Jurisdiction validation
|
|
182
|
+
if (options.requireJurisdiction) {
|
|
183
|
+
this._validateJurisdiction(token, options.allowedJurisdictions, errors)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
var verification = {
|
|
187
|
+
valid: errors.length === 0,
|
|
188
|
+
errors: errors,
|
|
189
|
+
warnings: warnings,
|
|
190
|
+
issuerDID: token.issuer,
|
|
191
|
+
subjectDID: token.credentialSubject ? token.credentialSubject.id : null,
|
|
192
|
+
rightType: token.credentialSubject ? token.credentialSubject.rightType : null,
|
|
193
|
+
transferable: token.credentialSubject ? token.credentialSubject.transferable : null,
|
|
194
|
+
revocable: token.credentialSubject ? token.credentialSubject.revocable : null,
|
|
195
|
+
verifiedAt: new Date().toISOString()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
verification: verification,
|
|
201
|
+
externalOperations: {
|
|
202
|
+
recordVerification: {
|
|
203
|
+
endpoint: 'POST /rights/verification-record',
|
|
204
|
+
data: {
|
|
205
|
+
tokenId: token.id,
|
|
206
|
+
result: verification.valid,
|
|
207
|
+
errors: errors,
|
|
208
|
+
warnings: warnings,
|
|
209
|
+
verifiedAt: verification.verifiedAt
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
auditVerification: {
|
|
213
|
+
endpoint: 'POST /audit/right-verification',
|
|
214
|
+
data: {
|
|
215
|
+
tokenId: token.id,
|
|
216
|
+
rightType: verification.rightType,
|
|
217
|
+
issuer: verification.issuerDID,
|
|
218
|
+
subject: verification.subjectDID,
|
|
219
|
+
result: verification.valid,
|
|
220
|
+
verifiedAt: verification.verifiedAt
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
updateTokenStatus: verification.valid ? {
|
|
224
|
+
endpoint: 'PUT /rights/tokens/' + token.id + '/status',
|
|
225
|
+
data: {
|
|
226
|
+
status: 'VERIFIED',
|
|
227
|
+
verifiedAt: verification.verifiedAt
|
|
228
|
+
}
|
|
229
|
+
} : null
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
error: 'Verification preparation failed: ' + error.message
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Prepare right token transfer for external processing
|
|
243
|
+
* @param {Object} token - Original right token
|
|
244
|
+
* @param {String} newOwnerDID - New owner DID
|
|
245
|
+
* @param {PrivateKey} currentOwnerKey - Current owner's private key
|
|
246
|
+
* @param {Object} options - Transfer options
|
|
247
|
+
* @returns {Object} Prepared transfer data
|
|
248
|
+
*/
|
|
249
|
+
prepareRightTokenTransfer: function(token, newOwnerDID, currentOwnerKey, options) {
|
|
250
|
+
options = options || {}
|
|
251
|
+
|
|
252
|
+
$.checkArgument(token && typeof token === 'object', 'Invalid token')
|
|
253
|
+
$.checkArgument(typeof newOwnerDID === 'string', 'New owner DID must be string')
|
|
254
|
+
$.checkArgument(currentOwnerKey instanceof PrivateKey, 'Invalid current owner key')
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
// Verify current token
|
|
258
|
+
var verification = this.prepareRightTokenVerification(token, {})
|
|
259
|
+
if (!verification.success || !verification.verification.valid) {
|
|
260
|
+
return {
|
|
261
|
+
success: false,
|
|
262
|
+
error: 'Cannot transfer invalid token: ' + verification.verification.errors.join(', ')
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Check transferability
|
|
267
|
+
if (!token.credentialSubject.transferable) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: 'Token is not transferable'
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Create transfer record
|
|
275
|
+
var transfer = {
|
|
276
|
+
'@context': [
|
|
277
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
278
|
+
'https://smartledger.technology/contexts/ltp/v1'
|
|
279
|
+
],
|
|
280
|
+
id: 'urn:uuid:' + this._generateUUID(),
|
|
281
|
+
type: ['VerifiableCredential', 'LegalRightTransfer'],
|
|
282
|
+
issuer: token.credentialSubject.id, // Current owner
|
|
283
|
+
issuanceDate: new Date().toISOString(),
|
|
284
|
+
credentialSubject: {
|
|
285
|
+
id: newOwnerDID,
|
|
286
|
+
originalToken: token.id,
|
|
287
|
+
transferFrom: token.credentialSubject.id,
|
|
288
|
+
transferTo: newOwnerDID,
|
|
289
|
+
transferDate: new Date().toISOString(),
|
|
290
|
+
transferReason: options.reason || 'ownership_transfer',
|
|
291
|
+
consideration: options.consideration || null
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Sign transfer with current owner key
|
|
296
|
+
var signedTransfer = this._signToken(transfer, currentOwnerKey)
|
|
297
|
+
|
|
298
|
+
// Create new token for new owner
|
|
299
|
+
var newToken = JSON.parse(JSON.stringify(token))
|
|
300
|
+
newToken.id = 'urn:uuid:' + this._generateUUID()
|
|
301
|
+
newToken.issuanceDate = new Date().toISOString()
|
|
302
|
+
newToken.credentialSubject.id = newOwnerDID
|
|
303
|
+
newToken.credentialSubject.transferHistory = newToken.credentialSubject.transferHistory || []
|
|
304
|
+
newToken.credentialSubject.transferHistory.push({
|
|
305
|
+
transferId: signedTransfer.id,
|
|
306
|
+
transferDate: signedTransfer.issuanceDate,
|
|
307
|
+
from: token.credentialSubject.id,
|
|
308
|
+
to: newOwnerDID
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
// Re-sign with current owner key (transfer authority)
|
|
312
|
+
delete newToken.proof
|
|
313
|
+
var newSignedToken = this._signToken(newToken, currentOwnerKey)
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
success: true,
|
|
317
|
+
newToken: newSignedToken,
|
|
318
|
+
transferProof: signedTransfer,
|
|
319
|
+
transferId: signedTransfer.id,
|
|
320
|
+
externalOperations: {
|
|
321
|
+
recordTransfer: {
|
|
322
|
+
endpoint: 'POST /rights/transfers',
|
|
323
|
+
data: {
|
|
324
|
+
originalTokenId: token.id,
|
|
325
|
+
newTokenId: newSignedToken.id,
|
|
326
|
+
transferProof: signedTransfer,
|
|
327
|
+
fromOwner: token.credentialSubject.id,
|
|
328
|
+
toOwner: newOwnerDID,
|
|
329
|
+
transferDate: signedTransfer.issuanceDate
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
updateOwnership: {
|
|
333
|
+
endpoint: 'PUT /rights/tokens/' + token.id + '/ownership',
|
|
334
|
+
data: {
|
|
335
|
+
newOwner: newOwnerDID,
|
|
336
|
+
transferId: signedTransfer.id,
|
|
337
|
+
transferDate: signedTransfer.issuanceDate,
|
|
338
|
+
previousOwner: token.credentialSubject.id
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
notifyParties: {
|
|
342
|
+
endpoint: 'POST /notifications/right-transferred',
|
|
343
|
+
data: {
|
|
344
|
+
originalTokenId: token.id,
|
|
345
|
+
newTokenId: newSignedToken.id,
|
|
346
|
+
fromOwner: token.credentialSubject.id,
|
|
347
|
+
toOwner: newOwnerDID,
|
|
348
|
+
rightType: token.credentialSubject.rightType
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
auditTransfer: {
|
|
352
|
+
endpoint: 'POST /audit/right-transfer',
|
|
353
|
+
data: {
|
|
354
|
+
originalTokenId: token.id,
|
|
355
|
+
newTokenId: newSignedToken.id,
|
|
356
|
+
transferId: signedTransfer.id,
|
|
357
|
+
fromOwner: token.credentialSubject.id,
|
|
358
|
+
toOwner: newOwnerDID,
|
|
359
|
+
transferredAt: signedTransfer.issuanceDate
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
} catch (error) {
|
|
366
|
+
return {
|
|
367
|
+
success: false,
|
|
368
|
+
error: error.message
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Prepare obligation token for external processing
|
|
375
|
+
* @param {Object} rightToken - Original right token
|
|
376
|
+
* @param {String} obligorDID - Who has the obligation
|
|
377
|
+
* @param {Object} obligation - Obligation details
|
|
378
|
+
* @param {PrivateKey} issuerKey - Issuer private key
|
|
379
|
+
* @returns {Object} Prepared obligation token data
|
|
380
|
+
*/
|
|
381
|
+
prepareObligationToken: function(rightToken, obligorDID, obligation, issuerKey) {
|
|
382
|
+
$.checkArgument(rightToken && typeof rightToken === 'object', 'Invalid right token')
|
|
383
|
+
$.checkArgument(typeof obligorDID === 'string', 'Obligor DID must be string')
|
|
384
|
+
$.checkArgument(obligation && typeof obligation === 'object', 'Obligation must be object')
|
|
385
|
+
$.checkArgument(issuerKey instanceof PrivateKey, 'Invalid issuer key')
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
var obligationToken = {
|
|
389
|
+
'@context': [
|
|
390
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
391
|
+
'https://smartledger.technology/contexts/ltp/v1'
|
|
392
|
+
],
|
|
393
|
+
id: 'urn:uuid:' + this._generateUUID(),
|
|
394
|
+
type: ['VerifiableCredential', 'LegalObligationToken'],
|
|
395
|
+
issuer: rightToken.issuer,
|
|
396
|
+
issuanceDate: new Date().toISOString(),
|
|
397
|
+
credentialSubject: {
|
|
398
|
+
id: obligorDID,
|
|
399
|
+
obligationType: 'correlative_obligation',
|
|
400
|
+
correlativeRight: rightToken.id,
|
|
401
|
+
rightHolder: rightToken.credentialSubject.id,
|
|
402
|
+
obligation: obligation,
|
|
403
|
+
jurisdiction: rightToken.credentialSubject.jurisdiction,
|
|
404
|
+
purpose: rightToken.credentialSubject.purpose
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
var signedObligation = this._signToken(obligationToken, issuerKey)
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
success: true,
|
|
412
|
+
obligationToken: signedObligation,
|
|
413
|
+
correlativeRight: rightToken.id,
|
|
414
|
+
externalOperations: {
|
|
415
|
+
storeObligation: {
|
|
416
|
+
endpoint: 'POST /obligations/tokens',
|
|
417
|
+
data: {
|
|
418
|
+
obligationToken: signedObligation,
|
|
419
|
+
correlativeRightId: rightToken.id,
|
|
420
|
+
obligor: obligorDID,
|
|
421
|
+
rightHolder: rightToken.credentialSubject.id
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
linkToRight: {
|
|
425
|
+
endpoint: 'POST /rights/tokens/' + rightToken.id + '/obligations',
|
|
426
|
+
data: {
|
|
427
|
+
obligationId: signedObligation.id,
|
|
428
|
+
obligor: obligorDID,
|
|
429
|
+
obligationType: 'correlative_obligation'
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
notifyObligor: {
|
|
433
|
+
endpoint: 'POST /notifications/obligation-created',
|
|
434
|
+
data: {
|
|
435
|
+
obligationId: signedObligation.id,
|
|
436
|
+
obligor: obligorDID,
|
|
437
|
+
rightHolder: rightToken.credentialSubject.id,
|
|
438
|
+
correlativeRight: rightToken.id
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
} catch (error) {
|
|
445
|
+
return {
|
|
446
|
+
success: false,
|
|
447
|
+
error: error.message
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Prepare right type validation for external systems
|
|
454
|
+
* @param {String} type - Right type to validate
|
|
455
|
+
* @returns {Object} Validation data
|
|
456
|
+
*/
|
|
457
|
+
prepareRightTypeValidation: function(type) {
|
|
458
|
+
$.checkArgument(typeof type === 'string', 'Right type must be string')
|
|
459
|
+
|
|
460
|
+
var validTypes = Object.values(RightTypes)
|
|
461
|
+
var isValid = validTypes.includes(type)
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
success: true,
|
|
465
|
+
validation: {
|
|
466
|
+
type: type,
|
|
467
|
+
valid: isValid,
|
|
468
|
+
availableTypes: validTypes,
|
|
469
|
+
category: this._categorizeRightType(type),
|
|
470
|
+
description: this._getRightTypeDescription(type)
|
|
471
|
+
},
|
|
472
|
+
externalOperations: {
|
|
473
|
+
validateType: {
|
|
474
|
+
endpoint: 'POST /rights/validate-type',
|
|
475
|
+
data: {
|
|
476
|
+
type: type,
|
|
477
|
+
valid: isValid
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Categorize right type
|
|
486
|
+
* @private
|
|
487
|
+
*/
|
|
488
|
+
_categorizeRightType: function(type) {
|
|
489
|
+
var categories = {
|
|
490
|
+
'PropertyTitle': 'PROPERTY_RIGHT',
|
|
491
|
+
'VehicleTitle': 'PROPERTY_RIGHT',
|
|
492
|
+
'IntellectualProperty': 'IP_RIGHT',
|
|
493
|
+
'MusicLicense': 'IP_RIGHT',
|
|
494
|
+
'SoftwareLicense': 'IP_RIGHT',
|
|
495
|
+
'FinancialInstrument': 'FINANCIAL_RIGHT',
|
|
496
|
+
'PromissoryNote': 'FINANCIAL_RIGHT',
|
|
497
|
+
'Bond': 'FINANCIAL_RIGHT',
|
|
498
|
+
'EquityShare': 'FINANCIAL_RIGHT',
|
|
499
|
+
'ProfessionalLicense': 'LICENSE_RIGHT',
|
|
500
|
+
'RegulatoryPermit': 'LICENSE_RIGHT',
|
|
501
|
+
'AccessRight': 'ACCESS_RIGHT',
|
|
502
|
+
'VotingRight': 'GOVERNANCE_RIGHT',
|
|
503
|
+
'RoyaltyRight': 'IP_RIGHT',
|
|
504
|
+
'UsageRight': 'ACCESS_RIGHT'
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return categories[type] || 'UNKNOWN'
|
|
508
|
+
},
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Get right type description
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
_getRightTypeDescription: function(type) {
|
|
515
|
+
var descriptions = {
|
|
516
|
+
'PropertyTitle': 'Legal ownership of real estate property',
|
|
517
|
+
'VehicleTitle': 'Legal ownership of motor vehicle',
|
|
518
|
+
'IntellectualProperty': 'Rights to intellectual property assets',
|
|
519
|
+
'MusicLicense': 'License for music performance and usage',
|
|
520
|
+
'SoftwareLicense': 'License for software usage and distribution',
|
|
521
|
+
'FinancialInstrument': 'Rights related to financial instruments',
|
|
522
|
+
'PromissoryNote': 'Promise to pay a specified amount',
|
|
523
|
+
'Bond': 'Debt security representing loan',
|
|
524
|
+
'EquityShare': 'Ownership stake in company',
|
|
525
|
+
'ProfessionalLicense': 'License for professional practice',
|
|
526
|
+
'RegulatoryPermit': 'Permit for regulated activities',
|
|
527
|
+
'AccessRight': 'Right to access resources or locations',
|
|
528
|
+
'VotingRight': 'Right to participate in governance',
|
|
529
|
+
'RoyaltyRight': 'Right to receive royalty payments',
|
|
530
|
+
'UsageRight': 'Right to use specific assets or services'
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return descriptions[type] || 'Unknown right type'
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Get available right types
|
|
538
|
+
* @returns {Object} Right types
|
|
539
|
+
*/
|
|
540
|
+
getRightTypes: function() {
|
|
541
|
+
return RightTypes
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Sign a token with private key
|
|
546
|
+
* @private
|
|
547
|
+
*/
|
|
548
|
+
_signToken: function(token, privateKey) {
|
|
549
|
+
// Create canonical hash
|
|
550
|
+
var canonicalToken = this._canonicalizeToken(token)
|
|
551
|
+
var tokenHash = Hash.sha256(Buffer.from(canonicalToken))
|
|
552
|
+
|
|
553
|
+
// Sign with ECDSA
|
|
554
|
+
var ecdsa = new ECDSA()
|
|
555
|
+
ecdsa.hashbuf = tokenHash
|
|
556
|
+
ecdsa.privkey = privateKey
|
|
557
|
+
ecdsa.pubkey = privateKey.toPublicKey()
|
|
558
|
+
|
|
559
|
+
ecdsa.sign()
|
|
560
|
+
var signature = ecdsa.sig
|
|
561
|
+
|
|
562
|
+
// Create proof
|
|
563
|
+
var proof = {
|
|
564
|
+
type: 'EcdsaSecp256k1Signature2019',
|
|
565
|
+
created: new Date().toISOString(),
|
|
566
|
+
verificationMethod: 'did:smartledger:' + privateKey.toPublicKey().toString() + '#keys-1',
|
|
567
|
+
proofPurpose: 'assertionMethod',
|
|
568
|
+
jws: this._createJWS(tokenHash, signature)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Add proof to token
|
|
572
|
+
var signedToken = JSON.parse(JSON.stringify(token))
|
|
573
|
+
signedToken.proof = proof
|
|
574
|
+
signedToken.tokenHash = tokenHash.toString('hex')
|
|
575
|
+
|
|
576
|
+
return signedToken
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Sign a token with private key
|
|
581
|
+
* @private
|
|
582
|
+
*/
|
|
583
|
+
_signToken: function(token, privateKey) {
|
|
584
|
+
var header = {
|
|
585
|
+
alg: 'ES256K',
|
|
586
|
+
typ: 'JWT'
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
var headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url')
|
|
590
|
+
var payloadB64 = hash.toString('base64url')
|
|
591
|
+
var signatureB64 = signature.toDER().toString('base64url')
|
|
592
|
+
|
|
593
|
+
return headerB64 + '..' + signatureB64
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Canonicalize token for hashing
|
|
598
|
+
* @private
|
|
599
|
+
*/
|
|
600
|
+
_canonicalizeToken: function(token) {
|
|
601
|
+
// Remove proof and hash fields
|
|
602
|
+
var canonical = JSON.parse(JSON.stringify(token))
|
|
603
|
+
delete canonical.proof
|
|
604
|
+
delete canonical.tokenHash
|
|
605
|
+
|
|
606
|
+
// Sort keys recursively
|
|
607
|
+
return JSON.stringify(this._sortObjectKeys(canonical))
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Sort object keys recursively
|
|
612
|
+
* @private
|
|
613
|
+
*/
|
|
614
|
+
_sortObjectKeys: function(obj) {
|
|
615
|
+
if (Array.isArray(obj)) {
|
|
616
|
+
return obj.map(this._sortObjectKeys.bind(this))
|
|
617
|
+
} else if (obj !== null && typeof obj === 'object') {
|
|
618
|
+
var sorted = {}
|
|
619
|
+
Object.keys(obj).sort().forEach(function(key) {
|
|
620
|
+
sorted[key] = this._sortObjectKeys(obj[key])
|
|
621
|
+
}.bind(this))
|
|
622
|
+
return sorted
|
|
623
|
+
}
|
|
624
|
+
return obj
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Validate token structure
|
|
629
|
+
* @private
|
|
630
|
+
*/
|
|
631
|
+
_validateTokenStructure: function(token, errors) {
|
|
632
|
+
if (!token['@context']) {
|
|
633
|
+
errors.push('Missing @context')
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!token.id) {
|
|
637
|
+
errors.push('Missing id')
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (!token.type || !Array.isArray(token.type)) {
|
|
641
|
+
errors.push('Missing or invalid type')
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (!token.issuer) {
|
|
645
|
+
errors.push('Missing issuer')
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (!token.issuanceDate) {
|
|
649
|
+
errors.push('Missing issuanceDate')
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (!token.credentialSubject) {
|
|
653
|
+
errors.push('Missing credentialSubject')
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (!token.proof) {
|
|
657
|
+
errors.push('Missing proof')
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Verify token signature
|
|
663
|
+
* @private
|
|
664
|
+
*/
|
|
665
|
+
_verifyTokenSignature: function(token, errors) {
|
|
666
|
+
try {
|
|
667
|
+
if (!token.proof || !token.proof.jws) {
|
|
668
|
+
errors.push('Missing signature proof')
|
|
669
|
+
return
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Recreate canonical hash
|
|
673
|
+
var canonicalToken = this._canonicalizeToken(token)
|
|
674
|
+
var expectedHash = Hash.sha256(Buffer.from(canonicalToken))
|
|
675
|
+
|
|
676
|
+
// Compare with stored hash if available
|
|
677
|
+
if (token.tokenHash && token.tokenHash !== expectedHash.toString('hex')) {
|
|
678
|
+
errors.push('Token hash mismatch')
|
|
679
|
+
return
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// TODO: Verify JWS signature with issuer's public key
|
|
683
|
+
// This would require DID resolution to get the public key
|
|
684
|
+
|
|
685
|
+
} catch (error) {
|
|
686
|
+
errors.push('Signature verification failed: ' + error.message)
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Validate temporal aspects
|
|
692
|
+
* @private
|
|
693
|
+
*/
|
|
694
|
+
_validateTokenTemporal: function(token, errors, warnings) {
|
|
695
|
+
var now = new Date()
|
|
696
|
+
var issuanceDate = new Date(token.issuanceDate)
|
|
697
|
+
|
|
698
|
+
if (issuanceDate > now) {
|
|
699
|
+
errors.push('Token issued in the future')
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (token.credentialSubject.validFrom) {
|
|
703
|
+
var validFrom = new Date(token.credentialSubject.validFrom)
|
|
704
|
+
if (now < validFrom) {
|
|
705
|
+
warnings.push('Token not yet valid')
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (token.credentialSubject.validUntil) {
|
|
710
|
+
var validUntil = new Date(token.credentialSubject.validUntil)
|
|
711
|
+
if (now > validUntil) {
|
|
712
|
+
errors.push('Token expired')
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Validate right type
|
|
719
|
+
* @private
|
|
720
|
+
*/
|
|
721
|
+
_validateRightType: function(token, errors) {
|
|
722
|
+
if (!token.credentialSubject.rightType) {
|
|
723
|
+
errors.push('Missing rightType')
|
|
724
|
+
return
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
var validTypes = Object.values(RightTypes)
|
|
728
|
+
if (!validTypes.includes(token.credentialSubject.rightType)) {
|
|
729
|
+
errors.push('Invalid right type: ' + token.credentialSubject.rightType)
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Validate jurisdiction
|
|
735
|
+
* @private
|
|
736
|
+
*/
|
|
737
|
+
_validateJurisdiction: function(token, allowedJurisdictions, errors) {
|
|
738
|
+
if (!token.credentialSubject.jurisdiction) {
|
|
739
|
+
errors.push('Missing jurisdiction')
|
|
740
|
+
return
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (allowedJurisdictions && Array.isArray(allowedJurisdictions)) {
|
|
744
|
+
if (!allowedJurisdictions.includes(token.credentialSubject.jurisdiction)) {
|
|
745
|
+
errors.push('Jurisdiction not allowed: ' + token.credentialSubject.jurisdiction)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Generate UUID
|
|
752
|
+
* @private
|
|
753
|
+
*/
|
|
754
|
+
_generateUUID: function() {
|
|
755
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
756
|
+
var r = Math.random() * 16 | 0
|
|
757
|
+
var v = c === 'x' ? r : (r & 0x3 | 0x8)
|
|
758
|
+
return v.toString(16)
|
|
759
|
+
})
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Export both the object and static methods for compatibility
|
|
764
|
+
RightToken.RightTypes = RightTypes
|
|
765
|
+
module.exports = RightToken
|