dkg.js 6.0.0 → 6.0.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.
@@ -1,54 +1,97 @@
1
1
  const { assertionMetadata, formatAssertion, calculateRoot } = require('assertion-tools');
2
- const jsonld = require('jsonld');
2
+ const { ethers } = require('ethers');
3
3
  const {
4
4
  isEmptyObject,
5
5
  deriveUAL,
6
6
  getOperationStatusObject,
7
7
  resolveUAL,
8
+ toNQuads,
9
+ toJSONLD,
10
+ sleepForMilliseconds,
11
+ deriveRepository,
8
12
  } = require('../services/utilities.js');
9
13
  const {
14
+ CONTENT_TYPES,
10
15
  OPERATIONS,
11
- DEFAULT_HASH_FUNCTION_ID,
12
16
  OPERATIONS_STEP_STATUS,
13
17
  GET_OUTPUT_FORMATS,
14
18
  OPERATION_STATUSES,
15
19
  DEFAULT_GET_LOCAL_STORE_RESULT_FREQUENCY,
16
20
  PRIVATE_ASSERTION_PREDICATE,
21
+ QUERY_TYPES,
22
+ DEFAULT_PARAMETERS,
17
23
  } = require('../constants.js');
18
24
  const emptyHooks = require('../util/empty-hooks');
25
+ const { STORE_TYPES, ASSET_STATES } = require('../constants');
19
26
 
