dkg 8.0.0a3__py3-none-any.whl → 8.0.2__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 +753 -0
  27. dkg/modules/async_module.py +66 -0
  28. dkg/modules/graph/__init__.py +0 -0
  29. dkg/modules/graph/async_graph.py +112 -0
  30. dkg/modules/graph/graph.py +87 -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 -6
  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 +181 -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 +76 -50
  58. dkg/utils/knowledge_asset_tools.py +5 -0
  59. dkg/utils/knowledge_collection_tools.py +248 -0
  60. dkg/utils/node_request.py +60 -14
  61. dkg/utils/rdf.py +9 -3
  62. {dkg-8.0.0a3.dist-info → dkg-8.0.2.dist-info}/METADATA +28 -19
  63. dkg-8.0.2.dist-info/RECORD +82 -0
  64. {dkg-8.0.0a3.dist-info → dkg-8.0.2.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.2.dist-info}/LICENSE +0 -0
  72. {dkg-8.0.0a3.dist-info → dkg-8.0.2.dist-info}/NOTICE +0 -0
@@ -0,0 +1,174 @@
1
+ from dkg.modules.module import Module
2
+ from dkg.managers.manager import DefaultRequestManager
3
+ from dkg.utils.blockchain_request import BlockchainRequest
4
+ from dkg.method import Method
5
+ from dkg.constants import ZERO_ADDRESS
6
+ from web3 import Web3
7
+ from typing import Optional
8
+ from dkg.types import Address, UAL
9
+ from dkg.utils.blockchain_request import KnowledgeCollectionResult, AllowanceResult
10
+ from dkg.utils.ual import parse_ual
11
+
12
+
13
+ class BlockchainService(Module):
14
+ def __init__(self, manager: DefaultRequestManager):
15
+ self.manager = manager
16
+
17
+ _owner = Method(BlockchainRequest.owner_of)
18
+ _get_contract_address = Method(BlockchainRequest.get_contract_address)
19
+ _get_current_allowance = Method(BlockchainRequest.allowance)
20
+ _increase_allowance = Method(BlockchainRequest.increase_allowance)
21
+ _decrease_allowance = Method(BlockchainRequest.decrease_allowance)
22
+ _create_knowledge_collection = Method(BlockchainRequest.create_knowledge_collection)
23
+ _mint_knowledge_collection = Method(BlockchainRequest.mint_knowledge_collection)
24
+ _get_asset_storage_address = Method(BlockchainRequest.get_asset_storage_address)
25
+ _key_is_operational_wallet = Method(BlockchainRequest.key_is_operational_wallet)
26
+ _time_until_next_epoch = Method(BlockchainRequest.time_until_next_epoch)
27
+ _epoch_length = Method(BlockchainRequest.epoch_length)
28
+ _get_stake_weighted_average_ask = Method(
29
+ BlockchainRequest.get_stake_weighted_average_ask
30
+ )
31
+ _get_block = Method(BlockchainRequest.get_block)
32
+
33
+ def decrease_knowledge_collection_allowance(
34
+ self,
35
+ allowance_gap: int,
36
+ ):
37
+ knowledge_collection_address = self._get_contract_address("KnowledgeCollection")
38
+ self._decrease_allowance(knowledge_collection_address, allowance_gap)
39
+
40
+ def increase_knowledge_collection_allowance(
41
+ self,
42
+ sender: str,
43
+ token_amount: str,
44
+ ) -> AllowanceResult:
45
+ """
46
+ Increases the allowance for knowledge collection if necessary.
47
+
48
+ Args:
49
+ sender: The address of the sender
50
+ token_amount: The amount of tokens to check/increase allowance for
51
+
52
+ Returns:
53
+ AllowanceResult containing whether allowance was increased and the gap
54
+ """
55
+ knowledge_collection_address = self._get_contract_address("KnowledgeCollection")
56
+
57
+ allowance = self._get_current_allowance(sender, knowledge_collection_address)
58
+ allowance_gap = int(token_amount) - int(allowance)
59
+
60
+ if allowance_gap > 0:
61
+ self._increase_allowance(knowledge_collection_address, allowance_gap)
62
+
63
+ return AllowanceResult(
64
+ allowance_increased=True, allowance_gap=allowance_gap
65
+ )
66
+
67
+ return AllowanceResult(allowance_increased=False, allowance_gap=allowance_gap)
68
+
69
+ def create_knowledge_collection(
70
+ self,
71
+ request: dict,
72
+ paranet_ka_contract: Optional[Address] = None,
73
+ paranet_token_id: Optional[int] = None,
74
+ ) -> KnowledgeCollectionResult:
75
+ """
76
+ Creates a knowledge collection on the blockchain.
77
+
78
+ Args:
79
+ request: dict containing all collection parameters
80
+ paranet_ka_contract: Optional paranet contract address
81
+ paranet_token_id: Optional paranet token ID
82
+ blockchain: Blockchain configuration
83
+
84
+ Returns:
85
+ KnowledgeCollectionResult containing collection ID and transaction receipt
86
+
87
+ Raises:
88
+ BlockchainError: If the collection creation fails
89
+ """
90
+ sender = self.manager.blockchain_provider.account.address
91
+ allowance_increased = False
92
+ allowance_gap = 0
93
+
94
+ try:
95
+ # Handle allowance
96
+ if request.get("paymaster") and request.get("paymaster") != ZERO_ADDRESS:
97
+ pass
98
+ else:
99
+ allowance_result = self.increase_knowledge_collection_allowance(
100
+ sender=sender,
101
+ token_amount=request.get("tokenAmount"),
102
+ )
103
+ allowance_increased = allowance_result.allowance_increased
104
+ allowance_gap = allowance_result.allowance_gap
105
+
106
+ if not paranet_ka_contract and not paranet_token_id:
107
+ receipt = self._create_knowledge_collection(
108
+ request.get("publishOperationId"),
109
+ Web3.to_bytes(hexstr=request.get("merkleRoot")),
110
+ request.get("knowledgeAssetsAmount"),
111
+ request.get("byteSize"),
112
+ request.get("epochs"),
113
+ request.get("tokenAmount"),
114
+ request.get("isImmutable"),
115
+ request.get("paymaster"),
116
+ request.get("publisherNodeIdentityId"),
117
+ Web3.to_bytes(hexstr=request.get("publisherNodeR")),
118
+ Web3.to_bytes(hexstr=request.get("publisherNodeVS")),
119
+ request.get("identityIds"),
120
+ [Web3.to_bytes(hexstr=x) for x in request.get("r")],
121
+ [Web3.to_bytes(hexstr=x) for x in request.get("vs")],
122
+ )
123
+ else:
124
+ receipt = self._mint_knowledge_collection(
125
+ paranet_ka_contract,
126
+ paranet_token_id,
127
+ list(request.values()),
128
+ )
129
+
130
+ event_data = self.manager.blockchain_provider.decode_logs_event(
131
+ receipt=receipt,
132
+ contract_name="KnowledgeCollectionStorage",
133
+ event_name="KnowledgeCollectionCreated",
134
+ )
135
+ collection_id = (
136
+ int(getattr(event_data[0].get("args", {}), "id", None))
137
+ if event_data
138
+ else None
139
+ )
140
+
141
+ return KnowledgeCollectionResult(
142
+ knowledge_collection_id=collection_id, receipt=receipt
143
+ )
144
+
145
+ except Exception as e:
146
+ if allowance_increased:
147
+ self.decrease_knowledge_collection_allowance(allowance_gap)
148
+ raise e
149
+
150
+ # TODO: change self._owner to v8 compatible function
151
+ def get_owner(self, ual: UAL) -> Address:
152
+ token_id = parse_ual(ual)["token_id"]
153
+
154
+ return self._owner(token_id)
155
+
156
+ def get_asset_storage_address(self, asset_storage_name: str) -> Address:
157
+ return self._get_asset_storage_address(asset_storage_name)
158
+
159
+ def key_is_operational_wallet(
160
+ self, identity_id: int, key: Address, purpose: int
161
+ ) -> bool:
162
+ return self._key_is_operational_wallet(identity_id, key, purpose)
163
+
164
+ def time_until_next_epoch(self) -> int:
165
+ return self._time_until_next_epoch()
166
+
167
+ def epoch_length(self) -> int:
168
+ return self._epoch_length()
169
+
170
+ def get_stake_weighted_average_ask(self) -> int:
171
+ return self._get_stake_weighted_average_ask()
172
+
173
+ def get_block(self, block_identifier: str | int):
174
+ return self._get_block(block_identifier)
@@ -0,0 +1,181 @@
1
+ from dkg.constants import (
2
+ DefaultParameters,
3
+ ZERO_ADDRESS,
4
+ DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS,
5
+ )
6
+
7
+
8
+ class InputService:
9
+ def __init__(self, manager, config):
10
+ self.manager = manager
11
+ self.config = config
12
+
13
+ def get_asset_get_arguments(self, options):
14
+ return {
15
+ "max_number_of_retries": self.get_max_number_of_retries(options),
16
+ "frequency": self.get_frequency(options),
17
+ "state": self.get_state(options),
18
+ "include_metadata": self.get_include_metadata(options),
19
+ "content_type": self.get_content_type(options),
20
+ "validate": self.get_validate(options),
21
+ "output_format": self.get_output_format(options),
22
+ "hash_function_id": self.get_hash_function_id(options),
23
+ "paranet_ual": self.get_paranet_ual(options),
24
+ "subject_ual": self.get_subject_ual(options),
25
+ }
26
+
27
+ def get_asset_create_arguments(self, options):
28
+ return {
29
+ "max_number_of_retries": self.get_max_number_of_retries(options),
30
+ "frequency": self.get_frequency(options),
31
+ "epochs_num": self.get_epochs_num(options),
32
+ "hash_function_id": self.get_hash_function_id(options),
33
+ "score_function_id": self.get_score_function_id(options),
34
+ "immutable": self.get_immutable(options),
35
+ "token_amount": self.get_token_amount(options),
36
+ "payer": self.get_payer(options),
37
+ "minimum_number_of_finalization_confirmations": self.get_minimum_number_of_finalization_confirmations(
38
+ options
39
+ ),
40
+ "minimum_number_of_node_replications": self.get_minimum_number_of_node_replications(
41
+ options
42
+ ),
43
+ }
44
+
45
+ def get_query_arguments(self, options):
46
+ return {
47
+ "paranet_ual": self.get_paranet_ual(options),
48
+ "repository": self.get_repository(options),
49
+ }
50
+
51
+ def get_publish_finality_arguments(self, options):
52
+ return {
53
+ "max_number_of_retries": self.get_max_number_of_retries(options),
54
+ "frequency": self.get_frequency(options),
55
+ "minimum_number_of_finalization_confirmations": self.get_minimum_number_of_finalization_confirmations(
56
+ options
57
+ ),
58
+ }
59
+
60
+ def get_max_number_of_retries(self, options):
61
+ return (
62
+ options.get("max_number_of_retries")
63
+ or self.config.get("max_number_of_retries")
64
+ or DefaultParameters.MAX_NUMBER_OF_RETRIES.value
65
+ )
66
+
67
+ def get_frequency(self, options):
68
+ return (
69
+ options.get("frequency")
70
+ or self.config.get("frequency")
71
+ or DefaultParameters.FREQUENCY.value
72
+ )
73
+
74
+ def get_state(self, options):
75
+ return (
76
+ options.get("state")
77
+ or self.config.get("state")
78
+ or DefaultParameters.STATE.value
79
+ )
80
+
81
+ def get_include_metadata(self, options):
82
+ return (
83
+ options.get("include_metadata")
84
+ or self.config.get("include_metadata")
85
+ or DefaultParameters.INCLUDE_METADATA.value
86
+ )
87
+
88
+ def get_content_type(self, options):
89
+ return (
90
+ options.get("content_type")
91
+ or self.config.get("content_type")
92
+ or DefaultParameters.CONTENT_TYPE.value
93
+ )
94
+
95
+ def get_validate(self, options):
96
+ return (
97
+ options.get("validate")
98
+ or self.config.get("validate")
99
+ or DefaultParameters.VALIDATE.value
100
+ )
101
+
102
+ def get_output_format(self, options):
103
+ return (
104
+ options.get("output_format")
105
+ or self.config.get("output_format")
106
+ or DefaultParameters.OUTPUT_FORMAT.value
107
+ )
108
+
109
+ def get_hash_function_id(self, options):
110
+ return (
111
+ options.get("hash_function_id")
112
+ or self.config.get("hash_function_id")
113
+ or DefaultParameters.HASH_FUNCTION_ID.value
114
+ )
115
+
116
+ def get_paranet_ual(self, options):
117
+ return (
118
+ options.get("paranet_ual")
119
+ or self.config.get("paranet_ual")
120
+ or DefaultParameters.PARANET_UAL.value
121
+ )
122
+
123
+ def get_subject_ual(self, options):
124
+ return (
125
+ options.get("subject_ual")
126
+ or self.config.get("subject_ual")
127
+ or DefaultParameters.GET_SUBJECT_UAL.value
128
+ )
129
+
130
+ def get_epochs_num(self, options):
131
+ return options.get("epochs_num") or self.config.get("epochs_num") or None
132
+
133
+ def get_immutable(self, options):
134
+ return (
135
+ options.get("immutable")
136
+ or self.config.get("immutable")
137
+ or DefaultParameters.IMMUTABLE.value
138
+ )
139
+
140
+ def get_token_amount(self, options):
141
+ return options.get("token_amount") or self.config.get("token_amount") or None
142
+
143
+ def get_payer(self, options):
144
+ return options.get("payer") or self.config.get("payer") or ZERO_ADDRESS
145
+
146
+ def get_minimum_number_of_finalization_confirmations(self, options):
147
+ return (
148
+ options.get("minimum_number_of_finalization_confirmations")
149
+ or self.config.get("minimum_number_of_finalization_confirmations")
150
+ or DefaultParameters.MIN_NUMBER_OF_FINALIZATION_CONFIRMATION.value
151
+ or None
152
+ )
153
+
154
+ def get_minimum_number_of_node_replications(self, options):
155
+ return (
156
+ options.get("minimum_number_of_node_replications")
157
+ or self.config.get("minimum_number_of_node_replications")
158
+ or None
159
+ )
160
+
161
+ def get_score_function_id(self, options):
162
+ environment = (
163
+ options.get("environment")
164
+ or self.config.get("environment")
165
+ or self.manager.blockchain_provider.environment
166
+ or DefaultParameters.ENVIRONMENT.value
167
+ )
168
+ blockchain_name = (
169
+ options.get("blockchain")
170
+ or self.config.get("blockchain")
171
+ or self.manager.blockchain_provider.blockchain_id
172
+ )
173
+
174
+ return DEFAULT_PROXIMITY_SCORE_FUNCTIONS_PAIR_IDS[environment][blockchain_name]
175
+
176
+ def get_repository(self, options):
177
+ return (
178
+ options.get("repository")
179
+ or self.config.get("repository")
180
+ or DefaultParameters.REPOSITORY.value
181
+ )
File without changes
@@ -0,0 +1,184 @@
1
+ from dkg.managers.async_manager import AsyncRequestManager
2
+ from dkg.method import Method
3
+ from dkg.constants import OperationStatuses, ErrorType, Status
4
+ from dkg.utils.node_request import NodeRequest
5
+ from dkg.modules.async_module import AsyncModule
6
+ from typing import Dict, Any
7
+ from dkg.types import UAL
8
+ from dkg.dataclasses import NodeResponseDict
9
+ import asyncio
10
+
11
+
12
+ class AsyncNodeService(AsyncModule):
13
+ def __init__(self, manager: AsyncRequestManager):
14
+ self.manager = manager
15
+
16
+ _info = Method(NodeRequest.info)
17
+ _get_operation_result = Method(NodeRequest.get_operation_result)
18
+ _finality_status = Method(NodeRequest.finality_status)
19
+ _ask = Method(NodeRequest.ask)
20
+ _get_bid_suggestion = Method(NodeRequest.bid_suggestion)
21
+ _publish = Method(NodeRequest.publish)
22
+ _get = Method(NodeRequest.get)
23
+ _query = Method(NodeRequest.query)
24
+
25
+ async def info(self) -> NodeResponseDict:
26
+ return await self._info()
27
+
28
+ async def get_operation_result(
29
+ self,
30
+ operation_id: str,
31
+ operation: str,
32
+ max_retries: int,
33
+ frequency: int,
34
+ ) -> Dict[str, Any]:
35
+ response = {"status": OperationStatuses.PENDING.value}
36
+
37
+ retries = 0
38
+
39
+ while True:
40
+ if retries > max_retries:
41
+ response["data"] = {
42
+ "errorType": ErrorType.DKG_CLIENT_ERROR.value,
43
+ "errorMessage": "Unable to get results. Max number of retries reached.",
44
+ }
45
+ break
46
+
47
+ if retries > 0:
48
+ await asyncio.sleep(frequency)
49
+
50
+ retries += 1
51
+
52
+ try:
53
+ result = await self._get_operation_result(
54
+ operation=operation,
55
+ operation_id=operation_id,
56
+ )
57
+ response = {"data": result}
58
+ except Exception:
59
+ response = {"data": {"status": Status.NETWORK_ERROR.value}}
60
+
61
+ # Check completion conditions
62
+ if (
63
+ response["data"].get("status") == OperationStatuses.COMPLETED.value
64
+ or response["data"].get("status") == OperationStatuses.FAILED.value
65
+ ):
66
+ break
67
+
68
+ return response["data"]
69
+
70
+ async def finality_status(
71
+ self,
72
+ ual: UAL,
73
+ required_confirmations: int,
74
+ max_number_of_retries: int,
75
+ frequency: int,
76
+ ):
77
+ retries = 0
78
+ finality = 0
79
+
80
+ while finality < required_confirmations and retries <= max_number_of_retries:
81
+ if retries > max_number_of_retries:
82
+ raise Exception(
83
+ f"Unable to achieve required confirmations. "
84
+ f"Max number of retries ({max_number_of_retries}) reached."
85
+ )
86
+
87
+ # Sleep between attempts (except for first try)
88
+ if retries > 0:
89
+ await asyncio.sleep(frequency)
90
+
91
+ retries += 1
92
+
93
+ try:
94
+ response = await self._finality_status(ual)
95
+ finality = response.get("finality", 0)
96
+ except Exception:
97
+ finality = 0
98
+
99
+ return finality
100
+
101
+ async def ask(
102
+ self,
103
+ ual: UAL,
104
+ required_confirmations: int,
105
+ max_number_of_retries: int,
106
+ frequency: int,
107
+ ):
108
+ finality_id = 0
109
+ retries = 0
110
+
111
+ while finality_id < required_confirmations and retries < max_number_of_retries:
112
+ if retries > max_number_of_retries:
113
+ raise Exception(
114
+ f"Unable to achieve required confirmations. "
115
+ f"Max number of retries ({max_number_of_retries}) reached."
116
+ )
117
+
118
+ if retries > 0:
119
+ await asyncio.sleep(frequency)
120
+
121
+ retries += 1
122
+
123
+ try:
124
+ try:
125
+ response = await self._ask(
126
+ ual=ual, minimumNumberOfNodeReplications=required_confirmations
127
+ )
128
+ except Exception as e:
129
+ response = None
130
+ print(f"failed: {e}")
131
+
132
+ if response is not None:
133
+ operation_id = response.json().get("operationId", 0)
134
+ if operation_id >= required_confirmations:
135
+ finality_id = operation_id
136
+
137
+ except Exception as e:
138
+ finality_id = 0
139
+ print(f"Retry {retries + 1}/{max_number_of_retries} failed: {e}")
140
+
141
+ return finality_id
142
+
143
+ async def publish(
144
+ self,
145
+ dataset_root,
146
+ dataset,
147
+ blockchain_id,
148
+ hash_function_id,
149
+ minimum_number_of_node_replications,
150
+ ):
151
+ return await self._publish(
152
+ dataset_root,
153
+ dataset,
154
+ blockchain_id,
155
+ hash_function_id,
156
+ minimum_number_of_node_replications,
157
+ )
158
+
159
+ async def get(
160
+ self,
161
+ ual_with_state,
162
+ content_type,
163
+ include_metadata,
164
+ hash_function_id,
165
+ paranet_ual,
166
+ subject_ual,
167
+ ):
168
+ return await self._get(
169
+ ual_with_state,
170
+ content_type,
171
+ include_metadata,
172
+ hash_function_id,
173
+ paranet_ual,
174
+ subject_ual,
175
+ )
176
+
177
+ async def query(
178
+ self,
179
+ query,
180
+ query_type,
181
+ repository,
182
+ paranet_ual,
183
+ ):
184
+ return await self._query(query, query_type, repository, paranet_ual)
@@ -0,0 +1,167 @@
1
+ from dkg.managers.manager import DefaultRequestManager
2
+ from dkg.method import Method
3
+ from dkg.modules.module import Module
4
+ import time
5
+ from dkg.utils.decorators import retry
6
+ from dkg.exceptions import (
7
+ OperationNotFinished,
8
+ )
9
+ from dkg.utils.node_request import (
10
+ NodeRequest,
11
+ validate_operation_status,
12
+ )
13
+
14
+
15
+ class NodeService(Module):
16
+ def __init__(self, manager: DefaultRequestManager):
17
+ self.manager = manager
18
+
19
+ _get_operation_result = Method(NodeRequest.get_operation_result)
20
+ _finality_status = Method(NodeRequest.finality_status)
21
+ _ask = Method(NodeRequest.ask)
22
+ _publish = Method(NodeRequest.publish)
23
+ _get = Method(NodeRequest.get)
24
+ _query = Method(NodeRequest.query)
25
+
26
+ def get_operation_result(
27
+ self, operation_id: str, operation: str, max_retries: int, frequency: int
28
+ ):
29
+ @retry(
30
+ catch=OperationNotFinished,
31
+ max_retries=max_retries,
32
+ base_delay=frequency,
33
+ backoff=1,
34
+ )
35
+ def retry_get_operation_result():
36
+ operation_result = self._get_operation_result(
37
+ operation_id=operation_id,
38
+ operation=operation,
39
+ )
40
+ validate_operation_status(operation_result)
41
+
42
+ return operation_result
43
+
44
+ return retry_get_operation_result()
45
+
46
+ def finality_status(
47
+ self,
48
+ ual: str,
49
+ required_confirmations: int,
50
+ max_number_of_retries: int,
51
+ frequency: int,
52
+ ):
53
+ retries = 0
54
+ finality = 0
55
+
56
+ while finality < required_confirmations and retries <= max_number_of_retries:
57
+ if retries > max_number_of_retries:
58
+ raise Exception(
59
+ f"Unable to achieve required confirmations. "
60
+ f"Max number of retries ({max_number_of_retries}) reached."
61
+ )
62
+
63
+ if retries > 0:
64
+ time.sleep(frequency)
65
+
66
+ retries += 1
67
+
68
+ try:
69
+ try:
70
+ response = self._finality_status(ual=ual)
71
+ except Exception as e:
72
+ response = None
73
+ print(f"failed: {e}")
74
+
75
+ if response is not None:
76
+ finality = response.get("finality", 0)
77
+ if finality >= required_confirmations:
78
+ break
79
+
80
+ except Exception:
81
+ finality = 0
82
+
83
+ return finality
84
+
85
+ def ask(self, ual, required_confirmations, max_number_of_retries, frequency):
86
+ confirmations_count = 0
87
+ retries = 0
88
+
89
+ while (
90
+ confirmations_count < required_confirmations
91
+ and retries < max_number_of_retries
92
+ ):
93
+ if retries > max_number_of_retries:
94
+ raise Exception(
95
+ f"Unable to achieve required confirmations. "
96
+ f"Max number of retries ({max_number_of_retries}) reached."
97
+ )
98
+
99
+ if retries > 0:
100
+ time.sleep(frequency)
101
+
102
+ retries += 1
103
+
104
+ try:
105
+ try:
106
+ response = self._ask(
107
+ ual=ual, minimumNumberOfNodeReplications=required_confirmations
108
+ )
109
+ except Exception as e:
110
+ response = None
111
+ print(f"failed: {e}")
112
+
113
+ if response is not None:
114
+ number_of_confirmations = response.json().get(
115
+ "numberOfConfirmations", 0
116
+ )
117
+ if number_of_confirmations >= required_confirmations:
118
+ confirmations_count = number_of_confirmations
119
+
120
+ except Exception as e:
121
+ confirmations_count = 0
122
+ print(f"Retry {retries + 1}/{max_number_of_retries} failed: {e}")
123
+
124
+ return confirmations_count
125
+
126
+ def publish(
127
+ self,
128
+ dataset_root,
129
+ dataset,
130
+ blockchain_id,
131
+ hash_function_id,
132
+ minimum_number_of_node_replications,
133
+ ):
134
+ return self._publish(
135
+ dataset_root,
136
+ dataset,
137
+ blockchain_id,
138
+ hash_function_id,
139
+ minimum_number_of_node_replications,
140
+ )
141
+
142
+ def get(
143
+ self,
144
+ ual_with_state,
145
+ content_type,
146
+ include_metadata,
147
+ hash_function_id,
148
+ paranet_ual,
149
+ subject_ual,
150
+ ):
151
+ return self._get(
152
+ ual_with_state,
153
+ content_type,
154
+ include_metadata,
155
+ hash_function_id,
156
+ paranet_ual,
157
+ subject_ual,
158
+ )
159
+
160
+ def query(
161
+ self,
162
+ query,
163
+ query_type,
164
+ repository,
165
+ paranet_ual,
166
+ ):
167
+ return self._query(query, query_type, repository, paranet_ual)