smartledger-bsv 3.2.2 → 3.3.2
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/architecture_demo.js +247 -0
- package/bsv-gdaf.min.js +37 -0
- package/bsv-ltp.min.js +37 -0
- package/bsv-shamir.min.js +12 -0
- package/bsv.bundle.js +9 -9
- package/build/bsv-smartcontract.min.js +10 -8
- package/build/bsv.bundle.js +9 -9
- package/build/bsv.min.js +10 -8
- package/build/webpack.gdaf.config.js +54 -0
- package/build/webpack.ltp.config.js +17 -0
- package/bundle-entry.js +77 -1
- package/complete_ltp_demo.js +511 -0
- package/gdaf-entry.js +54 -0
- package/index.js +259 -0
- 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/ltp-entry.js +92 -0
- package/package.json +44 -4
- package/shamir-entry.js +173 -0
- package/shamir_demo.js +121 -0
- package/simple_demo.js +204 -0
- package/test_shamir.js +221 -0
- package/test_standalone_shamir.html +83 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var bsv = require('../../')
|
|
4
|
+
var Hash = bsv.crypto.Hash
|
|
5
|
+
var Address = bsv.Address
|
|
6
|
+
var Transaction = bsv.Transaction
|
|
7
|
+
var $ = bsv.util.preconditions
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Legal Token Protocol - Registry Primitives
|
|
11
|
+
*
|
|
12
|
+
* Provides primitives for token registry management, revocation processing,
|
|
13
|
+
* and discovery mechanisms without direct blockchain publishing.
|
|
14
|
+
* External systems handle actual registry storage and blockchain anchoring.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
var LTPRegistry = {
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Prepare registry data structure for external storage
|
|
21
|
+
* @param {Object} config - Registry configuration
|
|
22
|
+
* @returns {Object} Prepared registry data
|
|
23
|
+
*/
|
|
24
|
+
prepareRegistry: function(config) {
|
|
25
|
+
config = config || {}
|
|
26
|
+
|
|
27
|
+
$.checkArgument(typeof config === 'object', 'Config must be object')
|
|
28
|
+
|
|
29
|
+
var registry = {
|
|
30
|
+
id: config.id || this._generateRegistryId(),
|
|
31
|
+
name: config.name || 'Legal Token Registry',
|
|
32
|
+
jurisdiction: config.jurisdiction || 'GLOBAL',
|
|
33
|
+
authority: config.authority || null,
|
|
34
|
+
created: new Date().toISOString(),
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
|
|
37
|
+
// Registry configuration
|
|
38
|
+
config: {
|
|
39
|
+
allowPublicRegistration: config.allowPublicRegistration || false,
|
|
40
|
+
requireApproval: config.requireApproval || true,
|
|
41
|
+
enableRevocation: config.enableRevocation !== false,
|
|
42
|
+
enableAuditTrail: config.enableAuditTrail !== false,
|
|
43
|
+
retentionPeriod: config.retentionPeriod || '7Y', // 7 years
|
|
44
|
+
complianceLevel: config.complianceLevel || 'STANDARD'
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Data structure templates
|
|
48
|
+
dataStructure: {
|
|
49
|
+
tokens: 'Map<tokenId, registrationRecord>',
|
|
50
|
+
revocations: 'Map<tokenId, revocationRecord>',
|
|
51
|
+
auditLog: 'Array<auditEntry>',
|
|
52
|
+
statistics: {
|
|
53
|
+
totalTokens: 0,
|
|
54
|
+
activeTokens: 0,
|
|
55
|
+
revokedTokens: 0,
|
|
56
|
+
registrations: 0
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// External integration points
|
|
61
|
+
externalStorage: {
|
|
62
|
+
required: true,
|
|
63
|
+
type: 'database_or_blockchain',
|
|
64
|
+
endpoints: {
|
|
65
|
+
store: 'POST /registry/{id}/tokens',
|
|
66
|
+
retrieve: 'GET /registry/{id}/tokens/{tokenId}',
|
|
67
|
+
search: 'GET /registry/{id}/search',
|
|
68
|
+
revoke: 'POST /registry/{id}/revocations'
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add initial audit entry template
|
|
74
|
+
var initialAudit = this._prepareAuditEntry({
|
|
75
|
+
action: 'REGISTRY_CREATED',
|
|
76
|
+
actor: config.authority,
|
|
77
|
+
timestamp: registry.created,
|
|
78
|
+
metadata: {
|
|
79
|
+
jurisdiction: registry.jurisdiction,
|
|
80
|
+
config: registry.config
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
registry: registry,
|
|
87
|
+
initialAudit: initialAudit,
|
|
88
|
+
instructions: {
|
|
89
|
+
step1: 'Store registry configuration in external system',
|
|
90
|
+
step2: 'Initialize token storage with provided data structure',
|
|
91
|
+
step3: 'Set up audit logging with provided templates',
|
|
92
|
+
step4: 'Configure external endpoints for registry operations'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Prepare token registration data for external registry
|
|
99
|
+
* @param {Object} token - Token to register
|
|
100
|
+
* @param {Object} registryConfig - Registry configuration
|
|
101
|
+
* @param {Object} options - Registration options
|
|
102
|
+
* @returns {Object} Prepared registration data
|
|
103
|
+
*/
|
|
104
|
+
prepareTokenRegistration: function(token, registryConfig, options) {
|
|
105
|
+
options = options || {}
|
|
106
|
+
|
|
107
|
+
$.checkArgument(token && typeof token === 'object', 'Invalid token')
|
|
108
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
// Validate token
|
|
112
|
+
var validation = this._validateTokenForRegistration(token, registryConfig)
|
|
113
|
+
if (!validation.valid) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: 'Token validation failed: ' + validation.error
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Create registration record template
|
|
121
|
+
var registrationRecord = {
|
|
122
|
+
tokenId: token.id,
|
|
123
|
+
tokenHash: this._hashToken(token),
|
|
124
|
+
token: token,
|
|
125
|
+
status: registryConfig.requireApproval ? 'PENDING' : 'ACTIVE',
|
|
126
|
+
registeredBy: options.registeredBy || 'UNKNOWN',
|
|
127
|
+
registeredAt: new Date().toISOString(),
|
|
128
|
+
approvedBy: null,
|
|
129
|
+
approvedAt: null,
|
|
130
|
+
metadata: options.metadata || {},
|
|
131
|
+
compliance: {
|
|
132
|
+
jurisdiction: token.jurisdiction || registryConfig.jurisdiction,
|
|
133
|
+
level: this._calculateComplianceLevel(token),
|
|
134
|
+
checks: validation.checks || []
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Prepare audit entry
|
|
139
|
+
var auditEntry = this._prepareAuditEntry({
|
|
140
|
+
action: 'TOKEN_REGISTERED',
|
|
141
|
+
actor: options.registeredBy,
|
|
142
|
+
tokenId: token.id,
|
|
143
|
+
timestamp: registrationRecord.registeredAt,
|
|
144
|
+
metadata: {
|
|
145
|
+
status: registrationRecord.status,
|
|
146
|
+
tokenType: token.type,
|
|
147
|
+
compliance: registrationRecord.compliance
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
registrationRecord: registrationRecord,
|
|
154
|
+
auditEntry: auditEntry,
|
|
155
|
+
registrationId: this._generateRegistrationId(token.id),
|
|
156
|
+
validation: validation,
|
|
157
|
+
externalOperations: {
|
|
158
|
+
store: {
|
|
159
|
+
endpoint: 'POST /registry/' + registryConfig.id + '/tokens',
|
|
160
|
+
data: registrationRecord
|
|
161
|
+
},
|
|
162
|
+
audit: {
|
|
163
|
+
endpoint: 'POST /registry/' + registryConfig.id + '/audit',
|
|
164
|
+
data: auditEntry
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
error: error.message
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Prepare token approval data for external registry
|
|
179
|
+
* @param {String} tokenId - Token ID to approve
|
|
180
|
+
* @param {String} approver - Approver identity
|
|
181
|
+
* @param {Object} registryConfig - Registry configuration
|
|
182
|
+
* @returns {Object} Prepared approval data
|
|
183
|
+
*/
|
|
184
|
+
prepareTokenApproval: function(tokenId, approver, registryConfig) {
|
|
185
|
+
try {
|
|
186
|
+
$.checkArgument(typeof tokenId === 'string', 'Token ID must be string')
|
|
187
|
+
$.checkArgument(typeof approver === 'string', 'Approver must be string')
|
|
188
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
189
|
+
|
|
190
|
+
var approvalData = {
|
|
191
|
+
tokenId: tokenId,
|
|
192
|
+
status: 'ACTIVE',
|
|
193
|
+
approvedBy: approver,
|
|
194
|
+
approvedAt: new Date().toISOString()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Prepare audit entry
|
|
198
|
+
var auditEntry = this._prepareAuditEntry({
|
|
199
|
+
action: 'TOKEN_APPROVED',
|
|
200
|
+
actor: approver,
|
|
201
|
+
tokenId: tokenId,
|
|
202
|
+
timestamp: approvalData.approvedAt
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
success: true,
|
|
207
|
+
approvalData: approvalData,
|
|
208
|
+
auditEntry: auditEntry,
|
|
209
|
+
externalOperations: {
|
|
210
|
+
update: {
|
|
211
|
+
endpoint: 'PUT /registry/' + registryConfig.id + '/tokens/' + tokenId + '/status',
|
|
212
|
+
data: approvalData
|
|
213
|
+
},
|
|
214
|
+
audit: {
|
|
215
|
+
endpoint: 'POST /registry/' + registryConfig.id + '/audit',
|
|
216
|
+
data: auditEntry
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: error.message
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Prepare token revocation data for external registry
|
|
231
|
+
* @param {String} tokenId - Token ID to revoke
|
|
232
|
+
* @param {Object} revocation - Revocation details
|
|
233
|
+
* @param {Object} registryConfig - Registry configuration
|
|
234
|
+
* @returns {Object} Prepared revocation data
|
|
235
|
+
*/
|
|
236
|
+
prepareTokenRevocation: function(tokenId, revocation, registryConfig) {
|
|
237
|
+
try {
|
|
238
|
+
$.checkArgument(typeof tokenId === 'string', 'Token ID must be string')
|
|
239
|
+
$.checkArgument(revocation && typeof revocation === 'object', 'Invalid revocation')
|
|
240
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
241
|
+
|
|
242
|
+
if (!registryConfig.enableRevocation) {
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
error: 'Revocation is disabled for this registry'
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Create revocation record
|
|
250
|
+
var revocationRecord = {
|
|
251
|
+
tokenId: tokenId,
|
|
252
|
+
reason: revocation.reason || 'UNSPECIFIED',
|
|
253
|
+
revokedBy: revocation.revokedBy || 'UNKNOWN',
|
|
254
|
+
revokedAt: new Date().toISOString(),
|
|
255
|
+
effectiveDate: revocation.effectiveDate || new Date().toISOString(),
|
|
256
|
+
legalBasis: revocation.legalBasis || null,
|
|
257
|
+
evidence: revocation.evidence || null,
|
|
258
|
+
revocationHash: this._generateRevocationHash(tokenId, revocation)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Prepare token status update
|
|
262
|
+
var statusUpdate = {
|
|
263
|
+
tokenId: tokenId,
|
|
264
|
+
status: 'REVOKED',
|
|
265
|
+
revokedAt: revocationRecord.revokedAt,
|
|
266
|
+
revocationReason: revocationRecord.reason
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Prepare audit entry
|
|
270
|
+
var auditEntry = this._prepareAuditEntry({
|
|
271
|
+
action: 'TOKEN_REVOKED',
|
|
272
|
+
actor: revocation.revokedBy,
|
|
273
|
+
tokenId: tokenId,
|
|
274
|
+
timestamp: revocationRecord.revokedAt,
|
|
275
|
+
metadata: {
|
|
276
|
+
reason: revocationRecord.reason,
|
|
277
|
+
legalBasis: revocationRecord.legalBasis
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
success: true,
|
|
283
|
+
revocationRecord: revocationRecord,
|
|
284
|
+
statusUpdate: statusUpdate,
|
|
285
|
+
auditEntry: auditEntry,
|
|
286
|
+
revocationId: this._generateRevocationId(tokenId),
|
|
287
|
+
externalOperations: {
|
|
288
|
+
storeRevocation: {
|
|
289
|
+
endpoint: 'POST /registry/' + registryConfig.id + '/revocations',
|
|
290
|
+
data: revocationRecord
|
|
291
|
+
},
|
|
292
|
+
updateStatus: {
|
|
293
|
+
endpoint: 'PUT /registry/' + registryConfig.id + '/tokens/' + tokenId + '/status',
|
|
294
|
+
data: statusUpdate
|
|
295
|
+
},
|
|
296
|
+
audit: {
|
|
297
|
+
endpoint: 'POST /registry/' + registryConfig.id + '/audit',
|
|
298
|
+
data: auditEntry
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: error.message
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Prepare token status query for external registry
|
|
313
|
+
* @param {String} tokenId - Token ID to check
|
|
314
|
+
* @param {Object} registryConfig - Registry configuration
|
|
315
|
+
* @returns {Object} Prepared status query
|
|
316
|
+
*/
|
|
317
|
+
prepareTokenStatusQuery: function(tokenId, registryConfig) {
|
|
318
|
+
try {
|
|
319
|
+
$.checkArgument(typeof tokenId === 'string', 'Token ID must be string')
|
|
320
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
success: true,
|
|
324
|
+
query: {
|
|
325
|
+
tokenId: tokenId,
|
|
326
|
+
registryId: registryConfig.id
|
|
327
|
+
},
|
|
328
|
+
externalOperations: {
|
|
329
|
+
retrieve: {
|
|
330
|
+
endpoint: 'GET /registry/' + registryConfig.id + '/tokens/' + tokenId,
|
|
331
|
+
method: 'GET'
|
|
332
|
+
},
|
|
333
|
+
checkRevocation: {
|
|
334
|
+
endpoint: 'GET /registry/' + registryConfig.id + '/revocations/' + tokenId,
|
|
335
|
+
method: 'GET'
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
expectedResponse: {
|
|
339
|
+
found: 'boolean',
|
|
340
|
+
status: 'string (PENDING|ACTIVE|REVOKED)',
|
|
341
|
+
registeredAt: 'ISO datetime',
|
|
342
|
+
registeredBy: 'string',
|
|
343
|
+
approvedAt: 'ISO datetime or null',
|
|
344
|
+
approvedBy: 'string or null',
|
|
345
|
+
revokedAt: 'ISO datetime or null',
|
|
346
|
+
revocationReason: 'string or null',
|
|
347
|
+
compliance: 'object',
|
|
348
|
+
revocation: 'object or null'
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
} catch (error) {
|
|
353
|
+
return {
|
|
354
|
+
success: false,
|
|
355
|
+
error: error.message
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Prepare token search query for external registry
|
|
362
|
+
* @param {Object} criteria - Search criteria
|
|
363
|
+
* @param {Object} registryConfig - Registry configuration
|
|
364
|
+
* @returns {Object} Prepared search query
|
|
365
|
+
*/
|
|
366
|
+
prepareTokenSearch: function(criteria, registryConfig) {
|
|
367
|
+
criteria = criteria || {}
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
371
|
+
|
|
372
|
+
// Build query parameters
|
|
373
|
+
var queryParams = []
|
|
374
|
+
|
|
375
|
+
if (criteria.status) {
|
|
376
|
+
queryParams.push('status=' + encodeURIComponent(criteria.status))
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (criteria.type) {
|
|
380
|
+
queryParams.push('type=' + encodeURIComponent(criteria.type))
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (criteria.registeredBy) {
|
|
384
|
+
queryParams.push('registeredBy=' + encodeURIComponent(criteria.registeredBy))
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (criteria.jurisdiction) {
|
|
388
|
+
queryParams.push('jurisdiction=' + encodeURIComponent(criteria.jurisdiction))
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (criteria.dateFrom) {
|
|
392
|
+
queryParams.push('dateFrom=' + encodeURIComponent(criteria.dateFrom))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (criteria.dateTo) {
|
|
396
|
+
queryParams.push('dateTo=' + encodeURIComponent(criteria.dateTo))
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (criteria.includeTokens) {
|
|
400
|
+
queryParams.push('includeTokens=true')
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (criteria.sortBy) {
|
|
404
|
+
queryParams.push('sortBy=' + encodeURIComponent(criteria.sortBy))
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (criteria.sortOrder) {
|
|
408
|
+
queryParams.push('sortOrder=' + encodeURIComponent(criteria.sortOrder))
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
var offset = criteria.offset || 0
|
|
412
|
+
var limit = criteria.limit || 100
|
|
413
|
+
queryParams.push('offset=' + offset)
|
|
414
|
+
queryParams.push('limit=' + limit)
|
|
415
|
+
|
|
416
|
+
var queryString = queryParams.length > 0 ? '?' + queryParams.join('&') : ''
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
success: true,
|
|
420
|
+
searchCriteria: criteria,
|
|
421
|
+
externalOperations: {
|
|
422
|
+
search: {
|
|
423
|
+
endpoint: 'GET /registry/' + registryConfig.id + '/search' + queryString,
|
|
424
|
+
method: 'GET'
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
expectedResponse: {
|
|
428
|
+
success: 'boolean',
|
|
429
|
+
results: 'Array<tokenRecord>',
|
|
430
|
+
totalCount: 'number',
|
|
431
|
+
totalRegistrations: 'number',
|
|
432
|
+
offset: 'number',
|
|
433
|
+
limit: 'number',
|
|
434
|
+
hasMore: 'boolean'
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
} catch (error) {
|
|
439
|
+
return {
|
|
440
|
+
success: false,
|
|
441
|
+
error: error.message
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Prepare registry statistics query for external registry
|
|
448
|
+
* @param {Object} registryConfig - Registry configuration
|
|
449
|
+
* @returns {Object} Prepared statistics query
|
|
450
|
+
*/
|
|
451
|
+
prepareStatisticsQuery: function(registryConfig) {
|
|
452
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
success: true,
|
|
456
|
+
query: {
|
|
457
|
+
registryId: registryConfig.id
|
|
458
|
+
},
|
|
459
|
+
externalOperations: {
|
|
460
|
+
getStatistics: {
|
|
461
|
+
endpoint: 'GET /registry/' + registryConfig.id + '/statistics',
|
|
462
|
+
method: 'GET'
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
expectedResponse: {
|
|
466
|
+
id: 'string',
|
|
467
|
+
name: 'string',
|
|
468
|
+
jurisdiction: 'string',
|
|
469
|
+
created: 'ISO datetime',
|
|
470
|
+
statistics: {
|
|
471
|
+
totalTokens: 'number',
|
|
472
|
+
activeTokens: 'number',
|
|
473
|
+
revokedTokens: 'number',
|
|
474
|
+
registrations: 'number'
|
|
475
|
+
},
|
|
476
|
+
auditEntries: 'number',
|
|
477
|
+
lastActivity: 'ISO datetime'
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Prepare audit log query for external registry
|
|
484
|
+
* @param {Object} registryConfig - Registry configuration
|
|
485
|
+
* @param {Object} options - Log options
|
|
486
|
+
* @returns {Object} Prepared audit log query
|
|
487
|
+
*/
|
|
488
|
+
prepareAuditLogQuery: function(registryConfig, options) {
|
|
489
|
+
options = options || {}
|
|
490
|
+
|
|
491
|
+
$.checkArgument(registryConfig && typeof registryConfig === 'object', 'Invalid registry config')
|
|
492
|
+
|
|
493
|
+
if (!registryConfig.enableAuditTrail) {
|
|
494
|
+
return {
|
|
495
|
+
success: false,
|
|
496
|
+
error: 'Audit trail is disabled'
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Build query parameters
|
|
501
|
+
var queryParams = []
|
|
502
|
+
|
|
503
|
+
if (options.action) {
|
|
504
|
+
queryParams.push('action=' + encodeURIComponent(options.action))
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (options.actor) {
|
|
508
|
+
queryParams.push('actor=' + encodeURIComponent(options.actor))
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (options.tokenId) {
|
|
512
|
+
queryParams.push('tokenId=' + encodeURIComponent(options.tokenId))
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (options.dateFrom) {
|
|
516
|
+
queryParams.push('dateFrom=' + encodeURIComponent(options.dateFrom))
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (options.dateTo) {
|
|
520
|
+
queryParams.push('dateTo=' + encodeURIComponent(options.dateTo))
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
var offset = options.offset || 0
|
|
524
|
+
var limit = options.limit || 100
|
|
525
|
+
queryParams.push('offset=' + offset)
|
|
526
|
+
queryParams.push('limit=' + limit)
|
|
527
|
+
|
|
528
|
+
var queryString = queryParams.length > 0 ? '?' + queryParams.join('&') : ''
|
|
529
|
+
|
|
530
|
+
return {
|
|
531
|
+
success: true,
|
|
532
|
+
auditOptions: options,
|
|
533
|
+
externalOperations: {
|
|
534
|
+
getAuditLog: {
|
|
535
|
+
endpoint: 'GET /registry/' + registryConfig.id + '/audit' + queryString,
|
|
536
|
+
method: 'GET'
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
expectedResponse: {
|
|
540
|
+
success: 'boolean',
|
|
541
|
+
entries: 'Array<auditEntry>',
|
|
542
|
+
totalEntries: 'number',
|
|
543
|
+
offset: 'number',
|
|
544
|
+
limit: 'number',
|
|
545
|
+
hasMore: 'boolean'
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Generate registry ID
|
|
552
|
+
* @private
|
|
553
|
+
*/
|
|
554
|
+
_generateRegistryId: function() {
|
|
555
|
+
var data = 'reg_' + Date.now() + '_' + Math.random()
|
|
556
|
+
return 'reg_' + Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Generate registration ID
|
|
561
|
+
* @private
|
|
562
|
+
*/
|
|
563
|
+
_generateRegistrationId: function(tokenId) {
|
|
564
|
+
var data = 'reg_' + tokenId + '_' + Date.now()
|
|
565
|
+
return Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Generate revocation ID
|
|
570
|
+
* @private
|
|
571
|
+
*/
|
|
572
|
+
_generateRevocationId: function(tokenId) {
|
|
573
|
+
var data = 'rev_' + tokenId + '_' + Date.now()
|
|
574
|
+
return Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
|
|
575
|
+
},
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Hash token for integrity
|
|
579
|
+
* @private
|
|
580
|
+
*/
|
|
581
|
+
_hashToken: function(token) {
|
|
582
|
+
// Remove dynamic fields
|
|
583
|
+
var staticToken = JSON.parse(JSON.stringify(token))
|
|
584
|
+
delete staticToken.proof
|
|
585
|
+
delete staticToken.tokenHash
|
|
586
|
+
|
|
587
|
+
return Hash.sha256(Buffer.from(JSON.stringify(staticToken))).toString('hex')
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Generate revocation hash
|
|
592
|
+
* @private
|
|
593
|
+
*/
|
|
594
|
+
_generateRevocationHash: function(tokenId, revocation) {
|
|
595
|
+
var data = tokenId + (revocation.reason || '') + (revocation.revokedBy || '') + Date.now()
|
|
596
|
+
return Hash.sha256(Buffer.from(data)).toString('hex')
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Validate token for registration
|
|
601
|
+
* @private
|
|
602
|
+
*/
|
|
603
|
+
_validateTokenForRegistration: function(token, registry) {
|
|
604
|
+
var checks = []
|
|
605
|
+
var valid = true
|
|
606
|
+
|
|
607
|
+
// Required fields
|
|
608
|
+
if (!token.id) {
|
|
609
|
+
checks.push({ field: 'id', valid: false, error: 'Missing token ID' })
|
|
610
|
+
valid = false
|
|
611
|
+
} else {
|
|
612
|
+
checks.push({ field: 'id', valid: true })
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (!token.type) {
|
|
616
|
+
checks.push({ field: 'type', valid: false, error: 'Missing token type' })
|
|
617
|
+
valid = false
|
|
618
|
+
} else {
|
|
619
|
+
checks.push({ field: 'type', valid: true })
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (!token.issuanceDate) {
|
|
623
|
+
checks.push({ field: 'issuanceDate', valid: false, error: 'Missing issuance date' })
|
|
624
|
+
valid = false
|
|
625
|
+
} else {
|
|
626
|
+
checks.push({ field: 'issuanceDate', valid: true })
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Proof validation
|
|
630
|
+
if (!token.proof) {
|
|
631
|
+
checks.push({ field: 'proof', valid: false, error: 'Missing cryptographic proof' })
|
|
632
|
+
valid = false
|
|
633
|
+
} else {
|
|
634
|
+
checks.push({ field: 'proof', valid: true })
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return {
|
|
638
|
+
valid: valid,
|
|
639
|
+
checks: checks
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Calculate compliance level
|
|
645
|
+
* @private
|
|
646
|
+
*/
|
|
647
|
+
_calculateComplianceLevel: function(token) {
|
|
648
|
+
var level = 'BASIC'
|
|
649
|
+
|
|
650
|
+
if (token.proof && token.proof.type) {
|
|
651
|
+
level = 'STANDARD'
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (token.jurisdiction && token.legalBasis) {
|
|
655
|
+
level = 'ENHANCED'
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (token.attestations && token.attestations.length > 0) {
|
|
659
|
+
level = 'PREMIUM'
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return level
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Prepare audit entry for external storage
|
|
667
|
+
* @private
|
|
668
|
+
*/
|
|
669
|
+
_prepareAuditEntry: function(entry) {
|
|
670
|
+
var auditEntry = {
|
|
671
|
+
id: this._generateAuditId(),
|
|
672
|
+
action: entry.action,
|
|
673
|
+
actor: entry.actor || 'SYSTEM',
|
|
674
|
+
tokenId: entry.tokenId || null,
|
|
675
|
+
timestamp: entry.timestamp || new Date().toISOString(),
|
|
676
|
+
metadata: entry.metadata || {},
|
|
677
|
+
hash: this._generateAuditHash(entry)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return auditEntry
|
|
681
|
+
},
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Generate audit ID
|
|
685
|
+
* @private
|
|
686
|
+
*/
|
|
687
|
+
_generateAuditId: function() {
|
|
688
|
+
var data = 'audit_' + Date.now() + '_' + Math.random()
|
|
689
|
+
return 'audit_' + Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 12)
|
|
690
|
+
},
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Generate audit hash
|
|
694
|
+
* @private
|
|
695
|
+
*/
|
|
696
|
+
_generateAuditHash: function(entry) {
|
|
697
|
+
var data = (entry.action || '') + (entry.actor || '') + (entry.tokenId || '') + (entry.timestamp || '')
|
|
698
|
+
return Hash.sha256(Buffer.from(data)).toString('hex').substring(0, 16)
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
module.exports = LTPRegistry
|