20
27
  class AssetOperationsManager {
21
28
  constructor(config, services) {
22
29
  this.nodeApiService = services.nodeApiService;
23
30
  this.validationService = services.validationService;
24
31
  this.blockchainService = services.blockchainService;
32
+ this.inputService = services.inputService;
25
33
  }
26
34
 
27
- async create(userContent, opts = {}, stepHooks = emptyHooks) {
28
- const options = JSON.parse(JSON.stringify(opts));
29
-
30
- this.validationService.validateContentType(userContent);
31
- let content = {};
35
+ /**
36
+ * Creates a new asset.
37
+ * @async
38
+ * @param {Object} content - The content of the asset to be created, contains public, private or both keys.
39
+ * @param {Object} [options={}] - Additional options for asset creation.
40
+ * @param {Object} [stepHooks=emptyHooks] - Hooks to execute during asset creation.
41
+ * @returns {Object} Object containing UAL, publicAssertionId and operation status.
42
+ */
43
+ async create(content, options = {}, stepHooks = emptyHooks) {
44
+ this.validationService.validateObjectType(content);
45
+ let jsonContent = {};
32
46
 
33
47
  // for backwards compatibility
34
- if (!userContent.public && !userContent.private) {
35
- content.public = userContent;
48
+ if (!content.public && !content.private) {
49
+ jsonContent.public = content;
36
50
  } else {
37
- content = userContent;
51
+ jsonContent = content;
38
52
  }
39
53
 
40
- this.validationService.validatePublishRequest(content, options);
54
+ const {
55
+ blockchain,
56
+ endpoint,
57
+ port,
58
+ maxNumberOfRetries,
59
+ frequency,
60
+ epochsNum,
61
+ hashFunctionId,
62
+ scoreFunctionId,
63
+ immutable,
64
+ tokenAmount,
65
+ authToken,
66
+ } = this.inputService.getAssetCreateArguments(options);
67
+
68
+ this.validationService.validateAssetCreate(
69
+ jsonContent,
70
+ blockchain,
71
+ endpoint,
72
+ port,
73
+ maxNumberOfRetries,
74
+ frequency,
75
+ epochsNum,
76
+ hashFunctionId,
77
+ scoreFunctionId,
78
+ immutable,
79
+ tokenAmount,
80
+ authToken,
81
+ );
41
82
 
42
83
  let privateAssertion;
43
84
  let privateAssertionId;
44
- if (content.private && !isEmptyObject(content.private)) {
45
- privateAssertion = await formatAssertion(content.private);
85
+ if (jsonContent.private && !isEmptyObject(jsonContent.private)) {
86
+ privateAssertion = await formatAssertion(jsonContent.private);
46
87
  privateAssertionId = calculateRoot(privateAssertion);
47
88
  }
48
89
  const publicGraph = {
49
90
  '@graph': [
50
- content.public && !isEmptyObject(content.public) ? content.public : null,
51
- content.private && !isEmptyObject(content.private)
91
+ jsonContent.public && !isEmptyObject(jsonContent.public)
92
+ ? jsonContent.public
93
+ : null,
94
+ jsonContent.private && !isEmptyObject(jsonContent.private)
52
95
  ? {
53
96
  [PRIVATE_ASSERTION_PREDICATE]: privateAssertionId,
54
97
  }
@@ -58,88 +101,112 @@ class AssetOperationsManager {
58
101
  const publicAssertion = await formatAssertion(publicGraph);
59
102
  const publicAssertionId = calculateRoot(publicAssertion);
60
103
 
61
- const blockchain = this.blockchainService.getBlockchain(options);
62
104
  const contentAssetStorageAddress = await this.blockchainService.getContractAddress(
63
- blockchain.name,
64
105
  'ContentAssetStorage',
65
- blockchain.rpc,
106
+ blockchain,
66
107
  );
108
+
67
109
  const tokenAmountInWei =
68
- options.tokenAmount ??
110
+ tokenAmount ??
69
111
  (await this.nodeApiService.getBidSuggestion(
112
+ endpoint,
113
+ port,
114
+ authToken,
70
115
  blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
71
- options.epochsNum,
116
+ epochsNum,
72
117
  assertionMetadata.getAssertionSizeInBytes(publicAssertion),
73
118
  contentAssetStorageAddress,
74
119
  publicAssertionId,
75
- options.hashFunctionId ?? DEFAULT_HASH_FUNCTION_ID,
76
- options,
120
+ hashFunctionId,
77
121
  ));
122
+
78
123
  const tokenId = await this.blockchainService.createAsset(
79
124
  {
80
125
  publicAssertionId,
81
126
  assertionSize: assertionMetadata.getAssertionSizeInBytes(publicAssertion),
82
127
  triplesNumber: assertionMetadata.getAssertionTriplesNumber(publicAssertion),
83
128
  chunksNumber: assertionMetadata.getAssertionChunksNumber(publicAssertion),
84
- epochsNum: options.epochsNum,
129
+ epochsNum,
85
130
  tokenAmount: tokenAmountInWei,
86
- scoreFunctionId: options.scoreFunctionId ?? 1,
87
- immutable_: options.immutable ?? false,
131
+ scoreFunctionId: scoreFunctionId ?? 1,
132
+ immutable_: immutable,
88
133
  },
89
- options,
134
+ blockchain,
90
135
  stepHooks,
91
136
  );
92
137
 
93
- const UAL = deriveUAL(blockchain.name, contentAssetStorageAddress, tokenId);
94
-
95
- let operationResult;
96
- let operationId;
138
+ const resolvedUAL = {
139
+ blockchain: blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
140
+ contract: contentAssetStorageAddress,
141
+ tokenId,
142
+ };
143
+ const assertions = [
144
+ {
145
+ ...resolvedUAL,
146
+ assertionId: publicAssertionId,
147
+ assertion: publicAssertion,
148
+ storeType: STORE_TYPES.TRIPLE,
149
+ },
150
+ ];
97
151
  if (privateAssertion?.length) {
98
- operationId = await this.nodeApiService.localStore(
99
- [
100
- {
101
- blockchain: blockchain.name,
102
- contract: contentAssetStorageAddress,
103
- tokenId,
104
- assertionId: publicAssertionId,
105
- assertion: publicAssertion,
106
- },
107
- {
108
- blockchain: blockchain.name,
109
- contract: contentAssetStorageAddress,
110
- tokenId,
111
- assertionId: privateAssertionId,
112
- assertion: privateAssertion,
113
- },
114
- ],
115
- options,
116
- );
117
- operationResult = await this.nodeApiService.getOperationResult(operationId, {
118
- ...options,
119
- operation: OPERATIONS.LOCAL_STORE,
120
- frequency: DEFAULT_GET_LOCAL_STORE_RESULT_FREQUENCY,
152
+ assertions.push({
153
+ ...resolvedUAL,
154
+ assertionId: privateAssertionId,
155
+ assertion: privateAssertion,
156
+ storeType: STORE_TYPES.TRIPLE,
121
157
  });
158
+ }
159
+ let operationId = await this.nodeApiService.localStore(
160
+ endpoint,
161
+ port,
162
+ authToken,
163
+ assertions,
164
+ );
165
+ let operationResult = await this.nodeApiService.getOperationResult(
166
+ endpoint,
167
+ port,
168
+ authToken,
169
+ OPERATIONS.LOCAL_STORE,
170
+ maxNumberOfRetries,
171
+ DEFAULT_GET_LOCAL_STORE_RESULT_FREQUENCY,
172
+ operationId,
173
+ );
122
174
 
123
- if (operationResult.status === OPERATION_STATUSES.FAILED) {
124
- return {
125
- UAL,
126
- assertionId: publicAssertionId,
127
- operation: getOperationStatusObject(operationResult, operationId),
128
- };
129
- }
175
+ const UAL = deriveUAL(
176
+ blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
177
+ contentAssetStorageAddress,
178
+ tokenId,
179
+ );
180
+
181
+ if (operationResult.status === OPERATION_STATUSES.FAILED) {
182
+ return {
183
+ UAL,
184
+ assertionId: publicAssertionId,
185
+ operation: getOperationStatusObject(operationResult, operationId),
186
+ };
130
187
  }
131
188
 
132
189
  operationId = await this.nodeApiService.publish(
190
+ endpoint,
191
+ port,
192
+ authToken,
133
193
  publicAssertionId,
134
194
  publicAssertion,
135
- UAL,
136
- options,
195
+ blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
196
+ contentAssetStorageAddress,
197
+ tokenId,
198
+ hashFunctionId,
137
199
  );
138
200
 
139
- operationResult = await this.nodeApiService.getOperationResult(operationId, {
140
- ...options,
141
- operation: OPERATIONS.PUBLISH,
142
- });
201
+ operationResult = await this.nodeApiService.getOperationResult(
202
+ endpoint,
203
+ port,
204
+ authToken,
205
+ OPERATIONS.PUBLISH,
206
+ maxNumberOfRetries,
207
+ frequency,
208
+ operationId,
209
+ );
143
210
 
144
211
  stepHooks.afterHook({
145
212
  status: OPERATIONS_STEP_STATUS.CREATE_ASSET_COMPLETED,
@@ -156,104 +223,473 @@ class AssetOperationsManager {
156
223
  };
157
224
  }
158
225
 
159
- async get(UAL, opts = {}) {
160
- const options = JSON.parse(JSON.stringify(opts));
161
- this.validationService.validateGetRequest(UAL, options);
226
+ /**
227
+ * Retrieves a public or private assertion for a given UAL.
228
+ * @async
229
+ * @param {string} UAL - The Universal Asset Locator
230
+ * @param {Object} [options={}] - Optional parameters for the asset get operation.
231
+ * @param {string} [options.state] - The state of the asset, "latest" or "finalized".
232
+ * @param {string} [options.contentType] - The type of content to retrieve, either "public", "private" or "all".
233
+ * @param {boolean} [options.validate] - Whether to validate the retrieved assertion.
234
+ * @param {string} [options.outputFormat] - The format of the retrieved assertion output, either "n-quads" or "json-ld".
235
+ * @returns {Object} - The result of the asset get operation.
236
+ */
237
+ async get(UAL, options = {}) {
238
+ const {
239
+ blockchain,
240
+ endpoint,
241
+ port,
242
+ maxNumberOfRetries,
243
+ frequency,
244
+ state,
245
+ contentType,
246
+ validate,
247
+ outputFormat,
248
+ authToken,
249
+ hashFunctionId,
250
+ } = this.inputService.getAssetGetArguments(options);
251
+
252
+ this.validationService.validateAssetGet(
253
+ UAL,
254
+ blockchain,
255
+ endpoint,
256
+ port,
257
+ maxNumberOfRetries,
258
+ frequency,
259
+ state,
260
+ contentType,
261
+ hashFunctionId,
262
+ validate,
263
+ outputFormat,
264
+ authToken,
265
+ );
266
+
162
267
  const { tokenId } = resolveUAL(UAL);
268
+ let hasPendingUpdate = false;
269
+ if (state === ASSET_STATES.LATEST) {
270
+ hasPendingUpdate = await this.blockchainService.hasPendingUpdate(tokenId, blockchain);
271
+ }
163
272
 
164
- const assertionId = await this.blockchainService.getLatestAssertionId(tokenId, options);
273
+ const publicAssertionId = hasPendingUpdate
274
+ ? await this.blockchainService.getUnfinalizedState(tokenId, blockchain)
275
+ : await this.blockchainService.getLatestAssertionId(tokenId, blockchain);
165
276
 
166
- const operationId = await this.nodeApiService.get(UAL, options);
167
- let operationResult = await this.nodeApiService.getOperationResult(operationId, {
168
- ...options,
169
- operation: OPERATIONS.GET,
170
- });
171
- let { assertion } = operationResult.data;
172
- if (assertion) {
277
+ const getPublicOperationId = await this.nodeApiService.get(
278
+ endpoint,
279
+ port,
280
+ authToken,
281
+ UAL,
282
+ state,
283
+ hashFunctionId,
284
+ );
285
+
286
+ const getPublicOperationResult = await this.nodeApiService.getOperationResult(
287
+ endpoint,
288
+ port,
289
+ authToken,
290
+ OPERATIONS.GET,
291
+ maxNumberOfRetries,
292
+ frequency,
293
+ getPublicOperationId,
294
+ );
295
+
296
+ if(!getPublicOperationResult.data.assertion) {
297
+ return {
298
+ errorType: 'DKG_CLIENT_ERROR',
299
+ errorMessage: "Unable to find assertion on the network!",
300
+ };
301
+ }
302
+
303
+ const publicAssertion = getPublicOperationResult.data.assertion;
304
+
305
+ if (validate === true && calculateRoot(publicAssertion) !== publicAssertionId) {
306
+ getPublicOperationResult.data = {
307
+ errorType: 'DKG_CLIENT_ERROR',
308
+ errorMessage: "Calculated root hashes don't match!",
309
+ };
310
+ }
311
+
312
+ let result = { operation: {} };
313
+ if (contentType !== CONTENT_TYPES.PRIVATE) {
314
+ let formattedPublicAssertion = publicAssertion;
173
315
  try {
174
- if (options.validate === true) {
175
- if (calculateRoot(assertion) !== assertionId)
176
- throw Error("Calculated root hashes don't match!");
177
- }
178
- if (options.outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) {
179
- assertion = await jsonld.fromRDF(assertion.join('\n'), {
180
- algorithm: 'URDNA2015',
181
- format: 'application/n-quads',
182
- });
316
+ if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) {
317
+ formattedPublicAssertion = await toJSONLD(publicAssertion.join('\n'));
318
+ } else {
319
+ formattedPublicAssertion = publicAssertion.join('\n');
183
320
  }
184
321
  } catch (error) {
185
- operationResult = {
186
- ...operationResult,
187
- data: {
322
+ getPublicOperationResult.data = {
323
+ errorType: 'DKG_CLIENT_ERROR',
324
+ errorMessage: error.message,
325
+ };
326
+ }
327
+
328
+ if (contentType === CONTENT_TYPES.PUBLIC) {
329
+ result = {
330
+ ...result,
331
+ assertion: formattedPublicAssertion,
332
+ assertionId: publicAssertionId,
333
+ };
334
+ } else {
335
+ result.public = {
336
+ assertion: formattedPublicAssertion,
337
+ assertionId: publicAssertionId,
338
+ };
339
+ }
340
+
341
+ result.operation.publicGet = getOperationStatusObject(
342
+ getPublicOperationResult,
343
+ getPublicOperationId,
344
+ );
345
+ }
346
+
347
+ if (contentType !== CONTENT_TYPES.PUBLIC) {
348
+ const privateAssertionLinkTriple = publicAssertion.filter((element) =>
349
+ element.includes(PRIVATE_ASSERTION_PREDICATE),
350
+ )[0];
351
+
352
+ let queryPrivateOperationId;
353
+ let queryPrivateOperationResult = {};
354
+ if (privateAssertionLinkTriple) {
355
+ const privateAssertionId = privateAssertionLinkTriple.match(/"(.*?)"/)[1];
356
+ let privateAssertion;
357
+ if (getPublicOperationResult?.data?.privateAssertion?.length)
358
+ privateAssertion = getPublicOperationResult.data.privateAssertion;
359
+ else {
360
+ const queryString = `
361
+ CONSTRUCT { ?s ?p ?o }
362
+ WHERE {
363
+ {
364
+ GRAPH <assertion:${privateAssertionId}>
365
+ {
366
+ ?s ?p ?o .
367
+ }
368
+ }
369
+ }`;
370
+
371
+ const repository = deriveRepository(
372
+ DEFAULT_PARAMETERS.GRAPH_LOCATION,
373
+ DEFAULT_PARAMETERS.GRAPH_STATE,
374
+ );
375
+ queryPrivateOperationId = await this.nodeApiService.query(
376
+ endpoint,
377
+ port,
378
+ authToken,
379
+ queryString,
380
+ QUERY_TYPES.CONSTRUCT,
381
+ repository
382
+ );
383
+
384
+ queryPrivateOperationResult = await this.nodeApiService.getOperationResult(
385
+ endpoint,
386
+ port,
387
+ authToken,
388
+ OPERATIONS.QUERY,
389
+ maxNumberOfRetries,
390
+ frequency,
391
+ queryPrivateOperationId,
392
+ );
393
+
394
+ const privateAssertionNQuads = queryPrivateOperationResult.data;
395
+
396
+ privateAssertion = await toNQuads(
397
+ privateAssertionNQuads,
398
+ 'application/n-quads',
399
+ );
400
+ }
401
+
402
+ let formattedPrivateAssertion;
403
+ if (
404
+ privateAssertion.length &&
405
+ validate === true &&
406
+ calculateRoot(privateAssertion) !== privateAssertionId
407
+ ) {
408
+ queryPrivateOperationResult.data = {
409
+ errorType: 'DKG_CLIENT_ERROR',
410
+ errorMessage: "Calculated root hashes don't match!",
411
+ };
412
+ }
413
+
414
+ try {
415
+ if (outputFormat !== GET_OUTPUT_FORMATS.N_QUADS) {
416
+ formattedPrivateAssertion = await toJSONLD(privateAssertion.join('\n'));
417
+ } else {
418
+ formattedPrivateAssertion = privateAssertion.join('\n');
419
+ }
420
+ } catch (error) {
421
+ queryPrivateOperationResult.data = {
188
422
  errorType: 'DKG_CLIENT_ERROR',
189
423
  errorMessage: error.message,
424
+ };
425
+ }
426
+
427
+ if (contentType === CONTENT_TYPES.PRIVATE) {
428
+ result = {
429
+ ...result,
430
+ assertion: formattedPrivateAssertion,
431
+ assertionId: privateAssertionId,
432
+ };
433
+ } else {
434
+ result.private = {
435
+ assertion: formattedPrivateAssertion,
436
+ assertionId: privateAssertionId,
437
+ };
438
+ }
439
+ if (queryPrivateOperationId) {
440
+ result.operation.queryPrivate = getOperationStatusObject(
441
+ queryPrivateOperationResult,
442
+ queryPrivateOperationId,
443
+ );
444
+ }
445
+ }
446
+ }
447
+
448
+ return result;
449
+ }
450
+
451
+ /**
452
+ * Updates an existing asset.
453
+ * @async
454
+ * @param {string} UAL - The Universal Asset Locator
455
+ * @param {Object} content - The content of the asset to be updated.
456
+ * @param {Object} [options={}] - Additional options for asset update.
457
+ * @returns {Object} Object containing UAL, publicAssertionId and operation status.
458
+ */
459
+ async update(UAL, content, options = {}) {
460
+ this.validationService.validateObjectType(content);
461
+ const jsonContent = content;
462
+
463
+ const {
464
+ blockchain,
465
+ endpoint,
466
+ port,
467
+ maxNumberOfRetries,
468
+ frequency,
469
+ hashFunctionId,
470
+ scoreFunctionId,
471
+ tokenAmount,
472
+ authToken,
473
+ } = this.inputService.getAssetUpdateArguments(options);
474
+
475
+ this.validationService.validateAssetUpdate(
476
+ jsonContent,
477
+ blockchain,
478
+ endpoint,
479
+ port,
480
+ maxNumberOfRetries,
481
+ frequency,
482
+ hashFunctionId,
483
+ scoreFunctionId,
484
+ tokenAmount,
485
+ authToken,
486
+ );
487
+
488
+ const { tokenId } = resolveUAL(UAL);
489
+
490
+ let privateAssertion;
491
+ let privateAssertionId;
492
+ if (jsonContent.private && !isEmptyObject(jsonContent.private)) {
493
+ privateAssertion = await formatAssertion(jsonContent.private);
494
+ privateAssertionId = calculateRoot(privateAssertion);
495
+ }
496
+ const publicGraph = {
497
+ '@graph': [
498
+ jsonContent.public && !isEmptyObject(jsonContent.public)
499
+ ? jsonContent.public
500
+ : null,
501
+ jsonContent.private && !isEmptyObject(jsonContent.private)
502
+ ? {
503
+ [PRIVATE_ASSERTION_PREDICATE]: privateAssertionId,
504
+ }
505
+ : null,
506
+ ],
507
+ };
508
+ const publicAssertion = await formatAssertion(publicGraph);
509
+ const publicAssertionId = calculateRoot(publicAssertion);
510
+
511
+ const contentAssetStorageAddress = await this.blockchainService.getContractAddress(
512
+ 'ContentAssetStorage',
513
+ blockchain,
514
+ );
515
+
516
+ let tokenAmountInWei;
517
+
518
+ if (tokenAmount != null) {
519
+ tokenAmountInWei = tokenAmount;
520
+ } else {
521
+ tokenAmountInWei = await this._getUpdateBidSuggestion(
522
+ UAL,
523
+ blockchain,
524
+ endpoint,
525
+ port,
526
+ authToken,
527
+ publicAssertionId,
528
+ assertionMetadata.getAssertionSizeInBytes(publicAssertion),
529
+ hashFunctionId,
530
+ );
531
+ }
532
+
533
+ await this.blockchainService.updateAsset(
534
+ tokenId,
535
+ publicAssertionId,
536
+ assertionMetadata.getAssertionSizeInBytes(publicAssertion),
537
+ assertionMetadata.getAssertionTriplesNumber(publicAssertion),
538
+ assertionMetadata.getAssertionChunksNumber(publicAssertion),
539
+ tokenAmountInWei,
540
+ blockchain,
541
+ );
542
+
543
+ const resolvedUAL = {
544
+ blockchain: blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
545
+ contract: contentAssetStorageAddress,
546
+ tokenId,
547
+ };
548
+
549
+ const assertions = [
550
+ {
551
+ ...resolvedUAL,
552
+ assertionId: publicAssertionId,
553
+ assertion: publicAssertion,
554
+ storeType: STORE_TYPES.PENDING,
555
+ },
556
+ ];
557
+
558
+ if (privateAssertion?.length) {
559
+ assertions.push({
560
+ ...resolvedUAL,
561
+ assertionId: privateAssertionId,
562
+ assertion: privateAssertion,
563
+ storeType: STORE_TYPES.PENDING,
564
+ });
565
+ }
566
+
567
+ let operationId = await this.nodeApiService.localStore(
568
+ endpoint,
569
+ port,
570
+ authToken,
571
+ assertions,
572
+ );
573
+
574
+ let operationResult = await this.nodeApiService.getOperationResult(
575
+ endpoint,
576
+ port,
577
+ authToken,
578
+ OPERATIONS.LOCAL_STORE,
579
+ maxNumberOfRetries,
580
+ DEFAULT_GET_LOCAL_STORE_RESULT_FREQUENCY,
581
+ operationId,
582
+ );
583
+
584
+ if (operationResult.status === OPERATION_STATUSES.FAILED) {
585
+ return {
586
+ UAL,
587
+ assertionId: publicAssertionId,
588
+ operation: getOperationStatusObject(operationResult, operationId),
589
+ };
590
+ }
591
+
592
+ operationId = await this.nodeApiService.update(
593
+ endpoint,
594
+ port,
595
+ authToken,
596
+ publicAssertionId,
597
+ publicAssertion,
598
+ blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
599
+ contentAssetStorageAddress,
600
+ tokenId,
601
+ hashFunctionId,
602
+ );
603
+ operationResult = await this.nodeApiService.getOperationResult(
604
+ endpoint,
605
+ port,
606
+ authToken,
607
+ OPERATIONS.UPDATE,
608
+ maxNumberOfRetries,
609
+ frequency,
610
+ operationId,
611
+ );
612
+ return {
613
+ UAL,
614
+ operation: getOperationStatusObject(operationResult, operationId),
615
+ };
616
+ }
617
+
618
+ async waitFinalization(UAL, options = {}) {
619
+ const blockchain = this.inputService.getBlockchain(options);
620
+ const frequency = this.inputService.getFrequency(options);
621
+ const maxNumberOfRetries = this.inputService.getMaxNumberOfRetries(options);
622
+
623
+ this.validationService.validateWaitAssetUpdateFinalization(UAL, blockchain);
624
+
625
+ const { tokenId } = resolveUAL(UAL);
626
+ const response = {
627
+ status: OPERATION_STATUSES.PENDING,
628
+ };
629
+ let pendingUpdate = true;
630
+ let retries = 0;
631
+ do {
632
+ if (retries > maxNumberOfRetries) {
633
+ response.data = {
634
+ ...response.data,
635
+ data: {
636
+ errorType: 'DKG_CLIENT_ERROR',
637
+ errorMessage: 'Unable to get results. Max number of retries reached.',
190
638
  },
191
639
  };
640
+ break;
192
641
  }
642
+ retries += 1;
643
+ // eslint-disable-next-line no-await-in-loop
644
+ await sleepForMilliseconds(frequency * 1000);
645
+ // eslint-disable-next-line no-await-in-loop
646
+ pendingUpdate = await this.blockchainService.hasPendingUpdate(tokenId, blockchain);
647
+ } while (pendingUpdate);
648
+ if (pendingUpdate) {
649
+ response.status = OPERATION_STATUSES.PENDING;
650
+ } else {
651
+ response.status = OPERATION_STATUSES.COMPLETED;
193
652
  }
194
653
 
195
654
  return {
196
- assertion,
197
- assertionId,
198
- operation: getOperationStatusObject(operationResult, operationId),
655
+ UAL,
656
+ operation: getOperationStatusObject(
657
+ { data: response.data, status: response.status },
658
+ null,
659
+ ),
199
660
  };
200
661
  }
201
662
 
202
- /* async update(UAL, content, opts = {}) {
203
- const options = JSON.parse(JSON.stringify(opts));
204
- this.validationService.validatePublishRequest(content, options);
205
-
206
- const assertion = await formatAssertion(content);
207
- const assertionId = calculateRoot(assertion);
208
- const tokenAmount =
209
- options.tokenAmount ??
210
- (await this.nodeApiService.getBidSuggestion(
211
- options.blockchain.name,
212
- options.epochsNum,
213
- assertionMetadata.getAssertionSizeInBytes(assertion),
214
- options.hashFunctionId ?? DEFAULT_HASH_FUNCTION_ID,
215
- options
216
- ));
217
-
218
- await this.blockchainService.updateAsset(
219
- Utilities.resolveUAL(UAL).tokenId,
220
- {
221
- assertionId,
222
- assertionSize: assertionMetadata.getAssertionSizeInBytes(assertion),
223
- triplesNumber: assertionMetadata.getAssertionTriplesNumber(assertion),
224
- chunksNumber: assertionMetadata.getAssertionChunksNumber(assertion),
225
- epochsNum: options.epochsNum,
226
- tokenAmount: tokenAmount,
227
- scoreFunctionId: options.scoreFunctionId ?? 1,
228
- },
229
- options
230
- );
231
- let operationId = await this.nodeApiService.publish(
232
- assertionId,
233
- assertion,
234
- UAL,
235
- options
236
- );
237
- let operationResult = await this.nodeApiService.getOperationResult(
238
- operationId,
239
- { ...options, operation: OPERATIONS.PUBLISH }
240
- );
241
- return {
242
- UAL,
243
- assertionId,
244
- operation: Utilities.getOperationStatusObject(
245
- operationResult,
246
- operationId
247
- ),
248
- };
249
- } */
250
-
251
- async transfer(UAL, to, opts = {}) {
252
- const options = JSON.parse(JSON.stringify(opts));
253
- this.validationService.validateAssetTransferRequest(UAL, to, options);
663
+ async cancelUpdate(UAL, options = {}) {
664
+ const blockchain = this.inputService.getBlockchain(options);
665
+
666
+ this.validationService.validateAssetUpdateCancel(UAL, blockchain);
667
+
668
+ const { tokenId } = resolveUAL(UAL);
669
+ await this.blockchainService.cancelAssetUpdate(tokenId, blockchain);
670
+
671
+ return {
672
+ UAL,
673
+ operation: getOperationStatusObject({ status: 'COMPLETED' }, null),
674
+ };
675
+ }
676
+
677
+ /**
678
+ * Transfer an asset to a new owner on a specified blockchain.
679
+ * @async
680
+ * @param {string} UAL - The Universal Asset Locator of the asset to be transferred.
681
+ * @param {string} newOwner - The address of the new owner.
682
+ * @param {Object} [options={}] - Additional options for asset transfer.
683
+ * @returns {Object} Object containing UAL, owner's address and operation status.
684
+ */
685
+ async transfer(UAL, newOwner, options = {}) {
686
+ const blockchain = this.inputService.getBlockchain(options);
687
+
688
+ this.validationService.validateAssetTransfer(UAL, newOwner, blockchain);
689
+
254
690
  const { tokenId } = resolveUAL(UAL);
255
- await this.blockchainService.transferAsset(tokenId, to, options);
256
- const owner = await this.blockchainService.getAssetOwner(tokenId, options);
691
+ await this.blockchainService.transferAsset(tokenId, newOwner, blockchain);
692
+ const owner = await this.blockchainService.getAssetOwner(tokenId, blockchain);
257
693
  return {
258
694
  UAL,
259
695
  owner,
@@ -261,16 +697,262 @@ class AssetOperationsManager {
261
697
  };
262
698
  }
263
699
 
264
- async getOwner(UAL, opts = {}) {
265
- const options = JSON.parse(JSON.stringify(opts));
266
- this.validationService.validateGetOwnerRequest(UAL);
700
+ /**
701
+ * Retrieves the owner of a specified asset for a given blockchain.
702
+ * @async
703
+ * @param {string} UAL - The Universal Asset Locator of the asset.
704
+ * @param {Object} [options={}] - Optional parameters for blockchain service.
705
+ * @returns {Object} An object containing the UAL, owner and operation status.
706
+ */
707
+ async getOwner(UAL, options = {}) {
708
+ const blockchain = this.inputService.getBlockchain(options);
709
+
710
+ this.validationService.validateAssetGetOwner(UAL, blockchain);
711
+
267
712
  const { tokenId } = resolveUAL(UAL);
268
- const owner = await this.blockchainService.getAssetOwner(tokenId, options);
713
+ const owner = await this.blockchainService.getAssetOwner(tokenId, blockchain);
269
714
  return {
270
715
  UAL,
271
716
  owner,
272
717
  operation: getOperationStatusObject({ data: {}, status: 'COMPLETED' }, null),
273
718
  };
274
719
  }
720
+
721
+ async burn(UAL, options = {}) {
722
+ const blockchain = this.inputService.getBlockchain(options);
723
+
724
+ this.validationService.validateAssetBurn(UAL, blockchain);
725
+
726
+ const { tokenId } = resolveUAL(UAL);
727
+ await this.blockchainService.burnAsset(tokenId, blockchain);
728
+
729
+ return {
730
+ UAL,
731
+ operation: getOperationStatusObject({ status: 'COMPLETED' }, null),
732
+ };
733
+ }
734
+
735
+ async extendStoringPeriod(UAL, epochsNumber, options = {}) {
736
+ const blockchain = this.inputService.getBlockchain(options);
737
+ const tokenAmount = this.inputService.getTokenAmount(options);
738
+
739
+ const { tokenId } = resolveUAL(UAL);
740
+
741
+ let tokenAmountInWei;
742
+
743
+ if (tokenAmount != null) {
744
+ tokenAmountInWei = tokenAmount;
745
+ } else {
746
+ const endpoint = this.inputService.getEndpoint(options);
747
+ const port = this.inputService.getPort(options);
748
+ const authToken = this.inputService.getAuthToken(options);
749
+ const hashFunctionId = this.inputService.getHashFunctionId(options);
750
+
751
+ const latestFinalizedState = await this.blockchainService.getLatestAssertionId(
752
+ tokenId,
753
+ blockchain,
754
+ );
755
+
756
+ const latestFinalizedStateSize = await this.blockchainService.getAssertionSize(
757
+ latestFinalizedState,
758
+ blockchain,
759
+ );
760
+
761
+ tokenAmountInWei = await this._getUpdateBidSuggestion(
762
+ UAL,
763
+ blockchain,
764
+ endpoint,
765
+ port,
766
+ authToken,
767
+ latestFinalizedState,
768
+ latestFinalizedStateSize,
769
+ hashFunctionId,
770
+ );
771
+
772
+ if (tokenAmountInWei < 0) {
773
+ tokenAmountInWei = 0;
774
+ }
775
+ }
776
+
777
+ this.validationService.validateExtendAssetStoringPeriod(
778
+ UAL,
779
+ epochsNumber,
780
+ tokenAmountInWei,
781
+ blockchain,
782
+ );
783
+
784
+ await this.blockchainService.extendAssetStoringPeriod(
785
+ tokenId,
786
+ epochsNumber,
787
+ tokenAmountInWei,
788
+ blockchain,
789
+ );
790
+
791
+ return {
792
+ UAL,
793
+ operation: getOperationStatusObject({ status: 'COMPLETED' }, null),
794
+ };
795
+ }
796
+
797
+ async addTokens(UAL, options = {}) {
798
+ const blockchain = this.inputService.getBlockchain(options);
799
+ const tokenAmount = this.inputService.getTokenAmount(options);
800
+
801
+ const { tokenId } = resolveUAL(UAL);
802
+
803
+ let tokenAmountInWei;
804
+
805
+ if (tokenAmount != null) {
806
+ tokenAmountInWei = tokenAmount;
807
+ } else {
808
+ const endpoint = this.inputService.getEndpoint(options);
809
+ const port = this.inputService.getPort(options);
810
+ const authToken = this.inputService.getAuthToken(options);
811
+ const hashFunctionId = this.inputService.getHashFunctionId(options);
812
+
813
+ const latestFinalizedState = await this.blockchainService.getLatestAssertionId(
814
+ tokenId,
815
+ blockchain,
816
+ );
817
+
818
+ const latestFinalizedStateSize = await this.blockchainService.getAssertionSize(
819
+ latestFinalizedState,
820
+ blockchain,
821
+ );
822
+
823
+ tokenAmountInWei = await this._getUpdateBidSuggestion(
824
+ UAL,
825
+ blockchain,
826
+ endpoint,
827
+ port,
828
+ authToken,
829
+ latestFinalizedState,
830
+ latestFinalizedStateSize,
831
+ hashFunctionId,
832
+ );
833
+
834
+ if (tokenAmountInWei <= 0) {
835
+ throw Error(
836
+ `Token amount is bigger than default suggested amount, please specify exact tokenAmount if you still want to add more tokens!`,
837
+ );
838
+ }
839
+ }
840
+
841
+ this.validationService.validateAddTokens(UAL, tokenAmountInWei, blockchain);
842
+
843
+ await this.blockchainService.addTokens(tokenId, tokenAmountInWei, blockchain);
844
+
845
+ return {
846
+ UAL,
847
+ operation: getOperationStatusObject({ status: 'COMPLETED' }, null),
848
+ };
849
+ }
850
+
851
+ async addUpdateTokens(UAL, options = {}) {
852
+ const blockchain = this.inputService.getBlockchain(options);
853
+ const tokenAmount = this.inputService.getTokenAmount(options);
854
+
855
+ const { tokenId } = resolveUAL(UAL);
856
+
857
+ let tokenAmountInWei;
858
+
859
+ if (tokenAmount != null) {
860
+ tokenAmountInWei = tokenAmount;
861
+ } else {
862
+ const endpoint = this.inputService.getEndpoint(options);
863
+ const port = this.inputService.getPort(options);
864
+ const authToken = this.inputService.getAuthToken(options);
865
+ const hashFunctionId = this.inputService.getHashFunctionId(options);
866
+
867
+ const unfinalizedState = await this.blockchainService.getUnfinalizedState(
868
+ tokenId,
869
+ blockchain,
870
+ );
871
+
872
+ const unfinalizedStateSize = await this.blockchainService.getAssertionSize(
873
+ unfinalizedState,
874
+ blockchain,
875
+ );
876
+
877
+ tokenAmountInWei = await this._getUpdateBidSuggestion(
878
+ UAL,
879
+ blockchain,
880
+ endpoint,
881
+ port,
882
+ authToken,
883
+ unfinalizedState,
884
+ unfinalizedStateSize,
885
+ hashFunctionId,
886
+ );
887
+ if (tokenAmountInWei <= 0) {
888
+ throw Error(
889
+ `Token amount is bigger than default suggested amount, please specify exact tokenAmount if you still want to add more tokens!`,
890
+ );
891
+ }
892
+ }
893
+
894
+ this.validationService.validateAddTokens(UAL, tokenAmountInWei, blockchain);
895
+
896
+ await this.blockchainService.addUpdateTokens(tokenId, tokenAmountInWei, blockchain);
897
+
898
+ return {
899
+ UAL,
900
+ operation: getOperationStatusObject({ status: 'COMPLETED' }, null),
901
+ };
902
+ }
903
+
904
+ async _getUpdateBidSuggestion(
905
+ UAL,
906
+ blockchain,
907
+ endpoint,
908
+ port,
909
+ authToken,
910
+ assertionId,
911
+ size,
912
+ hashFunctionId,
913
+ ) {
914
+ const { contract, tokenId } = resolveUAL(UAL);
915
+ const firstAssertionId = await this.blockchainService.getAssertionIdByIndex(
916
+ tokenId,
917
+ 0,
918
+ blockchain,
919
+ );
920
+
921
+ const keyword = ethers.solidityPacked(['address', 'bytes32'], [contract, firstAssertionId]);
922
+
923
+ const agreementId = ethers.sha256(
924
+ ethers.solidityPacked(['address', 'uint256', 'bytes'], [contract, tokenId, keyword]),
925
+ );
926
+ const agreementData = await this.blockchainService.getAgreementData(
927
+ agreementId,
928
+ blockchain,
929
+ );
930
+
931
+ const now = await this.blockchainService.getBlockchainTimestamp(blockchain);
932
+ const currentEpoch = Math.floor(
933
+ (now - agreementData.startTime) / agreementData.epochLength,
934
+ );
935
+
936
+ const epochsLeft = agreementData.epochsNumber - currentEpoch;
937
+
938
+ const bidSuggestion = await this.nodeApiService.getBidSuggestion(
939
+ endpoint,
940
+ port,
941
+ authToken,
942
+ blockchain.name.startsWith('otp') ? 'otp' : blockchain.name,
943
+ epochsLeft,
944
+ size,
945
+ contract,
946
+ assertionId,
947
+ hashFunctionId,
948
+ );
949
+
950
+ const tokenAmountInWei =
951
+ BigInt(bidSuggestion) -
952
+ (BigInt(agreementData.tokenAmount) + BigInt(agreementData.updateTokenAmount ?? 0));
953
+
954
+ return tokenAmountInWei > 0 ? tokenAmountInWei : 0;
955
+ }
275
956
  }
957
+
276
958
  module.exports = AssetOperationsManager;