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.
Files changed (70) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/README.md +289 -55
  3. package/architecture_demo.js +247 -0
  4. package/bsv-covenant.min.js +10 -0
  5. package/bsv-gdaf.min.js +37 -0
  6. package/bsv-ltp.min.js +37 -0
  7. package/bsv-script-helper.min.js +10 -0
  8. package/bsv-security.min.js +31 -0
  9. package/bsv-shamir.min.js +12 -0
  10. package/bsv-smartcontract.min.js +37 -0
  11. package/bsv.bundle.js +9 -9
  12. package/bsv.min.js +3 -3
  13. package/build/bsv-covenant.min.js +10 -0
  14. package/build/bsv-script-helper.min.js +10 -0
  15. package/build/bsv-security.min.js +31 -0
  16. package/build/bsv-smartcontract.min.js +39 -0
  17. package/build/bsv.bundle.js +39 -0
  18. package/build/bsv.min.js +39 -0
  19. package/build/webpack.bundle.config.js +22 -0
  20. package/build/webpack.config.js +18 -0
  21. package/build/webpack.covenant.config.js +27 -0
  22. package/build/webpack.gdaf.config.js +54 -0
  23. package/build/webpack.ltp.config.js +17 -0
  24. package/build/webpack.script-helper.config.js +27 -0
  25. package/build/webpack.security.config.js +23 -0
  26. package/build/webpack.smartcontract.config.js +25 -0
  27. package/build/webpack.subproject.config.js +6 -0
  28. package/bundle-entry.js +341 -0
  29. package/complete_ltp_demo.js +511 -0
  30. package/covenant-entry.js +44 -0
  31. package/docs/pushtx-key-insights.md +106 -0
  32. package/gdaf-entry.js +54 -0
  33. package/index.js +272 -5
  34. package/lib/crypto/shamir.js +360 -0
  35. package/lib/gdaf/attestation-signer.js +461 -0
  36. package/lib/gdaf/attestation-verifier.js +600 -0
  37. package/lib/gdaf/did-resolver.js +382 -0
  38. package/lib/gdaf/index.js +471 -0
  39. package/lib/gdaf/schema-validator.js +682 -0
  40. package/lib/gdaf/smartledger-anchor.js +486 -0
  41. package/lib/gdaf/zk-prover.js +507 -0
  42. package/lib/ltp/anchor.js +438 -0
  43. package/lib/ltp/claim.js +1026 -0
  44. package/lib/ltp/index.js +470 -0
  45. package/lib/ltp/obligation.js +945 -0
  46. package/lib/ltp/proof.js +828 -0
  47. package/lib/ltp/registry.js +702 -0
  48. package/lib/ltp/right.js +765 -0
  49. package/lib/smart_contract/API_REFERENCE.md +1 -1
  50. package/lib/smart_contract/EXAMPLES.md +2 -2
  51. package/lib/smart_contract/QUICK_START.md +2 -2
  52. package/lib/smart_contract/README.md +1 -1
  53. package/lib/smart_contract/index.js +4 -0
  54. package/ltp-entry.js +92 -0
  55. package/package.json +91 -20
  56. package/script-helper-entry.js +49 -0
  57. package/security-entry.js +70 -0
  58. package/shamir-entry.js +173 -0
  59. package/shamir_demo.js +121 -0
  60. package/simple_demo.js +204 -0
  61. package/smartcontract-entry.js +133 -0
  62. package/test_shamir.js +221 -0
  63. package/test_standalone_shamir.html +83 -0
  64. package/tests/bundle-completeness-test.html +131 -0
  65. package/tests/bundle-demo.html +476 -0
  66. package/tests/smartcontract-test.html +239 -0
  67. package/tests/standalone-modules-test.html +260 -0
  68. package/tests/test.html +612 -0
  69. package/tests/unpkg-demo.html +194 -0
  70. package/docs/nchain.md +0 -958
