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.
- dkg/__init__.py +1 -1
- dkg/assertion.py +2 -2
- dkg/clients/__init__.py +4 -0
- dkg/clients/async_dkg.py +109 -0
- dkg/{main.py → clients/dkg.py} +42 -21
- dkg/constants.py +117 -6
- dkg/data/interfaces/AskStorage.json +366 -0
- dkg/data/interfaces/Chronos.json +202 -0
- dkg/data/interfaces/Hub.json +294 -2
- dkg/data/interfaces/IdentityStorage.json +58 -0
- dkg/data/interfaces/{ContentAsset.json → KnowledgeCollection.json} +256 -343
- dkg/data/interfaces/KnowledgeCollectionStorage.json +2312 -0
- dkg/data/interfaces/Paranet.json +30 -214
- dkg/data/interfaces/ParanetIncentivesPoolFactory.json +18 -2
- dkg/data/interfaces/ParanetKnowledgeMinersRegistry.json +20 -4
- dkg/data/interfaces/{ParanetNeurowebIncentivesPool.json → ParanetNeuroIncentivesPool.json} +7 -7
- dkg/data/interfaces/ParanetsRegistry.json +102 -32
- dkg/data/interfaces/Token.json +146 -17
- dkg/managers/__init__.py +0 -0
- dkg/managers/async_manager.py +69 -0
- dkg/{manager.py → managers/manager.py} +5 -3
- dkg/method.py +5 -2
- dkg/modules/__init__.py +0 -0
- dkg/modules/asset/__init__.py +0 -0
- dkg/modules/asset/asset.py +739 -0
- dkg/modules/asset/async_asset.py +751 -0
- dkg/modules/async_module.py +66 -0
- dkg/modules/graph/__init__.py +0 -0
- dkg/modules/graph/async_graph.py +118 -0
- dkg/modules/graph/graph.py +94 -0
- dkg/{module.py → modules/module.py} +1 -1
- dkg/modules/network/__init__.py +0 -0
- dkg/{network.py → modules/network/network.py} +4 -4
- dkg/modules/node/__init__.py +0 -0
- dkg/modules/node/async_node.py +39 -0
- dkg/{node.py → modules/node/node.py} +2 -2
- dkg/modules/paranet/__init__.py +0 -0
- dkg/{paranet.py → modules/paranet/paranet.py} +2 -2
- dkg/providers/__init__.py +9 -2
- dkg/providers/blockchain/__init__.py +4 -0
- dkg/providers/blockchain/async_blockchain.py +245 -0
- dkg/providers/blockchain/base_blockchain.py +102 -0
- dkg/providers/{blockchain.py → blockchain/blockchain.py} +15 -96
- dkg/providers/node/__init__.py +4 -0
- dkg/providers/node/async_node_http.py +72 -0
- dkg/providers/node/base_node_http.py +25 -0
- dkg/providers/{node_http.py → node/node_http.py} +12 -10
- dkg/services/__init__.py +0 -0
- dkg/services/blockchain_services/__init__.py +0 -0
- dkg/services/blockchain_services/async_blockchain_service.py +180 -0
- dkg/services/blockchain_services/blockchain_service.py +174 -0
- dkg/services/input_service.py +183 -0
- dkg/services/node_services/__init__.py +0 -0
- dkg/services/node_services/async_node_service.py +184 -0
- dkg/services/node_services/node_service.py +167 -0
- dkg/types/__init__.py +11 -11
- dkg/utils/blockchain_request.py +68 -42
- dkg/utils/knowledge_asset_tools.py +5 -0
- dkg/utils/knowledge_collection_tools.py +248 -0
- dkg/utils/node_request.py +59 -13
- dkg/utils/rdf.py +9 -3
- {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/METADATA +28 -19
- dkg-8.0.1.dist-info/RECORD +82 -0
- {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/WHEEL +1 -1
- dkg/asset.py +0 -912
- dkg/data/interfaces/AssertionStorage.json +0 -229
- dkg/data/interfaces/ContentAssetStorage.json +0 -706
- dkg/data/interfaces/ServiceAgreementStorageProxy.json +0 -1314
- dkg/graph.py +0 -63
- dkg-8.0.0a3.dist-info/RECORD +0 -52
- {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/LICENSE +0 -0
- {dkg-8.0.0a3.dist-info → dkg-8.0.1.dist-info}/NOTICE +0 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
import json
|
2
|
+
from collections import namedtuple
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any, Type
|
5
|
+
|
6
|
+
from dkg.constants import BLOCKCHAINS
|
7
|
+
from dkg.exceptions import (
|
8
|
+
EnvironmentNotSupported,
|
9
|
+
RPCURINotDefined,
|
10
|
+
)
|
11
|
+
from dkg.types import URI, DataHexStr, Environment, Wei
|
12
|
+
from eth_account.signers.local import LocalAccount
|
13
|
+
from eth_typing import ABI, ABIFunction
|
14
|
+
from web3.logs import DISCARD
|
15
|
+
from web3.middleware import SignAndSendRawMiddlewareBuilder
|
16
|
+
from web3.types import TxReceipt
|
17
|
+
|
18
|
+
|
19
|
+
class BaseBlockchainProvider:
|
20
|
+
CONTRACTS_METADATA_DIR = Path(__file__).parents[2] / "data/interfaces"
|
21
|
+
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
environment: Environment,
|
25
|
+
blockchain_id: str,
|
26
|
+
rpc_uri: URI | None = None,
|
27
|
+
gas_price: Wei | None = None,
|
28
|
+
):
|
29
|
+
if environment not in BLOCKCHAINS.keys():
|
30
|
+
raise EnvironmentNotSupported(f"Environment {environment} isn't supported!")
|
31
|
+
|
32
|
+
self.environment = environment
|
33
|
+
self.rpc_uri = rpc_uri
|
34
|
+
self.blockchain_id = (
|
35
|
+
blockchain_id
|
36
|
+
if blockchain_id in BLOCKCHAINS[self.environment].keys()
|
37
|
+
else None
|
38
|
+
)
|
39
|
+
|
40
|
+
if self.rpc_uri is None and self.blockchain_id is not None:
|
41
|
+
self.blockchain_id = blockchain_id
|
42
|
+
self.rpc_uri = self.rpc_uri or BLOCKCHAINS[self.environment][
|
43
|
+
self.blockchain_id
|
44
|
+
].get("rpc", None)
|
45
|
+
|
46
|
+
if self.rpc_uri is None:
|
47
|
+
raise RPCURINotDefined(
|
48
|
+
"No RPC URI provided for unrecognized "
|
49
|
+
f"blockchain ID {self.blockchain_id}"
|
50
|
+
)
|
51
|
+
|
52
|
+
self.gas_price = gas_price
|
53
|
+
|
54
|
+
self.abi = self._load_abi()
|
55
|
+
self.output_named_tuples = self._generate_output_named_tuples()
|
56
|
+
|
57
|
+
def _generate_output_named_tuples(self) -> dict[str, dict[str, Type[tuple]]]:
|
58
|
+
def generate_output_namedtuple(function_abi: ABIFunction) -> Type[tuple] | None:
|
59
|
+
output_names = [output["name"] for output in function_abi["outputs"]]
|
60
|
+
if all(name != "" for name in output_names):
|
61
|
+
return namedtuple(f"{function_abi['name']}Result", output_names)
|
62
|
+
return None
|
63
|
+
|
64
|
+
output_named_tuples = {}
|
65
|
+
for contract_name, contract_abi in self.abi.items():
|
66
|
+
output_named_tuples[contract_name] = {}
|
67
|
+
for item in contract_abi:
|
68
|
+
if (item["type"] != "function") or not item["outputs"]:
|
69
|
+
continue
|
70
|
+
elif item["name"] in output_named_tuples[contract_name]:
|
71
|
+
continue
|
72
|
+
named_tuple = generate_output_namedtuple(item)
|
73
|
+
if named_tuple is not None:
|
74
|
+
output_named_tuples[contract_name][item["name"]] = named_tuple
|
75
|
+
|
76
|
+
return output_named_tuples
|
77
|
+
|
78
|
+
def _load_abi(self) -> ABI:
|
79
|
+
abi = {}
|
80
|
+
|
81
|
+
for contract_metadata in self.CONTRACTS_METADATA_DIR.glob("*.json"):
|
82
|
+
with open(contract_metadata, "r") as metadata_json:
|
83
|
+
abi[contract_metadata.stem] = json.load(metadata_json)
|
84
|
+
|
85
|
+
return abi
|
86
|
+
|
87
|
+
def decode_logs_event(
|
88
|
+
self, receipt: TxReceipt, contract_name: str, event_name: str
|
89
|
+
) -> Any:
|
90
|
+
return (
|
91
|
+
self.contracts[contract_name]
|
92
|
+
.events[event_name]()
|
93
|
+
.process_receipt(receipt, errors=DISCARD)
|
94
|
+
)
|
95
|
+
|
96
|
+
def set_account(self, private_key: DataHexStr):
|
97
|
+
self.account: LocalAccount = self.w3.eth.account.from_key(private_key)
|
98
|
+
self.w3.middleware_onion.inject(
|
99
|
+
SignAndSendRawMiddlewareBuilder.build(private_key),
|
100
|
+
layer=0,
|
101
|
+
)
|
102
|
+
self.w3.eth.default_account = self.account.address
|
@@ -15,66 +15,35 @@
|
|
15
15
|
# specific language governing permissions and limitations
|
16
16
|
# under the License.
|
17
17
|
|
18
|
-
import json
|
19
18
|
import os
|
20
|
-
from
|
19
|
+
from dotenv import load_dotenv
|
21
20
|
from functools import wraps
|
22
|
-
from
|
23
|
-
from typing import Any, Type
|
21
|
+
from typing import Any
|
24
22
|
|
25
23
|
import requests
|
26
24
|
from dkg.constants import BLOCKCHAINS
|
27
25
|
from dkg.exceptions import (
|
28
26
|
AccountMissing,
|
29
|
-
EnvironmentNotSupported,
|
30
27
|
NetworkNotSupported,
|
31
|
-
RPCURINotDefined,
|
32
28
|
)
|
33
|
-
from dkg.types import URI, Address,
|
34
|
-
from eth_account.signers.local import LocalAccount
|
35
|
-
from eth_typing import ABI, ABIFunction
|
29
|
+
from dkg.types import URI, Address, Environment, Wei
|
36
30
|
from web3 import Web3
|
37
31
|
from web3.contract import Contract
|
38
32
|
from web3.contract.contract import ContractFunction
|
39
|
-
from web3.logs import DISCARD
|
40
|
-
from web3.middleware import SignAndSendRawMiddlewareBuilder
|
41
33
|
from web3.types import TxReceipt
|
34
|
+
from dkg.providers.blockchain.base_blockchain import BaseBlockchainProvider
|
42
35
|
|
43
36
|
|
44
|
-
class BlockchainProvider:
|
45
|
-
CONTRACTS_METADATA_DIR = Path(__file__).parents[1] / "data/interfaces"
|
46
|
-
|
37
|
+
class BlockchainProvider(BaseBlockchainProvider):
|
47
38
|
def __init__(
|
48
39
|
self,
|
49
40
|
environment: Environment,
|
50
41
|
blockchain_id: str,
|
51
42
|
rpc_uri: URI | None = None,
|
52
|
-
private_key: DataHexStr | None = None,
|
53
43
|
gas_price: Wei | None = None,
|
54
44
|
verify: bool = True,
|
55
45
|
):
|
56
|
-
|
57
|
-
raise EnvironmentNotSupported(f"Environment {environment} isn't supported!")
|
58
|
-
|
59
|
-
self.environment = environment
|
60
|
-
self.rpc_uri = rpc_uri
|
61
|
-
self.blockchain_id = (
|
62
|
-
blockchain_id
|
63
|
-
if blockchain_id in BLOCKCHAINS[self.environment].keys()
|
64
|
-
else None
|
65
|
-
)
|
66
|
-
|
67
|
-
if self.rpc_uri is None and self.blockchain_id is not None:
|
68
|
-
self.blockchain_id = blockchain_id
|
69
|
-
self.rpc_uri = self.rpc_uri or BLOCKCHAINS[self.environment][
|
70
|
-
self.blockchain_id
|
71
|
-
].get("rpc", None)
|
72
|
-
|
73
|
-
if self.rpc_uri is None:
|
74
|
-
raise RPCURINotDefined(
|
75
|
-
"No RPC URI provided for unrecognized "
|
76
|
-
f"blockchain ID {self.blockchain_id}"
|
77
|
-
)
|
46
|
+
super().__init__(environment, blockchain_id, rpc_uri, gas_price)
|
78
47
|
|
79
48
|
self.w3 = Web3(
|
80
49
|
Web3.HTTPProvider(self.rpc_uri, request_kwargs={"verify": verify})
|
@@ -87,15 +56,11 @@ class BlockchainProvider:
|
|
87
56
|
f"Network with blockchain ID {self.blockchain_id} isn't supported!"
|
88
57
|
)
|
89
58
|
|
90
|
-
self.gas_price = gas_price
|
91
59
|
self.gas_price_oracle = BLOCKCHAINS[self.environment][self.blockchain_id].get(
|
92
60
|
"gas_price_oracle",
|
93
61
|
None,
|
94
62
|
)
|
95
63
|
|
96
|
-
self.abi = self._load_abi()
|
97
|
-
self.output_named_tuples = self._generate_output_named_tuples()
|
98
|
-
|
99
64
|
hub_address: Address = BLOCKCHAINS[self.environment][self.blockchain_id]["hub"]
|
100
65
|
self.contracts: dict[str, Contract] = {
|
101
66
|
"Hub": self.w3.eth.contract(
|
@@ -106,11 +71,9 @@ class BlockchainProvider:
|
|
106
71
|
}
|
107
72
|
self._init_contracts()
|
108
73
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
):
|
113
|
-
self.set_account(private_key or private_key_env)
|
74
|
+
load_dotenv()
|
75
|
+
if private_key := os.environ.get("PRIVATE_KEY"):
|
76
|
+
self.set_account(private_key)
|
114
77
|
|
115
78
|
def make_json_rpc_request(self, endpoint: str, args: dict[str, Any] = {}) -> Any:
|
116
79
|
web3_method = getattr(self.w3.eth, endpoint)
|
@@ -196,23 +159,6 @@ class BlockchainProvider:
|
|
196
159
|
|
197
160
|
return tx_receipt
|
198
161
|
|
199
|
-
def decode_logs_event(
|
200
|
-
self, receipt: TxReceipt, contract_name: str, event_name: str
|
201
|
-
) -> Any:
|
202
|
-
return (
|
203
|
-
self.contracts[contract_name]
|
204
|
-
.events[event_name]()
|
205
|
-
.process_receipt(receipt, errors=DISCARD)
|
206
|
-
)
|
207
|
-
|
208
|
-
def set_account(self, private_key: DataHexStr):
|
209
|
-
self.account: LocalAccount = self.w3.eth.account.from_key(private_key)
|
210
|
-
self.w3.middleware_onion.inject(
|
211
|
-
SignAndSendRawMiddlewareBuilder.build(private_key),
|
212
|
-
layer=0,
|
213
|
-
)
|
214
|
-
self.w3.eth.default_account = self.account.address
|
215
|
-
|
216
162
|
def _get_network_gas_price(self) -> Wei | None:
|
217
163
|
if self.environment == "development":
|
218
164
|
return None
|
@@ -260,11 +206,14 @@ class BlockchainProvider:
|
|
260
206
|
):
|
261
207
|
self.contracts[contract] = self.w3.eth.contract(
|
262
208
|
address=(
|
263
|
-
self.contracts["Hub"]
|
264
|
-
if not contract.endswith("AssetStorage")
|
265
|
-
else self.contracts["Hub"]
|
209
|
+
self.contracts["Hub"]
|
266
210
|
.functions.getAssetStorageAddress(contract)
|
267
211
|
.call()
|
212
|
+
if contract.endswith("AssetStorage")
|
213
|
+
or contract.endswith("CollectionStorage")
|
214
|
+
else self.contracts["Hub"]
|
215
|
+
.functions.getContractAddress(contract)
|
216
|
+
.call()
|
268
217
|
),
|
269
218
|
abi=self.abi[contract],
|
270
219
|
decode_tuples=True,
|
@@ -277,33 +226,3 @@ class BlockchainProvider:
|
|
277
226
|
return self.call_function(contract, "status")
|
278
227
|
except Exception:
|
279
228
|
return False
|
280
|
-
|
281
|
-
def _generate_output_named_tuples(self) -> dict[str, dict[str, Type[tuple]]]:
|
282
|
-
def generate_output_namedtuple(function_abi: ABIFunction) -> Type[tuple] | None:
|
283
|
-
output_names = [output["name"] for output in function_abi["outputs"]]
|
284
|
-
if all(name != "" for name in output_names):
|
285
|
-
return namedtuple(f"{function_abi['name']}Result", output_names)
|
286
|
-
return None
|
287
|
-
|
288
|
-
output_named_tuples = {}
|
289
|
-
for contract_name, contract_abi in self.abi.items():
|
290
|
-
output_named_tuples[contract_name] = {}
|
291
|
-
for item in contract_abi:
|
292
|
-
if (item["type"] != "function") or not item["outputs"]:
|
293
|
-
continue
|
294
|
-
elif item["name"] in output_named_tuples[contract_name]:
|
295
|
-
continue
|
296
|
-
named_tuple = generate_output_namedtuple(item)
|
297
|
-
if named_tuple is not None:
|
298
|
-
output_named_tuples[contract_name][item["name"]] = named_tuple
|
299
|
-
|
300
|
-
return output_named_tuples
|
301
|
-
|
302
|
-
def _load_abi(self) -> ABI:
|
303
|
-
abi = {}
|
304
|
-
|
305
|
-
for contract_metadata in self.CONTRACTS_METADATA_DIR.glob("*.json"):
|
306
|
-
with open(contract_metadata, "r") as metadata_json:
|
307
|
-
abi[contract_metadata.stem] = json.load(metadata_json)
|
308
|
-
|
309
|
-
return abi
|
@@ -0,0 +1,72 @@
|
|
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
|
+
from typing import Any
|
19
|
+
|
20
|
+
import aiohttp
|
21
|
+
from dkg.dataclasses import HTTPRequestMethod, NodeResponseDict
|
22
|
+
from dkg.exceptions import HTTPRequestMethodNotSupported, NodeRequestError
|
23
|
+
from dkg.types import URI
|
24
|
+
from dkg.providers.node.base_node_http import BaseNodeHTTPProvider
|
25
|
+
|
26
|
+
|
27
|
+
class AsyncNodeHTTPProvider(BaseNodeHTTPProvider):
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
endpoint_uri: URI | str,
|
31
|
+
api_version: str = "v1",
|
32
|
+
auth_token: str | None = None,
|
33
|
+
):
|
34
|
+
super().__init__(endpoint_uri, api_version, auth_token)
|
35
|
+
|
36
|
+
async def make_request(
|
37
|
+
self,
|
38
|
+
method: HTTPRequestMethod,
|
39
|
+
path: str,
|
40
|
+
params: dict[str, Any] = {},
|
41
|
+
data: dict[str, Any] = {},
|
42
|
+
) -> NodeResponseDict:
|
43
|
+
url = f"{self.url}/{path}"
|
44
|
+
|
45
|
+
async with aiohttp.ClientSession() as session:
|
46
|
+
try:
|
47
|
+
if method == HTTPRequestMethod.GET:
|
48
|
+
async with session.get(
|
49
|
+
url, params=params, headers=self.headers
|
50
|
+
) as response:
|
51
|
+
response.raise_for_status()
|
52
|
+
response = await response.json()
|
53
|
+
elif method == HTTPRequestMethod.POST:
|
54
|
+
async with session.post(
|
55
|
+
url, json=data, headers=self.headers
|
56
|
+
) as response:
|
57
|
+
response.raise_for_status()
|
58
|
+
response = await response.json()
|
59
|
+
else:
|
60
|
+
raise HTTPRequestMethodNotSupported(
|
61
|
+
f"{method.name} method isn't supported"
|
62
|
+
)
|
63
|
+
|
64
|
+
try:
|
65
|
+
return NodeResponseDict(response)
|
66
|
+
except ValueError as err:
|
67
|
+
raise NodeRequestError(f"JSON decoding failed: {err}")
|
68
|
+
|
69
|
+
except aiohttp.ClientError as err:
|
70
|
+
raise NodeRequestError(f"Network error: {err}")
|
71
|
+
except Exception as err:
|
72
|
+
raise NodeRequestError(f"Unexpected error: {err}")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from dkg.dataclasses import HTTPRequestMethod, NodeResponseDict
|
3
|
+
from typing import Any
|
4
|
+
from dkg.types import URI
|
5
|
+
|
6
|
+
|
7
|
+
class BaseNodeHTTPProvider(ABC):
|
8
|
+
def __init__(
|
9
|
+
self,
|
10
|
+
endpoint_uri: URI | str,
|
11
|
+
api_version: str = "v1",
|
12
|
+
auth_token: str | None = None,
|
13
|
+
):
|
14
|
+
self.url = f"{URI(endpoint_uri)}/{api_version}"
|
15
|
+
self.headers = {"Authorization": f"Bearer {auth_token}"} if auth_token else {}
|
16
|
+
|
17
|
+
@abstractmethod
|
18
|
+
async def make_request(
|
19
|
+
self,
|
20
|
+
method: HTTPRequestMethod,
|
21
|
+
path: str,
|
22
|
+
params: dict[str, Any] = {},
|
23
|
+
data: dict[str, Any] = {},
|
24
|
+
) -> NodeResponseDict:
|
25
|
+
raise NotImplementedError("Subclasses must implement make_request")
|
@@ -22,12 +22,17 @@ from dkg.dataclasses import HTTPRequestMethod, NodeResponseDict
|
|
22
22
|
from dkg.exceptions import HTTPRequestMethodNotSupported, NodeRequestError
|
23
23
|
from dkg.types import URI
|
24
24
|
from requests.exceptions import HTTPError, ConnectionError, Timeout, RequestException
|
25
|
+
from dkg.providers.node.base_node_http import BaseNodeHTTPProvider
|
25
26
|
|
26
27
|
|
27
|
-
class NodeHTTPProvider:
|
28
|
-
def __init__(
|
29
|
-
self
|
30
|
-
|
28
|
+
class NodeHTTPProvider(BaseNodeHTTPProvider):
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
endpoint_uri: URI | str,
|
32
|
+
api_version: str = "v1",
|
33
|
+
auth_token: str | None = None,
|
34
|
+
):
|
35
|
+
super().__init__(endpoint_uri, api_version, auth_token)
|
31
36
|
|
32
37
|
def make_request(
|
33
38
|
self,
|
@@ -36,16 +41,13 @@ class NodeHTTPProvider:
|
|
36
41
|
params: dict[str, Any] = {},
|
37
42
|
data: dict[str, Any] = {},
|
38
43
|
) -> NodeResponseDict:
|
39
|
-
url = f"{self.
|
40
|
-
headers = (
|
41
|
-
{"Authorization": f"Bearer {self.auth_token}"} if self.auth_token else {}
|
42
|
-
)
|
44
|
+
url = f"{self.url}/{path}"
|
43
45
|
|
44
46
|
try:
|
45
47
|
if method == HTTPRequestMethod.GET:
|
46
|
-
response = requests.get(url, params=params, headers=headers)
|
48
|
+
response = requests.get(url, params=params, headers=self.headers)
|
47
49
|
elif method == HTTPRequestMethod.POST:
|
48
|
-
response = requests.post(url, json=data, headers=headers)
|
50
|
+
response = requests.post(url, json=data, headers=self.headers)
|
49
51
|
else:
|
50
52
|
raise HTTPRequestMethodNotSupported(
|
51
53
|
f"{method.name} method isn't supported"
|
dkg/services/__init__.py
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,180 @@
|
|
1
|
+
from dkg.modules.async_module import AsyncModule
|
2
|
+
from dkg.managers.async_manager import AsyncRequestManager
|
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 AsyncBlockchainService(AsyncModule):
|
14
|
+
def __init__(self, manager: AsyncRequestManager):
|
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_asset = Method(BlockchainRequest.mint_knowledge_asset)
|
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
|
+
async def decrease_knowledge_collection_allowance(
|
34
|
+
self,
|
35
|
+
allowance_gap: int,
|
36
|
+
):
|
37
|
+
knowledge_collection_address = await self._get_contract_address(
|
38
|
+
"KnowledgeCollection"
|
39
|
+
)
|
40
|
+
await self._decrease_allowance(knowledge_collection_address, allowance_gap)
|
41
|
+
|
42
|
+
async def increase_knowledge_collection_allowance(
|
43
|
+
self,
|
44
|
+
sender: str,
|
45
|
+
token_amount: str,
|
46
|
+
) -> AllowanceResult:
|
47
|
+
"""
|
48
|
+
Increases the allowance for knowledge collection if necessary.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
sender: The address of the sender
|
52
|
+
token_amount: The amount of tokens to check/increase allowance for
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
AllowanceResult containing whether allowance was increased and the gap
|
56
|
+
"""
|
57
|
+
knowledge_collection_address = await self._get_contract_address(
|
58
|
+
"KnowledgeCollection"
|
59
|
+
)
|
60
|
+
|
61
|
+
allowance = await self._get_current_allowance(
|
62
|
+
sender, knowledge_collection_address
|
63
|
+
)
|
64
|
+
allowance_gap = int(token_amount) - int(allowance)
|
65
|
+
|
66
|
+
if allowance_gap > 0:
|
67
|
+
await self._increase_allowance(knowledge_collection_address, allowance_gap)
|
68
|
+
|
69
|
+
return AllowanceResult(
|
70
|
+
allowance_increased=True, allowance_gap=allowance_gap
|
71
|
+
)
|
72
|
+
|
73
|
+
return AllowanceResult(allowance_increased=False, allowance_gap=allowance_gap)
|
74
|
+
|
75
|
+
async def create_knowledge_collection(
|
76
|
+
self,
|
77
|
+
request: dict,
|
78
|
+
paranet_ka_contract: Optional[Address] = None,
|
79
|
+
paranet_token_id: Optional[int] = None,
|
80
|
+
) -> KnowledgeCollectionResult:
|
81
|
+
"""
|
82
|
+
Creates a knowledge collection on the blockchain.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
request: dict containing all collection parameters
|
86
|
+
paranet_ka_contract: Optional paranet contract address
|
87
|
+
paranet_token_id: Optional paranet token ID
|
88
|
+
blockchain: Blockchain configuration
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
KnowledgeCollectionResult containing collection ID and transaction receipt
|
92
|
+
|
93
|
+
Raises:
|
94
|
+
BlockchainError: If the collection creation fails
|
95
|
+
"""
|
96
|
+
sender = self.manager.blockchain_provider.account.address
|
97
|
+
allowance_increased = False
|
98
|
+
allowance_gap = 0
|
99
|
+
|
100
|
+
try:
|
101
|
+
# Handle allowance
|
102
|
+
if request.get("paymaster") and request.get("paymaster") != ZERO_ADDRESS:
|
103
|
+
pass
|
104
|
+
else:
|
105
|
+
allowance_result = await self.increase_knowledge_collection_allowance(
|
106
|
+
sender=sender,
|
107
|
+
token_amount=request.get("tokenAmount"),
|
108
|
+
)
|
109
|
+
allowance_increased = allowance_result.allowance_increased
|
110
|
+
allowance_gap = allowance_result.allowance_gap
|
111
|
+
|
112
|
+
if not paranet_ka_contract and not paranet_token_id:
|
113
|
+
receipt = await self._create_knowledge_collection(
|
114
|
+
request.get("publishOperationId"),
|
115
|
+
Web3.to_bytes(hexstr=request.get("merkleRoot")),
|
116
|
+
request.get("knowledgeAssetsAmount"),
|
117
|
+
request.get("byteSize"),
|
118
|
+
request.get("epochs"),
|
119
|
+
request.get("tokenAmount"),
|
120
|
+
request.get("isImmutable"),
|
121
|
+
request.get("paymaster"),
|
122
|
+
request.get("publisherNodeIdentityId"),
|
123
|
+
Web3.to_bytes(hexstr=request.get("publisherNodeR")),
|
124
|
+
Web3.to_bytes(hexstr=request.get("publisherNodeVS")),
|
125
|
+
request.get("identityIds"),
|
126
|
+
[Web3.to_bytes(hexstr=x) for x in request.get("r")],
|
127
|
+
[Web3.to_bytes(hexstr=x) for x in request.get("vs")],
|
128
|
+
)
|
129
|
+
else:
|
130
|
+
receipt = await self._mint_knowledge_asset(
|
131
|
+
paranet_ka_contract,
|
132
|
+
paranet_token_id,
|
133
|
+
list(request.values()),
|
134
|
+
)
|
135
|
+
|
136
|
+
event_data = self.manager.blockchain_provider.decode_logs_event(
|
137
|
+
receipt=receipt,
|
138
|
+
contract_name="KnowledgeCollectionStorage",
|
139
|
+
event_name="KnowledgeCollectionCreated",
|
140
|
+
)
|
141
|
+
collection_id = (
|
142
|
+
int(getattr(event_data[0].get("args", {}), "id", None))
|
143
|
+
if event_data
|
144
|
+
else None
|
145
|
+
)
|
146
|
+
|
147
|
+
return KnowledgeCollectionResult(
|
148
|
+
knowledge_collection_id=collection_id, receipt=receipt
|
149
|
+
)
|
150
|
+
|
151
|
+
except Exception as e:
|
152
|
+
if allowance_increased:
|
153
|
+
await self.decrease_knowledge_collection_allowance(allowance_gap)
|
154
|
+
raise e
|
155
|
+
|
156
|
+
# TODO: change self._owner to v8 compatible function
|
157
|
+
async def get_owner(self, ual: UAL) -> Address:
|
158
|
+
token_id = parse_ual(ual)["token_id"]
|
159
|
+
|
160
|
+
return await self._owner(token_id)
|
161
|
+
|
162
|
+
async def get_asset_storage_address(self, asset_storage_name: str) -> Address:
|
163
|
+
return await self._get_asset_storage_address(asset_storage_name)
|
164
|
+
|
165
|
+
async def key_is_operational_wallet(
|
166
|
+
self, identity_id: int, key: Address, purpose: int
|
167
|
+
) -> bool:
|
168
|
+
return await self._key_is_operational_wallet(identity_id, key, purpose)
|
169
|
+
|
170
|
+
async def time_until_next_epoch(self) -> int:
|
171
|
+
return await self._time_until_next_epoch()
|
172
|
+
|
173
|
+
async def epoch_length(self) -> int:
|
174
|
+
return await self._epoch_length()
|
175
|
+
|
176
|
+
async def get_stake_weighted_average_ask(self) -> int:
|
177
|
+
return await self._get_stake_weighted_average_ask()
|
178
|
+
|
179
|
+
async def get_block(self, block_identifier: str | int):
|
180
|
+
return await self._get_block(block_identifier)
|