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,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
|