@@ -0,0 +1,486 @@
1
+ 'use strict'
2
+
3
+ var bsv = require('../../')
4
+ var Transaction = bsv.Transaction
5
+ var Script = bsv.Script
6
+ var PrivateKey = bsv.PrivateKey
7
+ var Address = bsv.Address
8
+ var Hash = bsv.crypto.Hash
9
+ var $ = bsv.util.preconditions
10
+
11
+ /**
12
+ * SmartLedgerAnchor
13
+ *
14
+ * Blockchain anchoring system for credential root hashes and attestations.
15
+ * Provides immutable timestamping and proof of existence on the BSV blockchain
16
+ * using OP_RETURN outputs with structured metadata.
17
+ *
18
+ * Features:
19
+ * - Credential hash anchoring
20
+ * - Batch anchoring for efficiency
21
+ * - DID registration on-chain
22
+ * - Revocation list management
23
+ * - Timestamped proof of existence
24
+ * - Cost-effective anchoring strategies
25
+ */
26
+
27
+ /**
28
+ * SmartLedgerAnchor constructor
29
+ * @param {PrivateKey|String} privateKey - Private key for transactions
30
+ * @param {Object} options - Configuration options
31
+ */
32
+ function SmartLedgerAnchor(privateKey, options) {
33
+ if (!(this instanceof SmartLedgerAnchor)) {
34
+ return new SmartLedgerAnchor(privateKey, options)
35
+ }
36
+
37
+ if (typeof privateKey === 'string') {
38
+ privateKey = PrivateKey.fromWIF(privateKey)
39
+ }
40
+
41
+ // Allow null private key for query-only operations
42
+ if (privateKey !== null) {
43
+ $.checkArgument(privateKey instanceof PrivateKey, 'Invalid private key')
44
+ this.privateKey = privateKey
45
+ this.address = privateKey.toAddress()
46
+ } else {
47
+ this.privateKey = null
48
+ this.address = null
49
+ }
50
+
51
+ this.options = options || {}
52
+ this.network = this.options.network || 'mainnet'
53
+
54
+ // Protocol identifier
55
+ this.PROTOCOL_ID = 'SMARTLEDGER.ATTEST'
56
+ this.VERSION = 1
57
+
58
+ return this
59
+ }
60
+
61
+ /**
62
+ * Anchor credential hash to blockchain
63
+ * @param {String|Buffer} credentialHash - Hash to anchor
64
+ * @param {Object} metadata - Additional metadata
65
+ * @param {Array} utxos - UTXOs for transaction
66
+ * @returns {Promise<Object>} Anchor result
67
+ */
68
+ SmartLedgerAnchor.prototype.anchorCredential = async function(credentialHash, metadata, utxos) {
69
+ metadata = metadata || {}
70
+
71
+ if (typeof credentialHash === 'string') {
72
+ credentialHash = Buffer.from(credentialHash, 'hex')
73
+ }
74
+
75
+ $.checkArgument(Buffer.isBuffer(credentialHash), 'Invalid credential hash')
76
+ $.checkArgument(credentialHash.length === 32, 'Hash must be 32 bytes')
77
+
78
+ // Create anchor payload
79
+ var anchorData = this._createAnchorPayload('CREDENTIAL', credentialHash, metadata)
80
+
81
+ // Create transaction
82
+ var tx = await this._createAnchorTransaction(anchorData, utxos)
83
+
84
+ return {
85
+ txid: tx.hash,
86
+ transaction: tx,
87
+ anchorHash: credentialHash.toString('hex'),
88
+ metadata: metadata,
89
+ timestamp: new Date().toISOString(),
90
+ blockchainProof: {
91
+ protocol: this.PROTOCOL_ID,
92
+ version: this.VERSION,
93
+ type: 'CREDENTIAL'
94
+ }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Anchor multiple credentials in batch
100
+ * @param {Array} credentialHashes - Array of hashes to anchor
101
+ * @param {Object} metadata - Batch metadata
102
+ * @param {Array} utxos - UTXOs for transaction
103
+ * @returns {Promise<Object>} Batch anchor result
104
+ */
105
+ SmartLedgerAnchor.prototype.anchorBatch = async function(credentialHashes, metadata, utxos) {
106
+ metadata = metadata || {}
107
+
108
+ $.checkArgument(Array.isArray(credentialHashes), 'Credential hashes must be array')
109
+ $.checkArgument(credentialHashes.length > 0, 'Must provide at least one hash')
110
+
111
+ // Create Merkle tree from hashes
112
+ var merkleRoot = this._createMerkleRoot(credentialHashes)
113
+
114
+ // Create batch anchor payload
115
+ var anchorData = this._createAnchorPayload('BATCH', merkleRoot, {
116
+ ...metadata,
117
+ batchSize: credentialHashes.length,
118
+ merkleRoot: merkleRoot.toString('hex')
119
+ })
120
+
121
+ // Create transaction
122
+ var tx = await this._createAnchorTransaction(anchorData, utxos)
123
+
124
+ return {
125
+ txid: tx.hash,
126
+ transaction: tx,
127
+ merkleRoot: merkleRoot.toString('hex'),
128
+ batchSize: credentialHashes.length,
129
+ credentialHashes: credentialHashes.map(h => Buffer.isBuffer(h) ? h.toString('hex') : h),
130
+ metadata: metadata,
131
+ timestamp: new Date().toISOString(),
132
+ blockchainProof: {
133
+ protocol: this.PROTOCOL_ID,
134
+ version: this.VERSION,
135
+ type: 'BATCH'
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Register DID on blockchain
142
+ * @param {String} did - DID to register
143
+ * @param {Object} didDocument - DID Document
144
+ * @param {Array} utxos - UTXOs for transaction
145
+ * @returns {Promise<Object>} Registration result
146
+ */
147
+ SmartLedgerAnchor.prototype.registerDID = async function(did, didDocument, utxos) {
148
+ $.checkArgument(typeof did === 'string', 'DID must be string')
149
+ $.checkArgument(didDocument && typeof didDocument === 'object', 'Invalid DID document')
150
+
151
+ // Create hash of DID document
152
+ var documentHash = Hash.sha256(Buffer.from(JSON.stringify(didDocument), 'utf8'))
153
+
154
+ // Create DID registration payload
155
+ var anchorData = this._createAnchorPayload('DID_REG', documentHash, {
156
+ did: did,
157
+ operation: 'create',
158
+ documentHash: documentHash.toString('hex')
159
+ })
160
+
161
+ // Create transaction
162
+ var tx = await this._createAnchorTransaction(anchorData, utxos)
163
+
164
+ return {
165
+ txid: tx.hash,
166
+ transaction: tx,
167
+ did: did,
168
+ documentHash: documentHash.toString('hex'),
169
+ timestamp: new Date().toISOString(),
170
+ blockchainProof: {
171
+ protocol: this.PROTOCOL_ID,
172
+ version: this.VERSION,
173
+ type: 'DID_REG'
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Revoke credential on blockchain
180
+ * @param {String} credentialId - Credential ID to revoke
181
+ * @param {String} reason - Revocation reason
182
+ * @param {Array} utxos - UTXOs for transaction
183
+ * @returns {Promise<Object>} Revocation result
184
+ */
185
+ SmartLedgerAnchor.prototype.revokeCredential = async function(credentialId, reason, utxos) {
186
+ $.checkArgument(typeof credentialId === 'string', 'Credential ID must be string')
187
+
188
+ reason = reason || 'unspecified'
189
+
190
+ // Create revocation hash
191
+ var revocationData = {
192
+ credentialId: credentialId,
193
+ reason: reason,
194
+ timestamp: new Date().toISOString(),
195
+ issuer: this.address.toString()
196
+ }
197
+
198
+ var revocationHash = Hash.sha256(Buffer.from(JSON.stringify(revocationData), 'utf8'))
199
+
200
+ // Create revocation payload
201
+ var anchorData = this._createAnchorPayload('REVOKE', revocationHash, {
202
+ credentialId: credentialId,
203
+ reason: reason
204
+ })
205
+
206
+ // Create transaction
207
+ var tx = await this._createAnchorTransaction(anchorData, utxos)
208
+
209
+ return {
210
+ txid: tx.hash,
211
+ transaction: tx,
212
+ credentialId: credentialId,
213
+ reason: reason,
214
+ revocationHash: revocationHash.toString('hex'),
215
+ timestamp: new Date().toISOString(),
216
+ blockchainProof: {
217
+ protocol: this.PROTOCOL_ID,
218
+ version: this.VERSION,
219
+ type: 'REVOKE'
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Create anchor payload
226
+ * @private
227
+ */
228
+ SmartLedgerAnchor.prototype._createAnchorPayload = function(type, hash, metadata) {
229
+ var timestamp = Math.floor(Date.now() / 1000) // Unix timestamp
230
+
231
+ // Create structured payload
232
+ var payload = Buffer.concat([
233
+ Buffer.from(this.PROTOCOL_ID, 'utf8'), // Protocol identifier
234
+ Buffer.from([this.VERSION]), // Version
235
+ Buffer.from(type.padEnd(10, '\0'), 'utf8'), // Type (padded to 10 bytes)
236
+ Buffer.from([timestamp >> 24, timestamp >> 16, timestamp >> 8, timestamp]), // Timestamp (4 bytes)
237
+ hash, // Hash (32 bytes)
238
+ Buffer.from(JSON.stringify(metadata), 'utf8') // Metadata (variable)
239
+ ])
240
+
241
+ return payload
242
+ }
243
+
244
+ /**
245
+ * Create anchor transaction
246
+ * @private
247
+ */
248
+ SmartLedgerAnchor.prototype._createAnchorTransaction = async function(anchorData, utxos) {
249
+ if (!utxos || utxos.length === 0) {
250
+ throw new Error('UTXOs required for anchor transaction')
251
+ }
252
+
253
+ // Calculate required fee (simplified)
254
+ var estimatedSize = 200 + anchorData.length // Base size + OP_RETURN data
255
+ var feeRate = this.options.feeRate || 1 // satoshis per byte
256
+ var fee = estimatedSize * feeRate
257
+
258
+ // Calculate total input value
259
+ var totalInput = utxos.reduce((sum, utxo) => sum + utxo.satoshis, 0)
260
+
261
+ if (totalInput < fee) {
262
+ throw new Error('Insufficient funds for anchor transaction')
263
+ }
264
+
265
+ // Create transaction
266
+ var tx = new Transaction()
267
+
268
+ // Add inputs
269
+ utxos.forEach(utxo => {
270
+ tx.from(utxo)
271
+ })
272
+
273
+ // Add OP_RETURN output with anchor data
274
+ var opReturnScript = Script.buildDataOut(anchorData)
275
+ tx.addOutput(new Transaction.Output({
276
+ script: opReturnScript,
277
+ satoshis: 0
278
+ }))
279
+
280
+ // Add change output if needed
281
+ var change = totalInput - fee
282
+ if (change > 546) { // Dust limit
283
+ tx.to(this.address, change)
284
+ }
285
+
286
+ // Sign transaction
287
+ tx.sign(this.privateKey)
288
+
289
+ return tx
290
+ }
291
+
292
+ /**
293
+ * Create Merkle root from array of hashes
294
+ * @private
295
+ */
296
+ SmartLedgerAnchor.prototype._createMerkleRoot = function(hashes) {
297
+ if (hashes.length === 0) {
298
+ throw new Error('Cannot create Merkle root from empty array')
299
+ }
300
+
301
+ // Convert to buffers if needed
302
+ var hashBuffers = hashes.map(hash => {
303
+ return Buffer.isBuffer(hash) ? hash : Buffer.from(hash, 'hex')
304
+ })
305
+
306
+ // Build Merkle tree
307
+ while (hashBuffers.length > 1) {
308
+ var nextLevel = []
309
+
310
+ for (var i = 0; i < hashBuffers.length; i += 2) {
311
+ var left = hashBuffers[i]
312
+ var right = i + 1 < hashBuffers.length ? hashBuffers[i + 1] : left
313
+
314
+ var combined = Buffer.concat([left, right])
315
+ var hash = Hash.sha256(combined)
316
+ nextLevel.push(hash)
317
+ }
318
+
319
+ hashBuffers = nextLevel
320
+ }
321
+
322
+ return hashBuffers[0]
323
+ }
324
+
325
+ /**
326
+ * Parse anchor data from OP_RETURN output
327
+ * @param {Buffer} data - Raw OP_RETURN data
328
+ * @returns {Object} Parsed anchor data
329
+ */
330
+ SmartLedgerAnchor.parseAnchorData = function(data) {
331
+ if (!Buffer.isBuffer(data)) {
332
+ throw new Error('Data must be Buffer')
333
+ }
334
+
335
+ if (data.length < 50) { // Minimum expected size
336
+ throw new Error('Data too short for SmartLedger anchor')
337
+ }
338
+
339
+ var offset = 0
340
+
341
+ // Parse protocol identifier
342
+ var protocolLength = 'SMARTLEDGER.ATTEST'.length
343
+ var protocol = data.slice(offset, offset + protocolLength).toString('utf8')
344
+ offset += protocolLength
345
+
346
+ if (protocol !== 'SMARTLEDGER.ATTEST') {
347
+ throw new Error('Invalid protocol identifier')
348
+ }
349
+
350
+ // Parse version
351
+ var version = data[offset]
352
+ offset += 1
353
+
354
+ // Parse type
355
+ var type = data.slice(offset, offset + 10).toString('utf8').replace(/\0/g, '')
356
+ offset += 10
357
+
358
+ // Parse timestamp
359
+ var timestamp = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]
360
+ offset += 4
361
+
362
+ // Parse hash
363
+ var hash = data.slice(offset, offset + 32)
364
+ offset += 32
365
+
366
+ // Parse metadata
367
+ var metadataBuffer = data.slice(offset)
368
+ var metadata = {}
369
+
370
+ try {
371
+ metadata = JSON.parse(metadataBuffer.toString('utf8'))
372
+ } catch (e) {
373
+ // Ignore JSON parse errors
374
+ }
375
+
376
+ return {
377
+ protocol: protocol,
378
+ version: version,
379
+ type: type,
380
+ timestamp: new Date(timestamp * 1000),
381
+ hash: hash.toString('hex'),
382
+ metadata: metadata
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Verify anchor proof
388
+ * @param {String} txid - Transaction ID
389
+ * @param {String} expectedHash - Expected anchored hash
390
+ * @returns {Promise<Object>} Verification result
391
+ */
392
+ SmartLedgerAnchor.verifyAnchor = async function(txid, expectedHash) {
393
+ // Note: In production, this would query blockchain APIs
394
+ // For now, return a mock verification result
395
+
396
+ return {
397
+ verified: true,
398
+ txid: txid,
399
+ hash: expectedHash,
400
+ timestamp: new Date().toISOString(),
401
+ confirmations: 6, // Mock confirmations
402
+ blockHeight: 800000, // Mock block height
403
+ proof: {
404
+ type: 'blockchain_anchor',
405
+ protocol: 'SMARTLEDGER.ATTEST',
406
+ network: 'mainnet'
407
+ }
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Get anchoring cost estimate
413
+ * @param {Number} numHashes - Number of hashes to anchor
414
+ * @param {Object} options - Cost estimation options
415
+ * @returns {Object} Cost estimate
416
+ */
417
+ SmartLedgerAnchor.getCostEstimate = function(numHashes, options) {
418
+ options = options || {}
419
+
420
+ var feeRate = options.feeRate || 1 // satoshis per byte
421
+ var baseSize = 200 // Base transaction size
422
+ var perHashSize = 32 // Bytes per hash in batch
423
+
424
+ var individualCost = numHashes * (baseSize + perHashSize) * feeRate
425
+ var batchCost = (baseSize + (numHashes * perHashSize)) * feeRate
426
+
427
+ return {
428
+ individual: {
429
+ totalCost: individualCost,
430
+ costPerHash: baseSize * feeRate,
431
+ transactions: numHashes
432
+ },
433
+ batch: {
434
+ totalCost: batchCost,
435
+ costPerHash: Math.ceil(batchCost / numHashes),
436
+ transactions: 1
437
+ },
438
+ savings: {
439
+ absolute: individualCost - batchCost,
440
+ percentage: Math.round(((individualCost - batchCost) / individualCost) * 100)
441
+ }
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Create UTXO from transaction output
447
+ * @param {String} txid - Transaction ID
448
+ * @param {Number} outputIndex - Output index
449
+ * @param {Number} satoshis - Output value
450
+ * @param {Script} script - Output script
451
+ * @returns {Object} UTXO object
452
+ */
453
+ SmartLedgerAnchor.createUTXO = function(txid, outputIndex, satoshis, script) {
454
+ return {
455
+ txid: txid,
456
+ outputIndex: outputIndex,
457
+ satoshis: satoshis,
458
+ script: script
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Generate test anchor data
464
+ * @returns {Object} Test anchor data
465
+ */
466
+ SmartLedgerAnchor.generateTestData = function() {
467
+ var privateKey = new PrivateKey()
468
+ var anchor = new SmartLedgerAnchor(privateKey)
469
+
470
+ var credentialHash = Hash.sha256(Buffer.from('test credential data'))
471
+ var metadata = {
472
+ issuer: 'did:smartledger:test',
473
+ type: 'EmailVerifiedCredential',
474
+ environment: 'test'
475
+ }
476
+
477
+ return {
478
+ privateKey: privateKey.toWIF(),
479
+ address: privateKey.toAddress().toString(),
480
+ credentialHash: credentialHash.toString('hex'),
481
+ metadata: metadata,
482
+ anchor: anchor
483
+ }
484
+ }
485
+
486
+ module.exports = SmartLedgerAnchor