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.
Files changed (41) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/architecture_demo.js +247 -0
  3. package/bsv-gdaf.min.js +37 -0
  4. package/bsv-ltp.min.js +37 -0
  5. package/bsv-shamir.min.js +12 -0
  6. package/bsv.bundle.js +9 -9
  7. package/build/bsv-smartcontract.min.js +10 -8
  8. package/build/bsv.bundle.js +9 -9
  9. package/build/bsv.min.js +10 -8
  10. package/build/webpack.gdaf.config.js +54 -0
  11. package/build/webpack.ltp.config.js +17 -0
  12. package/bundle-entry.js +77 -1
  13. package/complete_ltp_demo.js +511 -0
  14. package/gdaf-entry.js +54 -0
  15. package/index.js +259 -0
  16. package/lib/crypto/shamir.js +360 -0
  17. package/lib/gdaf/attestation-signer.js +461 -0
  18. package/lib/gdaf/attestation-verifier.js +600 -0
  19. package/lib/gdaf/did-resolver.js +382 -0
  20. package/lib/gdaf/index.js +471 -0
  21. package/lib/gdaf/schema-validator.js +682 -0
  22. package/lib/gdaf/smartledger-anchor.js +486 -0
  23. package/lib/gdaf/zk-prover.js +507 -0
  24. package/lib/ltp/anchor.js +438 -0
  25. package/lib/ltp/claim.js +1026 -0
  26. package/lib/ltp/index.js +470 -0
  27. package/lib/ltp/obligation.js +945 -0
  28. package/lib/ltp/proof.js +828 -0
  29. package/lib/ltp/registry.js +702 -0
  30. package/lib/ltp/right.js +765 -0
  31. package/lib/smart_contract/API_REFERENCE.md +1 -1
  32. package/lib/smart_contract/EXAMPLES.md +2 -2
  33. package/lib/smart_contract/QUICK_START.md +2 -2
  34. package/lib/smart_contract/README.md +1 -1
  35. package/ltp-entry.js +92 -0
  36. package/package.json +44 -4
  37. package/shamir-entry.js +173 -0
  38. package/shamir_demo.js +121 -0
  39. package/simple_demo.js +204 -0
  40. package/test_shamir.js +221 -0
  41. package/test_standalone_shamir.html +83 -0
