dkg 8.0.0a3__py3-none-any.whl → 8.0.1__py3-none-any.whl

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 (72) hide show
  1. dkg/__init__.py +1 -1
  2. dkg/assertion.py +2 -2
  3. dkg/clients/__init__.py +4 -0
  4. dkg/clients/async_dkg.py +109 -0
  5. dkg/{main.py → clients/dkg.py} +42 -21
  6. dkg/constants.py +117 -6
  7. dkg/data/interfaces/AskStorage.json +366 -0
  8. dkg/data/interfaces/Chronos.json +202 -0
  9. dkg/data/interfaces/Hub.json +294 -2
  10. dkg/data/interfaces/IdentityStorage.json +58 -0
  11. dkg/data/interfaces/{ContentAsset.json → KnowledgeCollection.json} +256 -343
  12. dkg/data/interfaces/KnowledgeCollectionStorage.json +2312 -0
  13. dkg/data/interfaces/Paranet.json +30 -214
  14. dkg/data/interfaces/ParanetIncentivesPoolFactory.json +18 -2
  15. dkg/data/interfaces/ParanetKnowledgeMinersRegistry.json +20 -4
  16. dkg/data/interfaces/{ParanetNeurowebIncentivesPool.json → ParanetNeuroIncentivesPool.json} +7 -7
  17. dkg/data/interfaces/ParanetsRegistry.json +102 -32
  18. dkg/data/interfaces/Token.json +146 -17
  19. dkg/managers/__init__.py +0 -0
  20. dkg/managers/async_manager.py +69 -0
  21. dkg/{manager.py → managers/manager.py} +5 -3
  22. dkg/method.py +5 -2
  23. dkg/modules/__init__.py +0 -0
  24. dkg/modules/asset/__init__.py +0 -0
  25. dkg/modules/asset/asset.py +739 -0
  26. dkg/modules/asset/async_asset.py +751 -0
  27. dkg/modules/async_module.py +66 -0
  28. dkg/modules/graph/__init__.py +0 -0
  29. dkg/modules/graph/async_graph.py +118 -0
  30. dkg/modules/graph/graph.py +94 -0
  31. dkg/{module.py → modules/module.py} +1 -1
  32. dkg/modules/network/__init__.py +0 -0
  33. dkg/{network.py → modules/network/network.py} +4 -4
  34. dkg/modules/node/__init__.py +0 -0
  35. dkg/modules/node/async_node.py +39 -0
  36. dkg/{node.py → modules/node/node.py} +2 -2
  37. dkg/modules/paranet/__init__.py +0 -0
  38. dkg/{paranet.py → modules/paranet/paranet.py} +2 -2
  39. dkg/providers/__init__.py +9 -2
  40. dkg/providers/blockchain/__init__.py +4 -0
  41. dkg/providers/blockchain/async_blockchain.py +245 -0
  42. dkg/providers/blockchain/base_blockchain.py +102 -0
  43. dkg/providers/{blockchain.py → blockchain/blockchain.py} +15 -96
  44. dkg/providers/node/__init__.py +4 -0
  45. dkg/providers/node/async_node_http.py +72 -0
  46. dkg/providers/node/base_node_http.py +25 -0
  47. dkg/providers/{node_http.py → node/node_http.py} +12 -10
  48. dkg/services/__init__.py +0 -0
  49. dkg/services/blockchain_services/__init__.py +0 -0
  50. dkg/services/blockchain_services/async_blockchain_service.py +180 -0
  51. dkg/services/blockchain_services/blockchain_service.py +174 -0
  52. dkg/services/input_service.py +183 -0
  53. dkg/services/node_services/__init__.py +0 -0
  54. dkg/services/node_services/async_node_service.py +184 -0
  55. dkg/services/node_services/node_service.py +167 -0
  56. dkg/types/__init__.py +11 -11
  57. dkg/utils/blockchain_request.py +68 -42
  58. dkg/utils/knowledge_asset_tools.py +5 -0
  59. dkg/utils/knowledge_collection_tools.py +248 -0
  60. dkg/utils/node_request.py +59 -13
  61. dkg/utils/rdf.py +9 -3
  62. {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/METADATA +28 -19
  63. dkg-8.0.1.dist-info/RECORD +82 -0
  64. {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/WHEEL +1 -1
  65. dkg/asset.py +0 -912
  66. dkg/data/interfaces/AssertionStorage.json +0 -229
  67. dkg/data/interfaces/ContentAssetStorage.json +0 -706
  68. dkg/data/interfaces/ServiceAgreementStorageProxy.json +0 -1314
  69. dkg/graph.py +0 -63
  70. dkg-8.0.0a3.dist-info/RECORD +0 -52
  71. {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/LICENSE +0 -0
  72. {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/NOTICE +0 -0
dkg/asset.py DELETED
@@ -1,912 +0,0 @@
1
- # Licensed to the Apache Software Foundation (ASF) under one
2
- # or more contributor license agreements. See the NOTICE file
3
- # distributed with this work for additional information
4
- # regarding copyright ownership. The ASF licenses this file
5
- # to you under the Apache License, Version 2.0 (the
6
- # "License"); you may not use this file except in compliance
7
- # with the License. You may obtain a copy of the License at
8
-
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
-
11
- # Unless required by applicable law or agreed to in writing,
12
- # software distributed under the License is distributed on an
13
- # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
- # KIND, either express or implied. See the License for the
15
- # specific language governing permissions and limitations
16
- # under the License.
17
-
18
- import json
19
- import math
20
- import re
21
- from typing import Literal, Type
22
-
23
- from pyld import jsonld
24
- from web3 import Web3
25
- from web3.constants import ADDRESS_ZERO, HASH_ZERO
26
- from web3.exceptions import ContractLogicError
27
- from web3.types import TxReceipt
28
-
29
- from dkg.constants import (
30
- DEFAULT_HASH_FUNCTION_ID,
31
- DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS,
32
- PRIVATE_ASSERTION_PREDICATE,
33
- PRIVATE_CURRENT_REPOSITORY,
34
- PRIVATE_HISTORICAL_REPOSITORY,
35
- )
36
- from dkg.dataclasses import (
37
- BidSuggestionRange,
38
- KnowledgeAssetContentVisibility,
39
- KnowledgeAssetEnumStates,
40
- NodeResponseDict,
41
- )
42
- from dkg.exceptions import (
43
- DatasetOutputFormatNotSupported,
44
- InvalidKnowledgeAsset,
45
- InvalidStateOption,
46
- InvalidTokenAmount,
47
- MissingKnowledgeAssetState,
48
- OperationNotFinished,
49
- )
50
- from dkg.manager import DefaultRequestManager
51
- from dkg.method import Method
52
- from dkg.module import Module
53
- from dkg.types import JSONLD, UAL, Address, AgreementData, HexStr, Wei
54
- from dkg.utils.blockchain_request import BlockchainRequest
55
- from dkg.utils.decorators import retry
56
- from dkg.utils.merkle import MerkleTree, hash_assertion_with_indexes
57
- from dkg.utils.metadata import (
58
- generate_agreement_id,
59
- generate_assertion_metadata,
60
- generate_keyword,
61
- )
62
- from dkg.utils.node_request import (
63
- NodeRequest,
64
- OperationStatus,
65
- StoreTypes,
66
- validate_operation_status,
67
- )
68
- from dkg.utils.rdf import format_content, normalize_dataset
69
- from dkg.utils.ual import format_ual, parse_ual
70
-
71
-
72
- class KnowledgeAsset(Module):
73
- def __init__(self, manager: DefaultRequestManager):
74
- self.manager = manager
75
-
76
- _owner = Method(BlockchainRequest.owner_of)
77
-
78
- def is_valid_ual(self, ual: UAL) -> bool:
79
- if not ual or not isinstance(ual, str):
80
- raise ValueError("UAL must be a non-empty string.")
81
-
82
- parts = ual.split("/")
83
- if len(parts) != 3:
84
- raise ValueError("UAL format is incorrect.")
85
-
86
- prefixes = parts[0].split(":")
87
- prefixes_number = len(prefixes)
88
- if prefixes_number != 3 and prefixes_number != 4:
89
- raise ValueError("Prefix format in UAL is incorrect.")
90
-
91
- if prefixes[0] != "did":
92
- raise ValueError(
93
- f"Invalid DID prefix. Expected: 'did'. Received: '{prefixes[0]}'."
94
- )
95
-
96
- if prefixes[1] != "dkg":
97
- raise ValueError(
98
- f"Invalid DKG prefix. Expected: 'dkg'. Received: '{prefixes[1]}'."
99
- )
100
-
101
- if prefixes[2] != (
102
- blockchain_name := (
103
- self.manager.blockchain_provider.blockchain_id.split(":")[0]
104
- )
105
- ):
106
- raise ValueError(
107
- "Invalid blockchain name in the UAL prefix. "
108
- f"Expected: '{blockchain_name}'. Received: '${prefixes[2]}'."
109
- )
110
-
111
- if prefixes_number == 4:
112
- chain_id = self.manager.blockchain_provider.blockchain_id.split(":")[1]
113
-
114
- if int(prefixes[3]) != int(chain_id):
115
- raise ValueError(
116
- "Chain ID in UAL does not match the blockchain. "
117
- f"Expected: '${chain_id}'. Received: '${prefixes[3]}'."
118
- )
119
-
120
- contract_address = self.manager.blockchain_provider.contracts[
121
- "ContentAssetStorage"
122
- ].address
123
-
124
- if parts[1].lower() != contract_address.lower():
125
- raise ValueError(
126
- "Contract address in UAL does not match. "
127
- f"Expected: '${contract_address.lower()}'. "
128
- f"Received: '${parts[1].lower()}'."
129
- )
130
-
131
- try:
132
- owner = self._owner(int(parts[2]))
133
-
134
- if not owner or owner == ADDRESS_ZERO:
135
- raise ValueError("Token does not exist or has no owner.")
136
-
137
- return True
138
- except Exception as err:
139
- raise ValueError(f"Error fetching asset owner: {err}")
140
-
141
- _get_contract_address = Method(BlockchainRequest.get_contract_address)
142
- _get_current_allowance = Method(BlockchainRequest.allowance)
143
-
144
- def get_current_allowance(self, spender: Address | None = None) -> Wei:
145
- if spender is None:
146
- spender = self._get_contract_address("ServiceAgreementV1")
147
-
148
- return int(
149
- self._get_current_allowance(
150
- self.manager.blockchain_provider.account.address, spender
151
- )
152
- )
153
-
154
- _increase_allowance = Method(BlockchainRequest.increase_allowance)
155
- _decrease_allowance = Method(BlockchainRequest.decrease_allowance)
156
-
157
- def set_allowance(self, token_amount: Wei, spender: Address | None = None) -> Wei:
158
- if spender is None:
159
- spender = self._get_contract_address("ServiceAgreementV1")
160
-
161
- current_allowance = self.get_current_allowance(spender)
162
-
163
- allowance_difference = token_amount - current_allowance
164
-
165
- if allowance_difference > 0:
166
- self._increase_allowance(spender, allowance_difference)
167
- elif allowance_difference < 0:
168
- self._decrease_allowance(spender, -allowance_difference)
169
-
170
- return allowance_difference
171
-
172
- def increase_allowance(
173
- self, token_amount: Wei, spender: Address | None = None
174
- ) -> Wei:
175
- if spender is None:
176
- spender = self._get_contract_address("ServiceAgreementV1")
177
-
178
- self._increase_allowance(spender, token_amount)
179
-
180
- return token_amount
181
-
182
- def decrease_allowance(
183
- self, token_amount: Wei, spender: Address | None = None
184
- ) -> Wei:
185
- if spender is None:
186
- spender = self._get_contract_address("ServiceAgreementV1")
187
-
188
- current_allowance = self.get_current_allowance(spender)
189
- subtracted_value = min(token_amount, current_allowance)
190
-
191
- self._decrease_allowance(spender, subtracted_value)
192
-
193
- return subtracted_value
194
-
195
- _chain_id = Method(BlockchainRequest.chain_id)
196
-
197
- _get_asset_storage_address = Method(BlockchainRequest.get_asset_storage_address)
198
- _create = Method(BlockchainRequest.create_asset)
199
- _mint_paranet_knowledge_asset = Method(BlockchainRequest.mint_knowledge_asset)
200
-
201
- _get_bid_suggestion = Method(NodeRequest.bid_suggestion)
202
- _local_store = Method(NodeRequest.local_store)
203
- _publish = Method(NodeRequest.publish)
204
-
205
- def create(
206
- self,
207
- content: dict[Literal["public", "private"], JSONLD],
208
- epochs_number: int,
209
- token_amount: Wei | None = None,
210
- immutable: bool = False,
211
- content_type: Literal["JSON-LD", "N-Quads"] = "JSON-LD",
212
- paranet_ual: UAL | None = None,
213
- ) -> dict[str, UAL | HexStr | dict[str, dict[str, str] | TxReceipt]]:
214
- blockchain_id = self.manager.blockchain_provider.blockchain_id
215
- assertions = format_content(content, content_type)
216
-
217
- public_assertion_id = MerkleTree(
218
- hash_assertion_with_indexes(assertions["public"]),
219
- sort_pairs=True,
220
- ).root
221
- public_assertion_metadata = generate_assertion_metadata(assertions["public"])
222
-
223
- content_asset_storage_address = self._get_asset_storage_address(
224
- "ContentAssetStorage"
225
- )
226
-
227
- if token_amount is None:
228
- token_amount = int(
229
- self._get_bid_suggestion(
230
- blockchain_id,
231
- epochs_number,
232
- public_assertion_metadata["size"],
233
- content_asset_storage_address,
234
- public_assertion_id,
235
- DEFAULT_HASH_FUNCTION_ID,
236
- token_amount or BidSuggestionRange.LOW,
237
- )["bidSuggestion"]
238
- )
239
-
240
- current_allowance = self.get_current_allowance()
241
- if is_allowance_increased := current_allowance < token_amount:
242
- self.increase_allowance(token_amount)
243
-
244
- result = {"publicAssertionId": public_assertion_id, "operation": {}}
245
-
246
- try:
247
- if paranet_ual is None:
248
- receipt: TxReceipt = self._create(
249
- {
250
- "assertionId": Web3.to_bytes(hexstr=public_assertion_id),
251
- "size": public_assertion_metadata["size"],
252
- "triplesNumber": public_assertion_metadata["triples_number"],
253
- "chunksNumber": public_assertion_metadata["chunks_number"],
254
- "tokenAmount": token_amount,
255
- "epochsNumber": epochs_number,
256
- "scoreFunctionId": DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS[
257
- self.manager.blockchain_provider.environment
258
- ][blockchain_id],
259
- "immutable_": immutable,
260
- }
261
- )
262
- else:
263
- parsed_paranet_ual = parse_ual(paranet_ual)
264
- paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id = (
265
- parsed_paranet_ual["contract_address"],
266
- parsed_paranet_ual["token_id"],
267
- )
268
-
269
- receipt: TxReceipt = self._mint_paranet_knowledge_asset(
270
- paranet_knowledge_asset_storage,
271
- paranet_knowledge_asset_token_id,
272
- {
273
- "assertionId": Web3.to_bytes(hexstr=public_assertion_id),
274
- "size": public_assertion_metadata["size"],
275
- "triplesNumber": public_assertion_metadata["triples_number"],
276
- "chunksNumber": public_assertion_metadata["chunks_number"],
277
- "tokenAmount": token_amount,
278
- "epochsNumber": epochs_number,
279
- "scoreFunctionId": DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS[
280
- self.manager.blockchain_provider.environment
281
- ][blockchain_id],
282
- "immutable_": immutable,
283
- },
284
- )
285
-
286
- result["paranetId"] = Web3.to_hex(
287
- Web3.solidity_keccak(
288
- ["address", "uint256"],
289
- [
290
- paranet_knowledge_asset_storage,
291
- paranet_knowledge_asset_token_id,
292
- ],
293
- )
294
- )
295
- except ContractLogicError as err:
296
- if is_allowance_increased:
297
- self.decrease_allowance(token_amount)
298
- raise err
299
-
300
- events = self.manager.blockchain_provider.decode_logs_event(
301
- receipt,
302
- "ContentAsset",
303
- "AssetMinted",
304
- )
305
- token_id = events[0].args["tokenId"]
306
-
307
- result["UAL"] = format_ual(
308
- blockchain_id, content_asset_storage_address, token_id
309
- )
310
- result["operation"]["mintKnowledgeAsset"] = json.loads(Web3.to_json(receipt))
311
-
312
- assertions_list = [
313
- {
314
- "blockchain": blockchain_id,
315
- "contract": content_asset_storage_address,
316
- "tokenId": token_id,
317
- "assertionId": public_assertion_id,
318
- "assertion": assertions["public"],
319
- "storeType": StoreTypes.TRIPLE,
320
- }
321
- ]
322
-
323
- if content.get("private", None):
324
- assertions_list.append(
325
- {
326
- "blockchain": blockchain_id,
327
- "contract": content_asset_storage_address,
328
- "tokenId": token_id,
329
- "assertionId": MerkleTree(
330
- hash_assertion_with_indexes(assertions["private"]),
331
- sort_pairs=True,
332
- ).root,
333
- "assertion": assertions["private"],
334
- "storeType": StoreTypes.TRIPLE,
335
- }
336
- )
337
-
338
- operation_id = self._publish(
339
- public_assertion_id,
340
- assertions["public"],
341
- blockchain_id,
342
- content_asset_storage_address,
343
- token_id,
344
- DEFAULT_HASH_FUNCTION_ID,
345
- )["operationId"]
346
- operation_result = self.get_operation_result(operation_id, "publish")
347
-
348
- result["operation"]["publish"] = {
349
- "operationId": operation_id,
350
- "status": operation_result["status"],
351
- }
352
-
353
- if operation_result["status"] == OperationStatus.COMPLETED:
354
- operation_id = self._local_store(assertions_list)["operationId"]
355
- operation_result = self.get_operation_result(operation_id, "local-store")
356
-
357
- result["operation"]["localStore"] = {
358
- "operationId": operation_id,
359
- "status": operation_result["status"],
360
- }
361
-
362
- return result
363
-
364
-
365
- def local_store(
366
- self,
367
- content: dict[Literal["public", "private"], JSONLD],
368
- epochs_number: int,
369
- token_amount: Wei | None = None,
370
- immutable: bool = False,
371
- content_type: Literal["JSON-LD", "N-Quads"] = "JSON-LD",
372
- paranet_ual: UAL | None = None,
373
- ) -> dict[str, UAL | HexStr | dict[str, dict[str, str] | TxReceipt]]:
374
- blockchain_id = self.manager.blockchain_provider.blockchain_id
375
- assertions = format_content(content, content_type)
376
-
377
- public_assertion_id = MerkleTree(
378
- hash_assertion_with_indexes(assertions["public"]),
379
- sort_pairs=True,
380
- ).root
381
- public_assertion_metadata = generate_assertion_metadata(assertions["public"])
382
-
383
- content_asset_storage_address = self._get_asset_storage_address(
384
- "ContentAssetStorage"
385
- )
386
-
387
- if token_amount is None:
388
- token_amount = int(
389
- self._get_bid_suggestion(
390
- blockchain_id,
391
- epochs_number,
392
- public_assertion_metadata["size"],
393
- content_asset_storage_address,
394
- public_assertion_id,
395
- DEFAULT_HASH_FUNCTION_ID,
396
- token_amount or BidSuggestionRange.LOW,
397
- )["bidSuggestion"]
398
- )
399
-
400
- current_allowance = self.get_current_allowance()
401
- if is_allowance_increased := current_allowance < token_amount:
402
- self.increase_allowance(token_amount)
403
-
404
- result = {"publicAssertionId": public_assertion_id, "operation": {}}
405
-
406
- try:
407
- receipt: TxReceipt = self._create(
408
- {
409
- "assertionId": Web3.to_bytes(hexstr=public_assertion_id),
410
- "size": public_assertion_metadata["size"],
411
- "triplesNumber": public_assertion_metadata["triples_number"],
412
- "chunksNumber": public_assertion_metadata["chunks_number"],
413
- "tokenAmount": token_amount,
414
- "epochsNumber": epochs_number,
415
- "scoreFunctionId": DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS[
416
- self.manager.blockchain_provider.environment
417
- ][blockchain_id],
418
- "immutable_": immutable,
419
- }
420
- )
421
- except ContractLogicError as err:
422
- if is_allowance_increased:
423
- self.decrease_allowance(token_amount)
424
- raise err
425
-
426
- events = self.manager.blockchain_provider.decode_logs_event(
427
- receipt,
428
- "ContentAsset",
429
- "AssetMinted",
430
- )
431
- token_id = events[0].args["tokenId"]
432
-
433
- result["UAL"] = format_ual(
434
- blockchain_id, content_asset_storage_address, token_id
435
- )
436
- result["operation"]["mintKnowledgeAsset"] = json.loads(Web3.to_json(receipt))
437
-
438
- assertions_list = [
439
- {
440
- "blockchain": blockchain_id,
441
- "contract": content_asset_storage_address,
442
- "tokenId": token_id,
443
- "assertionId": public_assertion_id,
444
- "assertion": assertions["public"],
445
- "storeType": StoreTypes.TRIPLE_PARANET,
446
- "paranetUAL": paranet_ual,
447
- }
448
- ]
449
-
450
- if content.get("private", None):
451
- assertions_list.append(
452
- {
453
- "blockchain": blockchain_id,
454
- "contract": content_asset_storage_address,
455
- "tokenId": token_id,
456
- "assertionId": MerkleTree(
457
- hash_assertion_with_indexes(assertions["private"]),
458
- sort_pairs=True,
459
- ).root,
460
- "assertion": assertions["private"],
461
- "storeType": StoreTypes.TRIPLE_PARANET,
462
- "paranetUAL": paranet_ual,
463
- }
464
- )
465
-
466
- operation_id = self._local_store(assertions_list)["operationId"]
467
- operation_result = self.get_operation_result(operation_id, "local-store")
468
-
469
- result["operation"]["localStore"] = {
470
- "operationId": operation_id,
471
- "status": operation_result["status"],
472
- }
473
-
474
- if operation_result["status"] == OperationStatus.COMPLETED:
475
- parsed_paranet_ual = parse_ual(paranet_ual)
476
- paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id = (
477
- parsed_paranet_ual["contract_address"],
478
- parsed_paranet_ual["token_id"],
479
- )
480
-
481
- receipt: TxReceipt = self._submit_knowledge_asset(
482
- paranet_knowledge_asset_storage,
483
- paranet_knowledge_asset_token_id,
484
- content_asset_storage_address,
485
- token_id,
486
- )
487
-
488
- result["operation"]["submitToParanet"] = json.loads(Web3.to_json(receipt))
489
-
490
- return result
491
-
492
-
493
- _submit_knowledge_asset = Method(BlockchainRequest.submit_knowledge_asset)
494
-
495
- def submit_to_paranet(
496
- self, ual: UAL, paranet_ual: UAL
497
- ) -> dict[str, UAL | Address | TxReceipt]:
498
- parsed_ual = parse_ual(ual)
499
- knowledge_asset_storage, knowledge_asset_token_id = (
500
- parsed_ual["contract_address"],
501
- parsed_ual["token_id"],
502
- )
503
-
504
- parsed_paranet_ual = parse_ual(paranet_ual)
505
- paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id = (
506
- parsed_paranet_ual["contract_address"],
507
- parsed_paranet_ual["token_id"],
508
- )
509
-
510
- receipt: TxReceipt = self._submit_knowledge_asset(
511
- paranet_knowledge_asset_storage,
512
- paranet_knowledge_asset_token_id,
513
- knowledge_asset_storage,
514
- knowledge_asset_token_id,
515
- )
516
-
517
- return {
518
- "UAL": ual,
519
- "paranetUAL": paranet_ual,
520
- "paranetId": Web3.to_hex(
521
- Web3.solidity_keccak(
522
- ["address", "uint256"],
523
- [knowledge_asset_storage, knowledge_asset_token_id],
524
- )
525
- ),
526
- "operation": json.loads(Web3.to_json(receipt)),
527
- }
528
-
529
- _transfer = Method(BlockchainRequest.transfer_asset)
530
-
531
- def transfer(
532
- self,
533
- ual: UAL,
534
- new_owner: Address,
535
- ) -> dict[str, UAL | Address | TxReceipt]:
536
- token_id = parse_ual(ual)["token_id"]
537
-
538
- receipt: TxReceipt = self._transfer(
539
- self.manager.blockchain_provider.account,
540
- new_owner,
541
- token_id,
542
- )
543
-
544
- return {
545
- "UAL": ual,
546
- "owner": new_owner,
547
- "operation": json.loads(Web3.to_json(receipt)),
548
- }
549
-
550
- _burn_asset = Method(BlockchainRequest.burn_asset)
551
-
552
- def burn(self, ual: UAL) -> dict[str, UAL | TxReceipt]:
553
- token_id = parse_ual(ual)["token_id"]
554
-
555
- receipt: TxReceipt = self._burn_asset(token_id)
556
-
557
- return {"UAL": ual, "operation": json.loads(Web3.to_json(receipt))}
558
-
559
- _get_assertion_ids = Method(BlockchainRequest.get_assertion_ids)
560
- _get_latest_assertion_id = Method(BlockchainRequest.get_latest_assertion_id)
561
- _get_unfinalized_state = Method(BlockchainRequest.get_unfinalized_state)
562
-
563
- _get = Method(NodeRequest.get)
564
- _query = Method(NodeRequest.query)
565
-
566
- def get(
567
- self,
568
- ual: UAL,
569
- state: str | HexStr | int = KnowledgeAssetEnumStates.LATEST,
570
- content_visibility: str = KnowledgeAssetContentVisibility.ALL,
571
- output_format: Literal["JSON-LD", "N-Quads"] = "JSON-LD",
572
- validate: bool = True,
573
- ) -> dict[str, UAL | HexStr | list[JSONLD] | dict[str, str]]:
574
- state = (
575
- state.upper()
576
- if (isinstance(state, str) and not re.match(r"^0x[a-fA-F0-9]{64}$", state))
577
- else state
578
- )
579
- content_visibility = content_visibility.upper()
580
- output_format = output_format.upper()
581
-
582
- token_id = parse_ual(ual)["token_id"]
583
-
584
- def handle_latest_finalized_state(token_id: int) -> tuple[HexStr, bool]:
585
- return Web3.to_hex(self._get_latest_assertion_id(token_id)), True
586
-
587
- is_state_finalized = False
588
-
589
- match state:
590
- case KnowledgeAssetEnumStates.LATEST | KnowledgeAssetEnumStates.LATEST_FINALIZED:
591
- public_assertion_id, is_state_finalized = handle_latest_finalized_state(
592
- token_id
593
- )
594
-
595
- case _ if isinstance(state, int):
596
- assertion_ids = [
597
- Web3.to_hex(assertion_id)
598
- for assertion_id in self._get_assertion_ids(token_id)
599
- ]
600
- if 0 <= state < (states_number := len(assertion_ids)):
601
- public_assertion_id = assertion_ids[state]
602
-
603
- if state == states_number - 1:
604
- is_state_finalized = True
605
- else:
606
- raise InvalidStateOption(f"State index {state} is out of range.")
607
-
608
- case _ if isinstance(state, str) and re.match(
609
- r"^0x[a-fA-F0-9]{64}$", state
610
- ):
611
- assertion_ids = [
612
- Web3.to_hex(assertion_id)
613
- for assertion_id in self._get_assertion_ids(token_id)
614
- ]
615
-
616
- if state in assertion_ids:
617
- public_assertion_id = state
618
-
619
- if state == assertion_ids[-1]:
620
- is_state_finalized = True
621
- else:
622
- raise InvalidStateOption(
623
- f"Given state hash: {state} is not a part of the KA."
624
- )
625
-
626
- case _:
627
- raise InvalidStateOption(f"Invalid state option: {state}.")
628
-
629
- get_public_operation_id: NodeResponseDict = self._get(
630
- ual, public_assertion_id, hashFunctionId=1
631
- )["operationId"]
632
-
633
- get_public_operation_result = self.get_operation_result(
634
- get_public_operation_id, "get"
635
- )
636
- public_assertion = get_public_operation_result["data"].get("assertion", None)
637
-
638
- if public_assertion is None:
639
- raise MissingKnowledgeAssetState("Unable to find state on the network!")
640
-
641
- if validate:
642
- root = MerkleTree(
643
- hash_assertion_with_indexes(public_assertion), sort_pairs=True
644
- ).root
645
- if root != public_assertion_id:
646
- raise InvalidKnowledgeAsset(
647
- f"State: {public_assertion_id}. " f"Merkle Tree Root: {root}"
648
- )
649
-
650
- result = {"operation": {}}
651
- if content_visibility != KnowledgeAssetContentVisibility.PRIVATE:
652
- formatted_public_assertion = public_assertion
653
-
654
- match output_format:
655
- case "NQUADS" | "N-QUADS":
656
- formatted_public_assertion: list[JSONLD] = jsonld.from_rdf(
657
- "\n".join(public_assertion),
658
- {"algorithm": "URDNA2015", "format": "application/n-quads"},
659
- )
660
- case "JSONLD" | "JSON-LD":
661
- formatted_public_assertion = "\n".join(public_assertion)
662
-
663
- case _:
664
- raise DatasetOutputFormatNotSupported(
665
- f"{output_format} isn't supported!"
666
- )
667
-
668
- if content_visibility == KnowledgeAssetContentVisibility.PUBLIC:
669
- result = {
670
- **result,
671
- "asertion": formatted_public_assertion,
672
- "assertionId": public_assertion_id,
673
- }
674
- else:
675
- result["public"] = {
676
- "assertion": formatted_public_assertion,
677
- "assertionId": public_assertion_id,
678
- }
679
-
680
- result["operation"]["publicGet"] = {
681
- "operationId": get_public_operation_id,
682
- "status": get_public_operation_result["status"],
683
- }
684
-
685
- if content_visibility != KnowledgeAssetContentVisibility.PUBLIC:
686
- private_assertion_link_triples = list(
687
- filter(
688
- lambda element: PRIVATE_ASSERTION_PREDICATE in element,
689
- public_assertion,
690
- )
691
- )
692
-
693
- if private_assertion_link_triples:
694
- private_assertion_id = re.search(
695
- r'"(.*?)"', private_assertion_link_triples[0]
696
- ).group(1)
697
-
698
- private_assertion = get_public_operation_result["data"].get(
699
- "privateAssertion", None
700
- )
701
-
702
- query_private_operation_id: NodeResponseDict | None = None
703
- if private_assertion is None:
704
- query = f"""
705
- CONSTRUCT {{ ?s ?p ?o }}
706
- WHERE {{
707
- {{
708
- GRAPH <assertion:{private_assertion_id}>
709
- {{
710
- ?s ?p ?o .
711
- }}
712
- }}
713
- }}
714
- """
715
-
716
- query_private_operation_id = self._query(
717
- query,
718
- "CONSTRUCT",
719
- PRIVATE_CURRENT_REPOSITORY
720
- if is_state_finalized
721
- else PRIVATE_HISTORICAL_REPOSITORY,
722
- )["operationId"]
723
-
724
- query_private_operation_result = self.get_operation_result(
725
- query_private_operation_id, "query"
726
- )
727
-
728
- private_assertion = normalize_dataset(
729
- query_private_operation_result["data"],
730
- "N-Quads",
731
- )
732
-
733
- if validate:
734
- root = MerkleTree(
735
- hash_assertion_with_indexes(private_assertion),
736
- sort_pairs=True,
737
- ).root
738
- if root != private_assertion_id:
739
- raise InvalidKnowledgeAsset(
740
- f"State: {private_assertion_id}. "
741
- f"Merkle Tree Root: {root}"
742
- )
743
-
744
- match output_format:
745
- case "NQUADS" | "N-QUADS":
746
- formatted_private_assertion: list[JSONLD] = jsonld.from_rdf(
747
- "\n".join(private_assertion),
748
- {
749
- "algorithm": "URDNA2015",
750
- "format": "application/n-quads",
751
- },
752
- )
753
- case "JSONLD" | "JSON-LD":
754
- formatted_private_assertion = "\n".join(private_assertion)
755
-
756
- case _:
757
- raise DatasetOutputFormatNotSupported(
758
- f"{output_format} isn't supported!"
759
- )
760
-
761
- if content_visibility == KnowledgeAssetContentVisibility:
762
- result = {
763
- **result,
764
- "assertion": formatted_private_assertion,
765
- "assertionId": private_assertion_id,
766
- }
767
- else:
768
- result["private"] = {
769
- "assertion": formatted_private_assertion,
770
- "assertionId": private_assertion_id,
771
- }
772
-
773
- if query_private_operation_id is not None:
774
- result["operation"]["queryPrivate"] = {
775
- "operationId": query_private_operation_id,
776
- "status": query_private_operation_result["status"],
777
- }
778
-
779
- return result
780
-
781
- _extend_storing_period = Method(BlockchainRequest.extend_asset_storing_period)
782
-
783
- def extend_storing_period(
784
- self,
785
- ual: UAL,
786
- additional_epochs: int,
787
- token_amount: Wei | None = None,
788
- ) -> dict[str, UAL | TxReceipt]:
789
- parsed_ual = parse_ual(ual)
790
- blockchain_id, content_asset_storage_address, token_id = (
791
- parsed_ual["blockchain"],
792
- parsed_ual["contract_address"],
793
- parsed_ual["token_id"],
794
- )
795
-
796
- if token_amount is None:
797
- latest_finalized_state = self._get_latest_assertion_id(token_id)
798
- latest_finalized_state_size = self._get_assertion_size(
799
- latest_finalized_state
800
- )
801
-
802
- token_amount = int(
803
- self._get_bid_suggestion(
804
- blockchain_id,
805
- additional_epochs,
806
- latest_finalized_state_size,
807
- content_asset_storage_address,
808
- latest_finalized_state,
809
- DEFAULT_HASH_FUNCTION_ID,
810
- token_amount or BidSuggestionRange.LOW,
811
- )["bidSuggestion"]
812
- )
813
-
814
- receipt: TxReceipt = self._extend_storing_period(
815
- token_id, additional_epochs, token_amount
816
- )
817
-
818
- return {
819
- "UAL": ual,
820
- "operation": json.loads(Web3.to_json(receipt)),
821
- }
822
-
823
- _get_block = Method(BlockchainRequest.get_block)
824
-
825
- _get_service_agreement_data = Method(BlockchainRequest.get_service_agreement_data)
826
- _get_assertion_size = Method(BlockchainRequest.get_assertion_size)
827
- _add_tokens = Method(BlockchainRequest.increase_asset_token_amount)
828
-
829
- def add_tokens(
830
- self,
831
- ual: UAL,
832
- token_amount: Wei | None = None,
833
- ) -> dict[str, UAL | TxReceipt]:
834
- parsed_ual = parse_ual(ual)
835
- blockchain_id, content_asset_storage_address, token_id = (
836
- parsed_ual["blockchain"],
837
- parsed_ual["contract_address"],
838
- parsed_ual["token_id"],
839
- )
840
-
841
- if token_amount is None:
842
- agreement_id = self.get_agreement_id(
843
- content_asset_storage_address, token_id
844
- )
845
- # TODO: Dynamic types for namedtuples?
846
- agreement_data: Type[AgreementData] = self._get_service_agreement_data(
847
- agreement_id
848
- )
849
-
850
- timestamp_now = self._get_block("latest")["timestamp"]
851
- current_epoch = math.floor(
852
- (timestamp_now - agreement_data.startTime) / agreement_data.epochLength
853
- )
854
- epochs_left = agreement_data.epochsNumber - current_epoch
855
-
856
- latest_finalized_state = self._get_latest_assertion_id(token_id)
857
- latest_finalized_state_size = self._get_assertion_size(
858
- latest_finalized_state
859
- )
860
-
861
- token_amount = int(
862
- self._get_bid_suggestion(
863
- blockchain_id,
864
- epochs_left,
865
- latest_finalized_state_size,
866
- content_asset_storage_address,
867
- latest_finalized_state,
868
- DEFAULT_HASH_FUNCTION_ID,
869
- token_amount or BidSuggestionRange.LOW,
870
- )["bidSuggestion"]
871
- ) - sum(agreement_data.tokensInfo)
872
-
873
- if token_amount <= 0:
874
- raise InvalidTokenAmount(
875
- "Token amount is bigger than default suggested amount, "
876
- "please specify exact token_amount if you still want to add "
877
- "more tokens!"
878
- )
879
-
880
- receipt: TxReceipt = self._add_tokens(token_id, token_amount)
881
-
882
- return {
883
- "UAL": ual,
884
- "operation": json.loads(Web3.to_json(receipt)),
885
- }
886
-
887
- def get_owner(self, ual: UAL) -> Address:
888
- token_id = parse_ual(ual)["token_id"]
889
-
890
- return self._owner(token_id)
891
-
892
- _get_assertion_id_by_index = Method(BlockchainRequest.get_assertion_id_by_index)
893
-
894
- def get_agreement_id(self, contract_address: Address, token_id: int) -> HexStr:
895
- first_assertion_id = self._get_assertion_id_by_index(token_id, 0)
896
- keyword = generate_keyword(contract_address, first_assertion_id)
897
- return generate_agreement_id(contract_address, token_id, keyword)
898
-
899
- _get_operation_result = Method(NodeRequest.get_operation_result)
900
-
901
- @retry(catch=OperationNotFinished, max_retries=5, base_delay=1, backoff=2)
902
- def get_operation_result(
903
- self, operation_id: str, operation: str
904
- ) -> NodeResponseDict:
905
- operation_result = self._get_operation_result(
906
- operation_id=operation_id,
907
- operation=operation,
908
- )
909
-
910
- validate_operation_status(operation_result)
911
-
912
- return operation_result