@@ -0,0 +1,828 @@
1
+ 'use strict'
2
+
3
+ var bsv = require('../../')
4
+ var Hash = bsv.crypto.Hash
5
+ var ECDSA = bsv.crypto.ECDSA
6
+ var Signature = bsv.crypto.Signature
7
+ var PrivateKey = bsv.PrivateKey
8
+ var $ = bsv.util.preconditions
9
+
10
+ /**
11
+ * Legal Token Protocol - Proof Generation Primitives
12
+ *
13
+ * Provides primitives for cryptographic proof generation and verification
14
+ * without direct blockchain publishing. External systems handle proof storage
15
+ * and verification anchoring operations.
16
+ */
17
+
18
+ var LTPProof = {
19
+
20
+ /**
21
+ * Prepare signature proof for external verification
22
+ * @param {Object} token - Token to sign
23
+ * @param {PrivateKey} privateKey - Signing private key
24
+ * @param {Object} options - Signing options
25
+ * @returns {Object} Prepared signature proof data
26
+ */
27
+ prepareSignatureProof: function(token, privateKey, options) {
28
+ options = options || {}
29
+
30
+ $.checkArgument(token && typeof token === 'object', 'Invalid token')
31
+ $.checkArgument(privateKey instanceof PrivateKey, 'Invalid private key')
32
+
33
+ try {
34
+ // Canonicalize token for signing
35
+ var canonical = this._canonicalizeForSigning(token)
36
+ var tokenHash = Hash.sha256(Buffer.from(canonical))
37
+
38
+ // Create ECDSA signature
39
+ var ecdsa = new ECDSA()
40
+ ecdsa.hashbuf = tokenHash
41
+ ecdsa.privkey = privateKey
42
+ ecdsa.pubkey = privateKey.toPublicKey()
43
+
44
+ ecdsa.sign()
45
+ var signature = ecdsa.sig
46
+
47
+ // Create proof object
48
+ var proof = {
49
+ type: 'EcdsaSecp256k1Signature2019',
50
+ created: new Date().toISOString(),
51
+ verificationMethod: 'did:smartledger:' + privateKey.toPublicKey().toString() + '#keys-1',
52
+ proofPurpose: options.purpose || 'assertionMethod',
53
+ jws: this._createJWS(tokenHash, signature),
54
+ proofValue: signature.toDER().toString('hex')
55
+ }
56
+
57
+ return {
58
+ success: true,
59
+ proof: proof,
60
+ tokenHash: tokenHash.toString('hex'),
61
+ canonical: canonical,
62
+ signingKey: {
63
+ publicKey: privateKey.toPublicKey().toString(),
64
+ keyId: 'did:smartledger:' + privateKey.toPublicKey().toString() + '#keys-1'
65
+ },
66
+ externalOperations: {
67
+ storeProof: {
68
+ endpoint: 'POST /proofs/signature',
69
+ data: {
70
+ tokenHash: tokenHash.toString('hex'),
71
+ proof: proof,
72
+ signingKey: privateKey.toPublicKey().toString()
73
+ }
74
+ },
75
+ verifyProof: {
76
+ endpoint: 'POST /proofs/verify-signature',
77
+ data: {
78
+ tokenHash: tokenHash.toString('hex'),
79
+ signature: signature.toDER().toString('hex'),
80
+ publicKey: privateKey.toPublicKey().toString()
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ } catch (error) {
87
+ return {
88
+ success: false,
89
+ error: error.message
90
+ }
91
+ }
92
+ },
93
+
94
+ /**
95
+ * Prepare signature verification for external processing
96
+ * @param {Object} token - Token with proof
97
+ * @param {String} publicKey - Signer's public key (hex)
98
+ * @returns {Object} Prepared verification data
99
+ */
100
+ prepareSignatureVerification: function(token, publicKey) {
101
+ try {
102
+ $.checkArgument(token && typeof token === 'object', 'Invalid token')
103
+ $.checkArgument(typeof publicKey === 'string', 'Public key must be string')
104
+
105
+ if (!token.proof) {
106
+ return {
107
+ success: false,
108
+ error: 'No proof found in token'
109
+ }
110
+ }
111
+
112
+ // Recreate canonical representation
113
+ var canonical = this._canonicalizeForSigning(token)
114
+ var tokenHash = Hash.sha256(Buffer.from(canonical))
115
+
116
+ // Parse signature from proof
117
+ var signature = Signature.fromDER(Buffer.from(token.proof.proofValue, 'hex'))
118
+
119
+ // Prepare verification data
120
+ var verificationData = {
121
+ tokenHash: tokenHash.toString('hex'),
122
+ signature: signature.toDER().toString('hex'),
123
+ publicKey: publicKey,
124
+ proof: token.proof,
125
+ canonical: canonical
126
+ }
127
+
128
+ // Perform local verification for immediate feedback
129
+ var ecdsa = new ECDSA()
130
+ ecdsa.hashbuf = tokenHash
131
+ ecdsa.sig = signature
132
+ ecdsa.pubkey = bsv.PublicKey.fromString(publicKey)
133
+
134
+ var isValid = ecdsa.verify()
135
+
136
+ return {
137
+ success: true,
138
+ verification: {
139
+ valid: isValid,
140
+ tokenHash: tokenHash.toString('hex'),
141
+ signatureValid: isValid,
142
+ publicKey: publicKey,
143
+ verifiedAt: new Date().toISOString()
144
+ },
145
+ verificationData: verificationData,
146
+ externalOperations: {
147
+ recordVerification: {
148
+ endpoint: 'POST /proofs/verification-record',
149
+ data: {
150
+ tokenHash: tokenHash.toString('hex'),
151
+ result: isValid,
152
+ publicKey: publicKey,
153
+ verifiedAt: new Date().toISOString()
154
+ }
155
+ },
156
+ auditVerification: {
157
+ endpoint: 'POST /audit/proof-verification',
158
+ data: {
159
+ tokenId: token.id,
160
+ proofType: token.proof.type,
161
+ result: isValid,
162
+ verifier: publicKey
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ } catch (error) {
169
+ return {
170
+ success: false,
171
+ error: error.message
172
+ }
173
+ }
174
+ },
175
+
176
+ /**
177
+ * Prepare selective disclosure proof for external processing
178
+ * @param {Object} token - Original token
179
+ * @param {Array} revealedFields - Fields to reveal
180
+ * @param {String} nonce - Proof nonce
181
+ * @returns {Object} Prepared selective disclosure data
182
+ */
183
+ prepareSelectiveDisclosure: function(token, revealedFields, nonce) {
184
+ $.checkArgument(token && typeof token === 'object', 'Invalid token')
185
+ $.checkArgument(Array.isArray(revealedFields), 'Revealed fields must be array')
186
+ $.checkArgument(typeof nonce === 'string', 'Nonce must be string')
187
+
188
+ try {
189
+ // Create field commitments
190
+ var fieldCommitments = this._createFieldCommitments(token, nonce)
191
+
192
+ // Create Merkle tree of field commitments
193
+ var merkleTree = this._createMerkleTree(fieldCommitments)
194
+
195
+ // Create disclosure for revealed fields
196
+ var disclosures = []
197
+ var merkleProofs = []
198
+
199
+ revealedFields.forEach(function(fieldPath) {
200
+ var value = this._getNestedValue(token, fieldPath)
201
+ var commitment = this._findFieldCommitment(fieldCommitments, fieldPath)
202
+
203
+ if (commitment) {
204
+ disclosures.push({
205
+ path: fieldPath,
206
+ value: value,
207
+ commitment: commitment.hash,
208
+ salt: commitment.salt
209
+ })
210
+
211
+ // Generate Merkle proof for this field
212
+ var merkleProof = this._generateMerkleProof(merkleTree, commitment.index)
213
+ merkleProofs.push({
214
+ path: fieldPath,
215
+ leafIndex: commitment.index,
216
+ proof: merkleProof
217
+ })
218
+ }
219
+ }.bind(this))
220
+
221
+ var proof = {
222
+ type: 'LegalTokenSelectiveDisclosure',
223
+ created: new Date().toISOString(),
224
+ proofPurpose: 'selectiveDisclosure',
225
+ verificationMethod: token.proof ? token.proof.verificationMethod : null,
226
+ nonce: nonce,
227
+ merkleRoot: merkleTree.root,
228
+ disclosures: disclosures,
229
+ merkleProofs: merkleProofs,
230
+ totalFields: fieldCommitments.length
231
+ }
232
+
233
+ return {
234
+ success: true,
235
+ proof: proof,
236
+ hiddenFieldCount: fieldCommitments.length - disclosures.length,
237
+ revealedFieldCount: disclosures.length,
238
+ externalOperations: {
239
+ storeDisclosure: {
240
+ endpoint: 'POST /proofs/selective-disclosure',
241
+ data: {
242
+ tokenId: token.id,
243
+ proof: proof,
244
+ merkleRoot: merkleTree.root
245
+ }
246
+ },
247
+ verifyDisclosure: {
248
+ endpoint: 'POST /proofs/verify-disclosure',
249
+ data: {
250
+ proof: proof,
251
+ expectedNonce: nonce
252
+ }
253
+ },
254
+ auditDisclosure: {
255
+ endpoint: 'POST /audit/selective-disclosure',
256
+ data: {
257
+ tokenId: token.id,
258
+ revealedFields: revealedFields,
259
+ totalFields: fieldCommitments.length,
260
+ disclosedAt: new Date().toISOString()
261
+ }
262
+ }
263
+ }
264
+ }
265
+
266
+ } catch (error) {
267
+ return {
268
+ success: false,
269
+ error: error.message
270
+ }
271
+ }
272
+ },
273
+
274
+ /**
275
+ * Prepare selective disclosure verification for external processing
276
+ * @param {Object} proof - Selective disclosure proof
277
+ * @param {String} expectedNonce - Expected nonce
278
+ * @returns {Object} Prepared verification data
279
+ */
280
+ prepareSelectiveDisclosureVerification: function(proof, expectedNonce) {
281
+ try {
282
+ $.checkArgument(proof && typeof proof === 'object', 'Invalid proof')
283
+ $.checkArgument(typeof expectedNonce === 'string', 'Expected nonce must be string')
284
+
285
+ if (proof.nonce !== expectedNonce) {
286
+ return {
287
+ success: false,
288
+ error: 'Nonce mismatch'
289
+ }
290
+ }
291
+
292
+ var validDisclosures = []
293
+ var errors = []
294
+
295
+ // Verify each disclosure
296
+ proof.disclosures.forEach(function(disclosure, index) {
297
+ var merkleProof = proof.merkleProofs[index]
298
+
299
+ // Verify field commitment
300
+ var expectedCommitment = this._hashField(disclosure.path, disclosure.value, disclosure.salt)
301
+ if (expectedCommitment !== disclosure.commitment) {
302
+ errors.push('Invalid commitment for field: ' + disclosure.path)
303
+ return
304
+ }
305
+
306
+ // Verify Merkle proof
307
+ var isValidProof = this._verifyMerkleProof(
308
+ disclosure.commitment,
309
+ merkleProof.leafIndex,
310
+ merkleProof.proof,
311
+ proof.merkleRoot
312
+ )
313
+
314
+ if (isValidProof) {
315
+ validDisclosures.push({
316
+ path: disclosure.path,
317
+ value: disclosure.value,
318
+ verified: true
319
+ })
320
+ } else {
321
+ errors.push('Invalid Merkle proof for field: ' + disclosure.path)
322
+ }
323
+ }.bind(this))
324
+
325
+ var verification = {
326
+ valid: errors.length === 0,
327
+ errors: errors,
328
+ disclosures: validDisclosures,
329
+ merkleRoot: proof.merkleRoot,
330
+ totalFields: proof.totalFields,
331
+ verifiedAt: new Date().toISOString()
332
+ }
333
+
334
+ return {
335
+ success: true,
336
+ verification: verification,
337
+ externalOperations: {
338
+ recordVerification: {
339
+ endpoint: 'POST /proofs/disclosure-verification',
340
+ data: {
341
+ proofId: proof.id || 'unknown',
342
+ result: verification.valid,
343
+ verifiedFields: validDisclosures.length,
344
+ totalFields: proof.totalFields,
345
+ verifiedAt: verification.verifiedAt
346
+ }
347
+ },
348
+ auditVerification: {
349
+ endpoint: 'POST /audit/disclosure-verification',
350
+ data: {
351
+ merkleRoot: proof.merkleRoot,
352
+ disclosuresVerified: validDisclosures.length,
353
+ errors: errors.length,
354
+ result: verification.valid
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ } catch (error) {
361
+ return {
362
+ success: false,
363
+ error: error.message
364
+ }
365
+ }
366
+ },
367
+
368
+ /**
369
+ * Prepare legal validity proof for external compliance checking
370
+ * @param {Object} token - Legal token
371
+ * @param {Object} jurisdiction - Jurisdiction rules
372
+ * @param {String} nonce - Proof nonce
373
+ * @returns {Object} Prepared legal validity data
374
+ */
375
+ prepareLegalValidityProof: function(token, jurisdiction, nonce) {
376
+ $.checkArgument(token && typeof token === 'object', 'Invalid token')
377
+ $.checkArgument(jurisdiction && typeof jurisdiction === 'object', 'Invalid jurisdiction')
378
+ $.checkArgument(typeof nonce === 'string', 'Nonce must be string')
379
+
380
+ try {
381
+ // Check legal requirements
382
+ var requirements = jurisdiction.requirements || []
383
+ var validityChecks = []
384
+ var isValid = true
385
+
386
+ requirements.forEach(function(requirement) {
387
+ var check = this._checkLegalRequirement(token, requirement)
388
+ validityChecks.push(check)
389
+ if (!check.satisfied) {
390
+ isValid = false
391
+ }
392
+ }.bind(this))
393
+
394
+ // Create proof
395
+ var proof = {
396
+ type: 'LegalValidityProof',
397
+ created: new Date().toISOString(),
398
+ proofPurpose: 'legalValidity',
399
+ jurisdiction: jurisdiction.code,
400
+ nonce: nonce,
401
+ valid: isValid,
402
+ checks: validityChecks,
403
+ complianceHash: this._hashCompliance(token, jurisdiction, nonce)
404
+ }
405
+
406
+ return {
407
+ success: true,
408
+ proof: proof,
409
+ valid: isValid,
410
+ complianceReport: {
411
+ jurisdiction: jurisdiction.code,
412
+ totalRequirements: requirements.length,
413
+ satisfiedRequirements: validityChecks.filter(c => c.satisfied).length,
414
+ failedRequirements: validityChecks.filter(c => !c.satisfied).length,
415
+ overallCompliance: isValid
416
+ },
417
+ externalOperations: {
418
+ submitCompliance: {
419
+ endpoint: 'POST /compliance/legal-validity',
420
+ data: {
421
+ tokenId: token.id,
422
+ jurisdiction: jurisdiction.code,
423
+ proof: proof,
424
+ complianceReport: {
425
+ jurisdiction: jurisdiction.code,
426
+ totalRequirements: requirements.length,
427
+ satisfiedRequirements: validityChecks.filter(c => c.satisfied).length,
428
+ failedRequirements: validityChecks.filter(c => !c.satisfied).length,
429
+ overallCompliance: isValid
430
+ }
431
+ }
432
+ },
433
+ auditCompliance: {
434
+ endpoint: 'POST /audit/compliance-check',
435
+ data: {
436
+ tokenId: token.id,
437
+ jurisdiction: jurisdiction.code,
438
+ result: isValid,
439
+ checkedAt: new Date().toISOString(),
440
+ requirements: validityChecks
441
+ }
442
+ },
443
+ notifyRegulator: isValid ? null : {
444
+ endpoint: 'POST /notifications/compliance-failure',
445
+ data: {
446
+ tokenId: token.id,
447
+ jurisdiction: jurisdiction.code,
448
+ failedRequirements: validityChecks.filter(c => !c.satisfied)
449
+ }
450
+ }
451
+ }
452
+ }
453
+
454
+ } catch (error) {
455
+ return {
456
+ success: false,
457
+ error: error.message
458
+ }
459
+ }
460
+ },
461
+
462
+ /**
463
+ * Prepare zero-knowledge proof for external processing
464
+ * @param {Object} token - Token data
465
+ * @param {Object} statement - Statement to prove
466
+ * @param {String} nonce - Proof nonce
467
+ * @returns {Object} Prepared ZK proof data
468
+ */
469
+ prepareZeroKnowledgeProof: function(token, statement, nonce) {
470
+ $.checkArgument(token && typeof token === 'object', 'Invalid token')
471
+ $.checkArgument(statement && typeof statement === 'object', 'Invalid statement')
472
+ $.checkArgument(typeof nonce === 'string', 'Nonce must be string')
473
+
474
+ try {
475
+ // Create commitment to token data
476
+ var tokenCommitment = Hash.sha256(Buffer.from(this._canonicalizeForSigning(token) + nonce)).toString('hex')
477
+
478
+ // Create statement proof (simplified - real ZK would use more sophisticated cryptography)
479
+ var statementProof = {
480
+ type: 'LegalTokenZKProof',
481
+ created: new Date().toISOString(),
482
+ proofPurpose: 'zeroKnowledge',
483
+ statement: {
484
+ claim: statement.claim || 'UNSPECIFIED',
485
+ predicate: statement.predicate || 'SATISFIES',
486
+ threshold: statement.threshold || null
487
+ },
488
+ commitment: tokenCommitment,
489
+ nonce: nonce,
490
+ proofData: this._generateZKProofData(token, statement, nonce)
491
+ }
492
+
493
+ return {
494
+ success: true,
495
+ proof: statementProof,
496
+ commitment: tokenCommitment,
497
+ externalOperations: {
498
+ storeZKProof: {
499
+ endpoint: 'POST /proofs/zero-knowledge',
500
+ data: {
501
+ tokenId: token.id,
502
+ proof: statementProof,
503
+ statement: statement
504
+ }
505
+ },
506
+ verifyZKProof: {
507
+ endpoint: 'POST /proofs/verify-zk',
508
+ data: {
509
+ proof: statementProof,
510
+ expectedNonce: nonce
511
+ }
512
+ },
513
+ auditZKProof: {
514
+ endpoint: 'POST /audit/zk-proof',
515
+ data: {
516
+ tokenId: token.id,
517
+ statement: statement.claim,
518
+ proofGenerated: new Date().toISOString()
519
+ }
520
+ }
521
+ }
522
+ }
523
+
524
+ } catch (error) {
525
+ return {
526
+ success: false,
527
+ error: error.message
528
+ }
529
+ }
530
+ },
531
+
532
+ /**
533
+ * Generate ZK proof data (simplified implementation)
534
+ * @private
535
+ */
536
+ _generateZKProofData: function(token, statement, nonce) {
537
+ // Simplified ZK proof generation - real implementation would use more sophisticated cryptography
538
+ var proofData = {
539
+ challenge: Hash.sha256(Buffer.from(statement.claim + nonce)).toString('hex'),
540
+ response: Hash.sha256(Buffer.from(this._canonicalizeForSigning(token) + statement.claim)).toString('hex'),
541
+ witness: Hash.sha256(Buffer.from(nonce + token.id + statement.predicate)).toString('hex')
542
+ }
543
+
544
+ return proofData
545
+ },
546
+
547
+ /**
548
+ * Create JWS signature
549
+ * @private
550
+ */
551
+ _createJWS: function(hash, signature) {
552
+ var header = {
553
+ alg: 'ES256K',
554
+ typ: 'JWT'
555
+ }
556
+
557
+ var headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url')
558
+ var payloadB64 = hash.toString('base64url')
559
+ var signatureB64 = signature.toDER().toString('base64url')
560
+
561
+ return headerB64 + '..' + signatureB64
562
+ },
563
+
564
+ /**
565
+ * Canonicalize token for signing
566
+ * @private
567
+ */
568
+ _canonicalizeForSigning: function(token) {
569
+ // Remove proof and hash fields
570
+ var signingToken = JSON.parse(JSON.stringify(token))
571
+ delete signingToken.proof
572
+ delete signingToken.tokenHash
573
+
574
+ // Sort keys recursively
575
+ var canonical = this._sortObjectKeys(signingToken)
576
+
577
+ return JSON.stringify(canonical)
578
+ },
579
+
580
+ /**
581
+ * Create field commitments
582
+ * @private
583
+ */
584
+ _createFieldCommitments: function(token, nonce) {
585
+ var commitments = []
586
+ var index = 0
587
+
588
+ this._traverseObject(token, '', function(path, value) {
589
+ if (path !== 'proof' && path !== 'tokenHash') {
590
+ var salt = Hash.sha256(Buffer.from(nonce + path + index)).toString('hex')
591
+ var hash = this._hashField(path, value, salt)
592
+
593
+ commitments.push({
594
+ path: path,
595
+ value: value,
596
+ salt: salt,
597
+ hash: hash,
598
+ index: index
599
+ })
600
+
601
+ index++
602
+ }
603
+ }.bind(this))
604
+
605
+ return commitments
606
+ },
607
+
608
+ /**
609
+ * Hash field with salt
610
+ * @private
611
+ */
612
+ _hashField: function(path, value, salt) {
613
+ var data = path + ':' + JSON.stringify(value) + ':' + salt
614
+ return Hash.sha256(Buffer.from(data)).toString('hex')
615
+ },
616
+
617
+ /**
618
+ * Create Merkle tree from commitments
619
+ * @private
620
+ */
621
+ _createMerkleTree: function(commitments) {
622
+ var leaves = commitments.map(function(c) { return c.hash })
623
+
624
+ if (leaves.length === 0) {
625
+ return { root: null, levels: [] }
626
+ }
627
+
628
+ var levels = [leaves]
629
+ var currentLevel = leaves
630
+
631
+ while (currentLevel.length > 1) {
632
+ var nextLevel = []
633
+
634
+ for (var i = 0; i < currentLevel.length; i += 2) {
635
+ var left = currentLevel[i]
636
+ var right = currentLevel[i + 1] || left
637
+
638
+ var combined = Buffer.concat([
639
+ Buffer.from(left, 'hex'),
640
+ Buffer.from(right, 'hex')
641
+ ])
642
+
643
+ var hash = Hash.sha256(combined).toString('hex')
644
+ nextLevel.push(hash)
645
+ }
646
+
647
+ levels.push(nextLevel)
648
+ currentLevel = nextLevel
649
+ }
650
+
651
+ return {
652
+ root: currentLevel[0],
653
+ levels: levels
654
+ }
655
+ },
656
+
657
+ /**
658
+ * Generate Merkle proof for leaf
659
+ * @private
660
+ */
661
+ _generateMerkleProof: function(tree, leafIndex) {
662
+ var proof = []
663
+ var index = leafIndex
664
+
665
+ for (var level = 0; level < tree.levels.length - 1; level++) {
666
+ var currentLevel = tree.levels[level]
667
+ var isLeft = index % 2 === 0
668
+ var siblingIndex = isLeft ? index + 1 : index - 1
669
+
670
+ if (siblingIndex < currentLevel.length) {
671
+ proof.push({
672
+ hash: currentLevel[siblingIndex],
673
+ isLeft: !isLeft
674
+ })
675
+ }
676
+
677
+ index = Math.floor(index / 2)
678
+ }
679
+
680
+ return proof
681
+ },
682
+
683
+ /**
684
+ * Verify Merkle proof
685
+ * @private
686
+ */
687
+ _verifyMerkleProof: function(leaf, leafIndex, proof, expectedRoot) {
688
+ var currentHash = leaf
689
+ var currentIndex = leafIndex
690
+
691
+ for (var i = 0; i < proof.length; i++) {
692
+ var step = proof[i]
693
+ var isLeft = currentIndex % 2 === 0
694
+
695
+ var combined
696
+ if (step.isLeft) {
697
+ combined = Buffer.concat([
698
+ Buffer.from(step.hash, 'hex'),
699
+ Buffer.from(currentHash, 'hex')
700
+ ])
701
+ } else {
702
+ combined = Buffer.concat([
703
+ Buffer.from(currentHash, 'hex'),
704
+ Buffer.from(step.hash, 'hex')
705
+ ])
706
+ }
707
+
708
+ currentHash = Hash.sha256(combined).toString('hex')
709
+ currentIndex = Math.floor(currentIndex / 2)
710
+ }
711
+
712
+ return currentHash === expectedRoot
713
+ },
714
+
715
+ /**
716
+ * Traverse object and call callback for each field
717
+ * @private
718
+ */
719
+ _traverseObject: function(obj, basePath, callback) {
720
+ if (Array.isArray(obj)) {
721
+ obj.forEach(function(item, index) {
722
+ var path = basePath + '[' + index + ']'
723
+ this._traverseObject(item, path, callback)
724
+ }.bind(this))
725
+ } else if (obj !== null && typeof obj === 'object') {
726
+ Object.keys(obj).forEach(function(key) {
727
+ var path = basePath ? basePath + '.' + key : key
728
+ this._traverseObject(obj[key], path, callback)
729
+ }.bind(this))
730
+ } else {
731
+ callback(basePath, obj)
732
+ }
733
+ },
734
+
735
+ /**
736
+ * Get nested value from object
737
+ * @private
738
+ */
739
+ _getNestedValue: function(obj, path) {
740
+ return path.split('.').reduce(function(current, prop) {
741
+ return current && current[prop]
742
+ }, obj)
743
+ },
744
+
745
+ /**
746
+ * Find field commitment by path
747
+ * @private
748
+ */
749
+ _findFieldCommitment: function(commitments, path) {
750
+ return commitments.find(function(c) { return c.path === path })
751
+ },
752
+
753
+ /**
754
+ * Check legal requirement
755
+ * @private
756
+ */
757
+ _checkLegalRequirement: function(token, requirement) {
758
+ // Simplified legal requirement checking
759
+ // In practice, this would be much more sophisticated
760
+
761
+ switch (requirement.type) {
762
+ case 'field_present':
763
+ var value = this._getNestedValue(token, requirement.field)
764
+ return {
765
+ requirement: requirement.type,
766
+ field: requirement.field,
767
+ satisfied: value !== undefined && value !== null
768
+ }
769
+
770
+ case 'field_value':
771
+ var value = this._getNestedValue(token, requirement.field)
772
+ return {
773
+ requirement: requirement.type,
774
+ field: requirement.field,
775
+ satisfied: value === requirement.expectedValue
776
+ }
777
+
778
+ case 'temporal_validity':
779
+ var now = new Date()
780
+ var issuanceDate = new Date(token.issuanceDate)
781
+ return {
782
+ requirement: requirement.type,
783
+ satisfied: issuanceDate <= now
784
+ }
785
+
786
+ default:
787
+ return {
788
+ requirement: requirement.type,
789
+ satisfied: false,
790
+ error: 'Unknown requirement type'
791
+ }
792
+ }
793
+ },
794
+
795
+ /**
796
+ * Hash compliance information
797
+ * @private
798
+ */
799
+ _hashCompliance: function(token, jurisdiction, nonce) {
800
+ var complianceData = {
801
+ tokenId: token.id,
802
+ jurisdiction: jurisdiction.code,
803
+ nonce: nonce,
804
+ timestamp: new Date().toISOString()
805
+ }
806
+
807
+ return Hash.sha256(Buffer.from(JSON.stringify(complianceData))).toString('hex')
808
+ },
809
+
810
+ /**
811
+ * Sort object keys recursively
812
+ * @private
813
+ */
814
+ _sortObjectKeys: function(obj) {
815
+ if (Array.isArray(obj)) {
816
+ return obj.map(this._sortObjectKeys.bind(this))
817
+ } else if (obj !== null && typeof obj === 'object') {
818
+ var sorted = {}
819
+ Object.keys(obj).sort().forEach(function(key) {
820
+ sorted[key] = this._sortObjectKeys(obj[key])
821
+ }.bind(this))
822
+ return sorted
823
+ }
824
+ return obj
825
+ }
826
+ }
827
+
828
+ module.exports = LTPProof