dkg 8.0.0a2__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 +60 -13
- dkg/utils/rdf.py +9 -3
- {dkg-8.0.0a2.dist-info → dkg-8.0.1.dist-info}/METADATA +28 -19
- dkg-8.0.1.dist-info/RECORD +82 -0
- {dkg-8.0.0a2.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.0a2.dist-info/RECORD +0 -52
- {dkg-8.0.0a2.dist-info → dkg-8.0.1.dist-info}/LICENSE +0 -0
- {dkg-8.0.0a2.dist-info → dkg-8.0.1.dist-info}/NOTICE +0 -0
| @@ -0,0 +1,739 @@ | |
| 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 hashlib
         | 
| 20 | 
            +
            from typing import Literal
         | 
| 21 | 
            +
            from pyld import jsonld
         | 
| 22 | 
            +
            from web3 import Web3
         | 
| 23 | 
            +
            from web3.constants import ADDRESS_ZERO
         | 
| 24 | 
            +
            from web3.types import TxReceipt
         | 
| 25 | 
            +
            from itertools import chain
         | 
| 26 | 
            +
            from eth_abi.packed import encode_packed
         | 
| 27 | 
            +
            from eth_account.messages import encode_defunct
         | 
| 28 | 
            +
            from eth_account import Account
         | 
| 29 | 
            +
            from hexbytes import HexBytes
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            from dkg.constants import (
         | 
| 32 | 
            +
                PRIVATE_ASSERTION_PREDICATE,
         | 
| 33 | 
            +
                PRIVATE_RESOURCE_PREDICATE,
         | 
| 34 | 
            +
                PRIVATE_HASH_SUBJECT_PREFIX,
         | 
| 35 | 
            +
                CHUNK_BYTE_SIZE,
         | 
| 36 | 
            +
                MAX_FILE_SIZE,
         | 
| 37 | 
            +
                DEFAULT_RDF_FORMAT,
         | 
| 38 | 
            +
                Operations,
         | 
| 39 | 
            +
                OutputTypes,
         | 
| 40 | 
            +
            )
         | 
| 41 | 
            +
            from dkg.dataclasses import (
         | 
| 42 | 
            +
                NodeResponseDict,
         | 
| 43 | 
            +
            )
         | 
| 44 | 
            +
            from dkg.managers.manager import DefaultRequestManager
         | 
| 45 | 
            +
            from dkg.method import Method
         | 
| 46 | 
            +
            from dkg.modules.module import Module
         | 
| 47 | 
            +
            from dkg.types import JSONLD, UAL, Address, HexStr
         | 
| 48 | 
            +
            from dkg.utils.blockchain_request import (
         | 
| 49 | 
            +
                BlockchainRequest,
         | 
| 50 | 
            +
            )
         | 
| 51 | 
            +
            from dkg.utils.node_request import (
         | 
| 52 | 
            +
                NodeRequest,
         | 
| 53 | 
            +
                OperationStatus,
         | 
| 54 | 
            +
                get_operation_status_object,
         | 
| 55 | 
            +
            )
         | 
| 56 | 
            +
            from dkg.utils.ual import format_ual, parse_ual
         | 
| 57 | 
            +
            import dkg.utils.knowledge_collection_tools as kc_tools
         | 
| 58 | 
            +
            import dkg.utils.knowledge_asset_tools as ka_tools
         | 
| 59 | 
            +
            from dkg.services.input_service import InputService
         | 
| 60 | 
            +
            from dkg.services.node_services.node_service import NodeService
         | 
| 61 | 
            +
            from dkg.services.blockchain_services.blockchain_service import BlockchainService
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             | 
| 64 | 
            +
            class KnowledgeAsset(Module):
         | 
| 65 | 
            +
                def __init__(
         | 
| 66 | 
            +
                    self,
         | 
| 67 | 
            +
                    manager: DefaultRequestManager,
         | 
| 68 | 
            +
                    input_service: InputService,
         | 
| 69 | 
            +
                    node_service: NodeService,
         | 
| 70 | 
            +
                    blockchain_service: BlockchainService,
         | 
| 71 | 
            +
                ):
         | 
| 72 | 
            +
                    self.manager = manager
         | 
| 73 | 
            +
                    self.input_service = input_service
         | 
| 74 | 
            +
                    self.node_service = node_service
         | 
| 75 | 
            +
                    self.blockchain_service = blockchain_service
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def is_valid_ual(self, ual: UAL) -> bool:
         | 
| 78 | 
            +
                    if not ual or not isinstance(ual, str):
         | 
| 79 | 
            +
                        raise ValueError("UAL must be a non-empty string.")
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    parts = ual.split("/")
         | 
| 82 | 
            +
                    if len(parts) != 3:
         | 
| 83 | 
            +
                        raise ValueError("UAL format is incorrect.")
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    prefixes = parts[0].split(":")
         | 
| 86 | 
            +
                    prefixes_number = len(prefixes)
         | 
| 87 | 
            +
                    if prefixes_number != 3 and prefixes_number != 4:
         | 
| 88 | 
            +
                        raise ValueError("Prefix format in UAL is incorrect.")
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    if prefixes[0] != "did":
         | 
| 91 | 
            +
                        raise ValueError(
         | 
| 92 | 
            +
                            f"Invalid DID prefix. Expected: 'did'. Received: '{prefixes[0]}'."
         | 
| 93 | 
            +
                        )
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    if prefixes[1] != "dkg":
         | 
| 96 | 
            +
                        raise ValueError(
         | 
| 97 | 
            +
                            f"Invalid DKG prefix. Expected: 'dkg'. Received: '{prefixes[1]}'."
         | 
| 98 | 
            +
                        )
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    if prefixes[2] != (
         | 
| 101 | 
            +
                        blockchain_name := (
         | 
| 102 | 
            +
                            self.manager.blockchain_provider.blockchain_id.split(":")[0]
         | 
| 103 | 
            +
                        )
         | 
| 104 | 
            +
                    ):
         | 
| 105 | 
            +
                        raise ValueError(
         | 
| 106 | 
            +
                            "Invalid blockchain name in the UAL prefix. "
         | 
| 107 | 
            +
                            f"Expected: '{blockchain_name}'. Received: '${prefixes[2]}'."
         | 
| 108 | 
            +
                        )
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    if prefixes_number == 4:
         | 
| 111 | 
            +
                        chain_id = self.manager.blockchain_provider.blockchain_id.split(":")[1]
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                        if int(prefixes[3]) != int(chain_id):
         | 
| 114 | 
            +
                            raise ValueError(
         | 
| 115 | 
            +
                                "Chain ID in UAL does not match the blockchain. "
         | 
| 116 | 
            +
                                f"Expected: '${chain_id}'. Received: '${prefixes[3]}'."
         | 
| 117 | 
            +
                            )
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    contract_address = self.manager.blockchain_provider.contracts[
         | 
| 120 | 
            +
                        "ContentAssetStorage"
         | 
| 121 | 
            +
                    ].address
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    if parts[1].lower() != contract_address.lower():
         | 
| 124 | 
            +
                        raise ValueError(
         | 
| 125 | 
            +
                            "Contract address in UAL does not match. "
         | 
| 126 | 
            +
                            f"Expected: '${contract_address.lower()}'. "
         | 
| 127 | 
            +
                            f"Received: '${parts[1].lower()}'."
         | 
| 128 | 
            +
                        )
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    try:
         | 
| 131 | 
            +
                        owner = self.blockchain_service.get_owner(int(parts[2]))
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                        if not owner or owner == ADDRESS_ZERO:
         | 
| 134 | 
            +
                            raise ValueError("Token does not exist or has no owner.")
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                        return True
         | 
| 137 | 
            +
                    except Exception as err:
         | 
| 138 | 
            +
                        raise ValueError(f"Error fetching asset owner: {err}")
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def process_content(self, content: str) -> list:
         | 
| 141 | 
            +
                    return [line.strip() for line in content.split("\n") if line.strip() != ""]
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                def solidity_packed_sha256(self, types: list[str], values: list) -> str:
         | 
| 144 | 
            +
                    # Encode the values using eth_abi's encode_packed
         | 
| 145 | 
            +
                    packed_data = encode_packed(types, values)
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    # Calculate SHA256
         | 
| 148 | 
            +
                    sha256_hash = hashlib.sha256(packed_data).hexdigest()
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    return f"0x{sha256_hash}"
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                def insert_triple_sorted(self, triples_list: list, new_triple: str) -> int:
         | 
| 153 | 
            +
                    # Assuming triples_list is already sorted
         | 
| 154 | 
            +
                    left = 0
         | 
| 155 | 
            +
                    right = len(triples_list)
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                    while left < right:
         | 
| 158 | 
            +
                        mid = (left + right) // 2
         | 
| 159 | 
            +
                        if triples_list[mid] < new_triple:
         | 
| 160 | 
            +
                            left = mid + 1
         | 
| 161 | 
            +
                        else:
         | 
| 162 | 
            +
                            right = mid
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    # Insert the new triple at the correct position
         | 
| 165 | 
            +
                    triples_list.insert(left, new_triple)
         | 
| 166 | 
            +
                    return left
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                def get_operation_status_dict(self, operation_result, operation_id):
         | 
| 169 | 
            +
                    # Check if data exists and has errorType
         | 
| 170 | 
            +
                    operation_data = (
         | 
| 171 | 
            +
                        {"status": operation_result.get("status"), **operation_result.get("data")}
         | 
| 172 | 
            +
                        if operation_result.get("data")
         | 
| 173 | 
            +
                        and operation_result.get("data", {}).get("errorType")
         | 
| 174 | 
            +
                        else {"status": operation_result.get("status")}
         | 
| 175 | 
            +
                    )
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    return {"operationId": operation_id, **operation_data}
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                def get_message_signer_address(self, dataset_root: str, signature: dict):
         | 
| 180 | 
            +
                    message = encode_defunct(HexBytes(dataset_root))
         | 
| 181 | 
            +
                    r, s, v = signature.get("r"), signature.get("s"), signature.get("v")
         | 
| 182 | 
            +
                    r = r[2:] if r.startswith("0x") else r
         | 
| 183 | 
            +
                    s = s[2:] if s.startswith("0x") else s
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    sig = "0x" + r + s + hex(v)[2:].zfill(2)
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    return Account.recover_message(message, signature=sig)
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                def create(
         | 
| 190 | 
            +
                    self,
         | 
| 191 | 
            +
                    content: dict[Literal["public", "private"], JSONLD],
         | 
| 192 | 
            +
                    options: dict = {},
         | 
| 193 | 
            +
                ) -> dict[str, UAL | HexStr | dict[str, dict[str, str] | TxReceipt]]:
         | 
| 194 | 
            +
                    arguments = self.input_service.get_asset_create_arguments(options)
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                    max_number_of_retries = arguments.get("max_number_of_retries")
         | 
| 197 | 
            +
                    frequency = arguments.get("frequency")
         | 
| 198 | 
            +
                    epochs_num = arguments.get("epochs_num")
         | 
| 199 | 
            +
                    hash_function_id = arguments.get("hash_function_id")
         | 
| 200 | 
            +
                    immutable = arguments.get("immutable")
         | 
| 201 | 
            +
                    token_amount = arguments.get("token_amount")
         | 
| 202 | 
            +
                    payer = arguments.get("payer")
         | 
| 203 | 
            +
                    minimum_number_of_finalization_confirmations = arguments.get(
         | 
| 204 | 
            +
                        "minimum_number_of_finalization_confirmations"
         | 
| 205 | 
            +
                    )
         | 
| 206 | 
            +
                    minimum_number_of_node_replications = arguments.get(
         | 
| 207 | 
            +
                        "minimum_number_of_node_replications"
         | 
| 208 | 
            +
                    )
         | 
| 209 | 
            +
                    blockchain_id = self.manager.blockchain_provider.blockchain_id
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                    dataset = {}
         | 
| 212 | 
            +
                    public_content = dataset.get("public")
         | 
| 213 | 
            +
                    private_content = dataset.get("private")
         | 
| 214 | 
            +
                    if isinstance(content, str):
         | 
| 215 | 
            +
                        dataset["public"] = self.process_content(content)
         | 
| 216 | 
            +
                    elif isinstance(public_content, str) or (
         | 
| 217 | 
            +
                        not public_content and private_content and isinstance(private_content, str)
         | 
| 218 | 
            +
                    ):
         | 
| 219 | 
            +
                        if public_content:
         | 
| 220 | 
            +
                            dataset["public"] = self.process_content(public_content)
         | 
| 221 | 
            +
                        else:
         | 
| 222 | 
            +
                            dataset["public"] = []
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                        if private_content and isinstance(private_content, str):
         | 
| 225 | 
            +
                            dataset["private"] = self.process_content(private_content)
         | 
| 226 | 
            +
                    else:
         | 
| 227 | 
            +
                        dataset = kc_tools.format_dataset(content)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                    public_triples_grouped = []
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    dataset["public"] = kc_tools.generate_missing_ids_for_blank_nodes(
         | 
| 232 | 
            +
                        dataset.get("public")
         | 
| 233 | 
            +
                    )
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                    if dataset.get("private") and len(dataset.get("private")):
         | 
| 236 | 
            +
                        dataset["private"] = kc_tools.generate_missing_ids_for_blank_nodes(
         | 
| 237 | 
            +
                            dataset.get("private")
         | 
| 238 | 
            +
                        )
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                        # Group private triples by subject and flatten
         | 
| 241 | 
            +
                        private_triples_grouped = kc_tools.group_nquads_by_subject(
         | 
| 242 | 
            +
                            dataset.get("private"), True
         | 
| 243 | 
            +
                        )
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                        dataset["private"] = list(chain.from_iterable(private_triples_grouped))
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                        # Compute private root and add to public
         | 
| 248 | 
            +
                        private_root = kc_tools.calculate_merkle_root(dataset.get("private"))
         | 
| 249 | 
            +
                        dataset["public"].append(
         | 
| 250 | 
            +
                            f'<{ka_tools.generate_named_node()}> <{PRIVATE_ASSERTION_PREDICATE}> "{private_root}" .'
         | 
| 251 | 
            +
                        )
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                        # Compute private root and add to public
         | 
| 254 | 
            +
                        public_triples_grouped = kc_tools.group_nquads_by_subject(
         | 
| 255 | 
            +
                            dataset.get("public"), True
         | 
| 256 | 
            +
                        )
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                        # Create a dictionary for public subject -> index for quick lookup
         | 
| 259 | 
            +
                        public_subject_dict = {}
         | 
| 260 | 
            +
                        for i in range(len(public_triples_grouped)):
         | 
| 261 | 
            +
                            public_subject = public_triples_grouped[i][0].split(" ")[0]
         | 
| 262 | 
            +
                            public_subject_dict[public_subject] = i
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                        private_triple_subject_hashes_grouped_without_public_pair = []
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                        # Integrate private subjects into public or store separately if no match to be appended later
         | 
| 267 | 
            +
                        for private_triples in private_triples_grouped:
         | 
| 268 | 
            +
                            private_subject = private_triples[0].split(" ")[
         | 
| 269 | 
            +
                                0
         | 
| 270 | 
            +
                            ]  # Extract the private subject
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                            private_subject_hash = self.solidity_packed_sha256(
         | 
| 273 | 
            +
                                types=["string"],
         | 
| 274 | 
            +
                                values=[private_subject[1:-1]],
         | 
| 275 | 
            +
                            )
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                            if (
         | 
| 278 | 
            +
                                private_subject in public_subject_dict
         | 
| 279 | 
            +
                            ):  # Check if there's a public pair
         | 
| 280 | 
            +
                                # If there's a public pair, insert a representation in that group
         | 
| 281 | 
            +
                                public_index = public_subject_dict.get(private_subject)
         | 
| 282 | 
            +
                                self.insert_triple_sorted(
         | 
| 283 | 
            +
                                    public_triples_grouped[public_index],
         | 
| 284 | 
            +
                                    f"{private_subject} <{PRIVATE_RESOURCE_PREDICATE}> <{ka_tools.generate_named_node()}> .",
         | 
| 285 | 
            +
                                )
         | 
| 286 | 
            +
                            else:
         | 
| 287 | 
            +
                                # If no public pair, maintain separate list, inserting sorted by hash
         | 
| 288 | 
            +
                                self.insert_triple_sorted(
         | 
| 289 | 
            +
                                    private_triple_subject_hashes_grouped_without_public_pair,
         | 
| 290 | 
            +
                                    f"<{PRIVATE_HASH_SUBJECT_PREFIX}{private_subject_hash}> <{PRIVATE_RESOURCE_PREDICATE}> <{ka_tools.generate_named_node()}> .",
         | 
| 291 | 
            +
                                )
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                        for triple in private_triple_subject_hashes_grouped_without_public_pair:
         | 
| 294 | 
            +
                            public_triples_grouped.append([triple])
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                        dataset["public"] = list(chain.from_iterable(public_triples_grouped))
         | 
| 297 | 
            +
                    else:
         | 
| 298 | 
            +
                        # No private triples, just group and flatten public
         | 
| 299 | 
            +
                        public_triples_grouped = kc_tools.group_nquads_by_subject(
         | 
| 300 | 
            +
                            dataset.get("public"), True
         | 
| 301 | 
            +
                        )
         | 
| 302 | 
            +
                        dataset["public"] = list(chain.from_iterable(public_triples_grouped))
         | 
| 303 | 
            +
             | 
| 304 | 
            +
                    # Calculate the number of chunks
         | 
| 305 | 
            +
                    number_of_chunks = kc_tools.calculate_number_of_chunks(
         | 
| 306 | 
            +
                        dataset.get("public"), CHUNK_BYTE_SIZE
         | 
| 307 | 
            +
                    )
         | 
| 308 | 
            +
                    dataset_size = number_of_chunks * CHUNK_BYTE_SIZE
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                    # Validate the assertion size in bytes
         | 
| 311 | 
            +
                    if dataset_size > MAX_FILE_SIZE:
         | 
| 312 | 
            +
                        raise ValueError(f"File size limit is {MAX_FILE_SIZE / (1024 * 1024)}MB.")
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                    # Calculate the Merkle root
         | 
| 315 | 
            +
                    dataset_root = kc_tools.calculate_merkle_root(dataset.get("public"))
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                    # Get the contract address for KnowledgeCollectionStorage
         | 
| 318 | 
            +
                    content_asset_storage_address = (
         | 
| 319 | 
            +
                        self.blockchain_service.get_asset_storage_address(
         | 
| 320 | 
            +
                            "KnowledgeCollectionStorage"
         | 
| 321 | 
            +
                        )
         | 
| 322 | 
            +
                    )
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                    publish_operation_id = self.node_service.publish(
         | 
| 325 | 
            +
                        dataset_root,
         | 
| 326 | 
            +
                        dataset,
         | 
| 327 | 
            +
                        blockchain_id,
         | 
| 328 | 
            +
                        hash_function_id,
         | 
| 329 | 
            +
                        minimum_number_of_node_replications,
         | 
| 330 | 
            +
                    )["operationId"]
         | 
| 331 | 
            +
                    publish_operation_result = self.node_service.get_operation_result(
         | 
| 332 | 
            +
                        publish_operation_id,
         | 
| 333 | 
            +
                        Operations.PUBLISH.value,
         | 
| 334 | 
            +
                        max_number_of_retries,
         | 
| 335 | 
            +
                        frequency,
         | 
| 336 | 
            +
                    )
         | 
| 337 | 
            +
             | 
| 338 | 
            +
                    if publish_operation_result.get(
         | 
| 339 | 
            +
                        "status"
         | 
| 340 | 
            +
                    ) != OperationStatus.COMPLETED and not publish_operation_result.get(
         | 
| 341 | 
            +
                        "data", {}
         | 
| 342 | 
            +
                    ).get("minAcksReached"):
         | 
| 343 | 
            +
                        return {
         | 
| 344 | 
            +
                            "datasetRoot": dataset_root,
         | 
| 345 | 
            +
                            "operation": {
         | 
| 346 | 
            +
                                "publish": self.get_operation_status_dict(
         | 
| 347 | 
            +
                                    publish_operation_result, publish_operation_id
         | 
| 348 | 
            +
                                )
         | 
| 349 | 
            +
                            },
         | 
| 350 | 
            +
                        }
         | 
| 351 | 
            +
             | 
| 352 | 
            +
                    data = publish_operation_result.get("data", {})
         | 
| 353 | 
            +
                    signatures = data.get("signatures")
         | 
| 354 | 
            +
             | 
| 355 | 
            +
                    publisher_node_signature = data.get("publisherNodeSignature", {})
         | 
| 356 | 
            +
                    publisher_node_identity_id = publisher_node_signature.get("identityId")
         | 
| 357 | 
            +
                    publisher_node_r = publisher_node_signature.get("r")
         | 
| 358 | 
            +
                    publisher_node_vs = publisher_node_signature.get("vs")
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                    identity_ids, r, vs = [], [], []
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    for signature in signatures:
         | 
| 363 | 
            +
                        try:
         | 
| 364 | 
            +
                            signer_address = self.get_message_signer_address(
         | 
| 365 | 
            +
                                dataset_root, signature
         | 
| 366 | 
            +
                            )
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                            key_is_operational_wallet = (
         | 
| 369 | 
            +
                                self.blockchain_service.key_is_operational_wallet(
         | 
| 370 | 
            +
                                    signature.get("identityId"),
         | 
| 371 | 
            +
                                    Web3.solidity_keccak(["address"], [signer_address]),
         | 
| 372 | 
            +
                                    2,  # IdentityLib.OPERATIONAL_KEY
         | 
| 373 | 
            +
                                )
         | 
| 374 | 
            +
                            )
         | 
| 375 | 
            +
             | 
| 376 | 
            +
                            # If valid, append the signature components
         | 
| 377 | 
            +
                            if key_is_operational_wallet:
         | 
| 378 | 
            +
                                identity_ids.append(signature.get("identityId"))
         | 
| 379 | 
            +
                                r.append(signature.get("r"))
         | 
| 380 | 
            +
                                vs.append(signature.get("vs"))
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                        except Exception:
         | 
| 383 | 
            +
                            continue
         | 
| 384 | 
            +
             | 
| 385 | 
            +
                    if token_amount:
         | 
| 386 | 
            +
                        estimated_publishing_cost = token_amount
         | 
| 387 | 
            +
                    else:
         | 
| 388 | 
            +
                        time_until_next_epoch = self.blockchain_service.time_until_next_epoch()
         | 
| 389 | 
            +
                        epoch_length = self.blockchain_service.epoch_length()
         | 
| 390 | 
            +
                        stake_weighted_average_ask = (
         | 
| 391 | 
            +
                            self.blockchain_service.get_stake_weighted_average_ask()
         | 
| 392 | 
            +
                        )
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                        # Convert to integers and perform calculation
         | 
| 395 | 
            +
                        estimated_publishing_cost = (
         | 
| 396 | 
            +
                            (
         | 
| 397 | 
            +
                                int(stake_weighted_average_ask)
         | 
| 398 | 
            +
                                * (
         | 
| 399 | 
            +
                                    int(epochs_num) * int(1e18)
         | 
| 400 | 
            +
                                    + (int(time_until_next_epoch) * int(1e18)) // int(epoch_length)
         | 
| 401 | 
            +
                                )
         | 
| 402 | 
            +
                                * int(dataset_size)
         | 
| 403 | 
            +
                            )
         | 
| 404 | 
            +
                            // 1024
         | 
| 405 | 
            +
                            // int(1e18)
         | 
| 406 | 
            +
                        )
         | 
| 407 | 
            +
             | 
| 408 | 
            +
                    knowledge_collection_id = None
         | 
| 409 | 
            +
                    mint_knowledge_asset_receipt = None
         | 
| 410 | 
            +
             | 
| 411 | 
            +
                    knowledge_collection_result = (
         | 
| 412 | 
            +
                        self.blockchain_service.create_knowledge_collection(
         | 
| 413 | 
            +
                            {
         | 
| 414 | 
            +
                                "publishOperationId": publish_operation_id,
         | 
| 415 | 
            +
                                "merkleRoot": dataset_root,
         | 
| 416 | 
            +
                                "knowledgeAssetsAmount": kc_tools.count_distinct_subjects(
         | 
| 417 | 
            +
                                    dataset.get("public")
         | 
| 418 | 
            +
                                ),
         | 
| 419 | 
            +
                                "byteSize": dataset_size,
         | 
| 420 | 
            +
                                "epochs": epochs_num,
         | 
| 421 | 
            +
                                "tokenAmount": estimated_publishing_cost,
         | 
| 422 | 
            +
                                "isImmutable": immutable,
         | 
| 423 | 
            +
                                "paymaster": payer,
         | 
| 424 | 
            +
                                "publisherNodeIdentityId": publisher_node_identity_id,
         | 
| 425 | 
            +
                                "publisherNodeR": publisher_node_r,
         | 
| 426 | 
            +
                                "publisherNodeVS": publisher_node_vs,
         | 
| 427 | 
            +
                                "identityIds": identity_ids,
         | 
| 428 | 
            +
                                "r": r,
         | 
| 429 | 
            +
                                "vs": vs,
         | 
| 430 | 
            +
                            },
         | 
| 431 | 
            +
                            None,
         | 
| 432 | 
            +
                            None,
         | 
| 433 | 
            +
                        )
         | 
| 434 | 
            +
                    )
         | 
| 435 | 
            +
                    knowledge_collection_id = knowledge_collection_result.knowledge_collection_id
         | 
| 436 | 
            +
                    mint_knowledge_asset_receipt = knowledge_collection_result.receipt
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                    ual = format_ual(
         | 
| 439 | 
            +
                        blockchain_id, content_asset_storage_address, knowledge_collection_id
         | 
| 440 | 
            +
                    )
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                    finality_status_result = 0
         | 
| 443 | 
            +
                    if minimum_number_of_finalization_confirmations > 0:
         | 
| 444 | 
            +
                        finality_status_result = self.node_service.finality_status(
         | 
| 445 | 
            +
                            ual,
         | 
| 446 | 
            +
                            minimum_number_of_finalization_confirmations,
         | 
| 447 | 
            +
                            max_number_of_retries,
         | 
| 448 | 
            +
                            frequency,
         | 
| 449 | 
            +
                        )
         | 
| 450 | 
            +
             | 
| 451 | 
            +
                    return json.loads(
         | 
| 452 | 
            +
                        Web3.to_json(
         | 
| 453 | 
            +
                            {
         | 
| 454 | 
            +
                                "UAL": ual,
         | 
| 455 | 
            +
                                "datasetRoot": dataset_root,
         | 
| 456 | 
            +
                                "signatures": publish_operation_result.get("data", {}).get(
         | 
| 457 | 
            +
                                    "signatures"
         | 
| 458 | 
            +
                                ),
         | 
| 459 | 
            +
                                "operation": {
         | 
| 460 | 
            +
                                    "mintKnowledgeAsset": mint_knowledge_asset_receipt,
         | 
| 461 | 
            +
                                    "publish": get_operation_status_object(
         | 
| 462 | 
            +
                                        publish_operation_result, publish_operation_id
         | 
| 463 | 
            +
                                    ),
         | 
| 464 | 
            +
                                    "finality": {
         | 
| 465 | 
            +
                                        "status": (
         | 
| 466 | 
            +
                                            "FINALIZED"
         | 
| 467 | 
            +
                                            if finality_status_result
         | 
| 468 | 
            +
                                            >= minimum_number_of_finalization_confirmations
         | 
| 469 | 
            +
                                            else "NOT FINALIZED"
         | 
| 470 | 
            +
                                        )
         | 
| 471 | 
            +
                                    },
         | 
| 472 | 
            +
                                    "numberOfConfirmations": finality_status_result,
         | 
| 473 | 
            +
                                    "requiredConfirmations": minimum_number_of_finalization_confirmations,
         | 
| 474 | 
            +
                                },
         | 
| 475 | 
            +
                            }
         | 
| 476 | 
            +
                        )
         | 
| 477 | 
            +
                    )
         | 
| 478 | 
            +
             | 
| 479 | 
            +
                _submit_knowledge_asset = Method(BlockchainRequest.submit_knowledge_asset)
         | 
| 480 | 
            +
             | 
| 481 | 
            +
                def submit_to_paranet(
         | 
| 482 | 
            +
                    self, ual: UAL, paranet_ual: UAL
         | 
| 483 | 
            +
                ) -> dict[str, UAL | Address | TxReceipt]:
         | 
| 484 | 
            +
                    parsed_ual = parse_ual(ual)
         | 
| 485 | 
            +
                    knowledge_asset_storage, knowledge_asset_token_id = (
         | 
| 486 | 
            +
                        parsed_ual["contract_address"],
         | 
| 487 | 
            +
                        parsed_ual["token_id"],
         | 
| 488 | 
            +
                    )
         | 
| 489 | 
            +
             | 
| 490 | 
            +
                    parsed_paranet_ual = parse_ual(paranet_ual)
         | 
| 491 | 
            +
                    paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id = (
         | 
| 492 | 
            +
                        parsed_paranet_ual["contract_address"],
         | 
| 493 | 
            +
                        parsed_paranet_ual["token_id"],
         | 
| 494 | 
            +
                    )
         | 
| 495 | 
            +
             | 
| 496 | 
            +
                    receipt: TxReceipt = self._submit_knowledge_asset(
         | 
| 497 | 
            +
                        paranet_knowledge_asset_storage,
         | 
| 498 | 
            +
                        paranet_knowledge_asset_token_id,
         | 
| 499 | 
            +
                        knowledge_asset_storage,
         | 
| 500 | 
            +
                        knowledge_asset_token_id,
         | 
| 501 | 
            +
                    )
         | 
| 502 | 
            +
             | 
| 503 | 
            +
                    return {
         | 
| 504 | 
            +
                        "UAL": ual,
         | 
| 505 | 
            +
                        "paranetUAL": paranet_ual,
         | 
| 506 | 
            +
                        "paranetId": Web3.to_hex(
         | 
| 507 | 
            +
                            Web3.solidity_keccak(
         | 
| 508 | 
            +
                                ["address", "uint256"],
         | 
| 509 | 
            +
                                [knowledge_asset_storage, knowledge_asset_token_id],
         | 
| 510 | 
            +
                            )
         | 
| 511 | 
            +
                        ),
         | 
| 512 | 
            +
                        "operation": json.loads(Web3.to_json(receipt)),
         | 
| 513 | 
            +
                    }
         | 
| 514 | 
            +
             | 
| 515 | 
            +
                _transfer = Method(BlockchainRequest.transfer_asset)
         | 
| 516 | 
            +
             | 
| 517 | 
            +
                def transfer(
         | 
| 518 | 
            +
                    self,
         | 
| 519 | 
            +
                    ual: UAL,
         | 
| 520 | 
            +
                    new_owner: Address,
         | 
| 521 | 
            +
                ) -> dict[str, UAL | Address | TxReceipt]:
         | 
| 522 | 
            +
                    token_id = parse_ual(ual)["token_id"]
         | 
| 523 | 
            +
             | 
| 524 | 
            +
                    receipt: TxReceipt = self._transfer(
         | 
| 525 | 
            +
                        self.manager.blockchain_provider.account,
         | 
| 526 | 
            +
                        new_owner,
         | 
| 527 | 
            +
                        token_id,
         | 
| 528 | 
            +
                    )
         | 
| 529 | 
            +
             | 
| 530 | 
            +
                    return {
         | 
| 531 | 
            +
                        "UAL": ual,
         | 
| 532 | 
            +
                        "owner": new_owner,
         | 
| 533 | 
            +
                        "operation": json.loads(Web3.to_json(receipt)),
         | 
| 534 | 
            +
                    }
         | 
| 535 | 
            +
             | 
| 536 | 
            +
                _burn_asset = Method(BlockchainRequest.burn_asset)
         | 
| 537 | 
            +
             | 
| 538 | 
            +
                def burn(self, ual: UAL) -> dict[str, UAL | TxReceipt]:
         | 
| 539 | 
            +
                    token_id = parse_ual(ual)["token_id"]
         | 
| 540 | 
            +
             | 
| 541 | 
            +
                    receipt: TxReceipt = self._burn_asset(token_id)
         | 
| 542 | 
            +
             | 
| 543 | 
            +
                    return {"UAL": ual, "operation": json.loads(Web3.to_json(receipt))}
         | 
| 544 | 
            +
             | 
| 545 | 
            +
                _get_latest_assertion_id = Method(BlockchainRequest.get_latest_assertion_id)
         | 
| 546 | 
            +
             | 
| 547 | 
            +
                _get = Method(NodeRequest.get)
         | 
| 548 | 
            +
                _query = Method(NodeRequest.query)
         | 
| 549 | 
            +
             | 
| 550 | 
            +
                def get(self, ual: UAL, options: dict = {}) -> dict:
         | 
| 551 | 
            +
                    arguments = self.input_service.get_asset_get_arguments(options)
         | 
| 552 | 
            +
             | 
| 553 | 
            +
                    max_number_of_retries = arguments.get("max_number_of_retries")
         | 
| 554 | 
            +
                    frequency = arguments.get("frequency")
         | 
| 555 | 
            +
                    state = arguments.get("state")
         | 
| 556 | 
            +
                    include_metadata = arguments.get("include_metadata")
         | 
| 557 | 
            +
                    content_type = arguments.get("content_type")
         | 
| 558 | 
            +
                    validate = arguments.get("validate")
         | 
| 559 | 
            +
                    output_format = arguments.get("output_format")
         | 
| 560 | 
            +
                    hash_function_id = arguments.get("hash_function_id")
         | 
| 561 | 
            +
                    paranet_ual = arguments.get("paranet_ual")
         | 
| 562 | 
            +
                    subject_ual = arguments.get("subject_ual")
         | 
| 563 | 
            +
             | 
| 564 | 
            +
                    ual_with_state = f"{ual}:{state}" if state else ual
         | 
| 565 | 
            +
                    get_public_operation_id: NodeResponseDict = self.node_service.get(
         | 
| 566 | 
            +
                        ual_with_state,
         | 
| 567 | 
            +
                        content_type,
         | 
| 568 | 
            +
                        include_metadata,
         | 
| 569 | 
            +
                        hash_function_id,
         | 
| 570 | 
            +
                        paranet_ual,
         | 
| 571 | 
            +
                        subject_ual,
         | 
| 572 | 
            +
                    )["operationId"]
         | 
| 573 | 
            +
             | 
| 574 | 
            +
                    get_public_operation_result = self.node_service.get_operation_result(
         | 
| 575 | 
            +
                        get_public_operation_id,
         | 
| 576 | 
            +
                        Operations.GET.value,
         | 
| 577 | 
            +
                        max_number_of_retries,
         | 
| 578 | 
            +
                        frequency,
         | 
| 579 | 
            +
                    )
         | 
| 580 | 
            +
             | 
| 581 | 
            +
                    if subject_ual:
         | 
| 582 | 
            +
                        if get_public_operation_result.get("data"):
         | 
| 583 | 
            +
                            return {
         | 
| 584 | 
            +
                                "operation": {
         | 
| 585 | 
            +
                                    "get": get_operation_status_object(
         | 
| 586 | 
            +
                                        get_public_operation_result, get_public_operation_id
         | 
| 587 | 
            +
                                    ),
         | 
| 588 | 
            +
                                },
         | 
| 589 | 
            +
                                "subject_ual_pairs": get_public_operation_result.get("data"),
         | 
| 590 | 
            +
                            }
         | 
| 591 | 
            +
                        if get_public_operation_result.get("status") != "FAILED":
         | 
| 592 | 
            +
                            get_public_operation_result["data"] = {
         | 
| 593 | 
            +
                                "errorType": "DKG_CLIENT_ERROR",
         | 
| 594 | 
            +
                                "errorMessage": "Unable to find assertion on the network!",
         | 
| 595 | 
            +
                            }
         | 
| 596 | 
            +
                            get_public_operation_result["status"] = "FAILED"
         | 
| 597 | 
            +
             | 
| 598 | 
            +
                        return {
         | 
| 599 | 
            +
                            "operation": {
         | 
| 600 | 
            +
                                "get": get_operation_status_object(
         | 
| 601 | 
            +
                                    get_public_operation_result, get_public_operation_id
         | 
| 602 | 
            +
                                ),
         | 
| 603 | 
            +
                            },
         | 
| 604 | 
            +
                        }
         | 
| 605 | 
            +
                    metadata = get_public_operation_result.get("data")
         | 
| 606 | 
            +
                    assertion = get_public_operation_result.get("data", {}).get("assertion", None)
         | 
| 607 | 
            +
             | 
| 608 | 
            +
                    if not assertion:
         | 
| 609 | 
            +
                        if get_public_operation_result.get("status") != "FAILED":
         | 
| 610 | 
            +
                            get_public_operation_result["data"] = {
         | 
| 611 | 
            +
                                "errorType": "DKG_CLIENT_ERROR",
         | 
| 612 | 
            +
                                "errorMessage": "Unable to find assertion on the network!",
         | 
| 613 | 
            +
                            }
         | 
| 614 | 
            +
                            get_public_operation_result["status"] = "FAILED"
         | 
| 615 | 
            +
             | 
| 616 | 
            +
                        return {
         | 
| 617 | 
            +
                            "operation": {
         | 
| 618 | 
            +
                                "get": get_operation_status_object(
         | 
| 619 | 
            +
                                    get_public_operation_result, get_public_operation_id
         | 
| 620 | 
            +
                                ),
         | 
| 621 | 
            +
                            },
         | 
| 622 | 
            +
                        }
         | 
| 623 | 
            +
             | 
| 624 | 
            +
                    if validate:
         | 
| 625 | 
            +
                        is_valid = True  # #TODO: Implement assertion validation logic
         | 
| 626 | 
            +
                        if not is_valid:
         | 
| 627 | 
            +
                            get_public_operation_result["data"] = {
         | 
| 628 | 
            +
                                "error_type": "DKG_CLIENT_ERROR",
         | 
| 629 | 
            +
                                "error_message": "Calculated root hashes don't match!",
         | 
| 630 | 
            +
                            }
         | 
| 631 | 
            +
             | 
| 632 | 
            +
                    formatted_assertion = "\n".join(
         | 
| 633 | 
            +
                        assertion.get("public", [])
         | 
| 634 | 
            +
                        + (
         | 
| 635 | 
            +
                            assertion.get("private")
         | 
| 636 | 
            +
                            if isinstance(assertion.get("private"), list)
         | 
| 637 | 
            +
                            else []
         | 
| 638 | 
            +
                        )
         | 
| 639 | 
            +
                    )
         | 
| 640 | 
            +
             | 
| 641 | 
            +
                    formatted_metadata = None
         | 
| 642 | 
            +
                    if output_format == OutputTypes.JSONLD.value:
         | 
| 643 | 
            +
                        formatted_assertion = self.to_jsonld(formatted_assertion)
         | 
| 644 | 
            +
             | 
| 645 | 
            +
                        if include_metadata:
         | 
| 646 | 
            +
                            formatted_metadata = self.to_jsonld("\n".join(metadata))
         | 
| 647 | 
            +
             | 
| 648 | 
            +
                    if output_format == OutputTypes.NQUADS.value:
         | 
| 649 | 
            +
                        formatted_assertion = self.to_nquads(
         | 
| 650 | 
            +
                            formatted_assertion, DEFAULT_RDF_FORMAT
         | 
| 651 | 
            +
                        )
         | 
| 652 | 
            +
                        if include_metadata:
         | 
| 653 | 
            +
                            formatted_metadata = self.to_nquads(
         | 
| 654 | 
            +
                                "\n".join(metadata), DEFAULT_RDF_FORMAT
         | 
| 655 | 
            +
                            )
         | 
| 656 | 
            +
             | 
| 657 | 
            +
                    result = {
         | 
| 658 | 
            +
                        "assertion": formatted_assertion,
         | 
| 659 | 
            +
                        "operation": {
         | 
| 660 | 
            +
                            "get": get_operation_status_object(
         | 
| 661 | 
            +
                                get_public_operation_result, get_public_operation_id
         | 
| 662 | 
            +
                            ),
         | 
| 663 | 
            +
                        },
         | 
| 664 | 
            +
                    }
         | 
| 665 | 
            +
             | 
| 666 | 
            +
                    if include_metadata and metadata:
         | 
| 667 | 
            +
                        result["metadata"] = formatted_metadata
         | 
| 668 | 
            +
             | 
| 669 | 
            +
                    return result
         | 
| 670 | 
            +
             | 
| 671 | 
            +
                # _extend_storing_period = Method(BlockchainRequest.extend_asset_storing_period)
         | 
| 672 | 
            +
             | 
| 673 | 
            +
                # def extend_storing_period(
         | 
| 674 | 
            +
                #     self,
         | 
| 675 | 
            +
                #     ual: UAL,
         | 
| 676 | 
            +
                #     additional_epochs: int,
         | 
| 677 | 
            +
                #     token_amount: Wei | None = None,
         | 
| 678 | 
            +
                # ) -> dict[str, UAL | TxReceipt]:
         | 
| 679 | 
            +
                #     parsed_ual = parse_ual(ual)
         | 
| 680 | 
            +
                #     blockchain_id, content_asset_storage_address, token_id = (
         | 
| 681 | 
            +
                #         parsed_ual["blockchain"],
         | 
| 682 | 
            +
                #         parsed_ual["contract_address"],
         | 
| 683 | 
            +
                #         parsed_ual["token_id"],
         | 
| 684 | 
            +
                #     )
         | 
| 685 | 
            +
             | 
| 686 | 
            +
                #     if token_amount is None:
         | 
| 687 | 
            +
                #         latest_finalized_state = self._get_latest_assertion_id(token_id)
         | 
| 688 | 
            +
                #         latest_finalized_state_size = self._get_assertion_size(
         | 
| 689 | 
            +
                #             latest_finalized_state
         | 
| 690 | 
            +
                #         )
         | 
| 691 | 
            +
             | 
| 692 | 
            +
                #         token_amount = int(
         | 
| 693 | 
            +
                #             self._get_bid_suggestion(
         | 
| 694 | 
            +
                #                 blockchain_id,
         | 
| 695 | 
            +
                #                 additional_epochs,
         | 
| 696 | 
            +
                #                 latest_finalized_state_size,
         | 
| 697 | 
            +
                #                 content_asset_storage_address,
         | 
| 698 | 
            +
                #                 latest_finalized_state,
         | 
| 699 | 
            +
                #                 DefaultParameters.HASH_FUNCTION_ID.value,
         | 
| 700 | 
            +
                #                 token_amount or BidSuggestionRange.LOW,
         | 
| 701 | 
            +
                #             )["bidSuggestion"]
         | 
| 702 | 
            +
                #         )
         | 
| 703 | 
            +
             | 
| 704 | 
            +
                #     receipt: TxReceipt = self._extend_storing_period(
         | 
| 705 | 
            +
                #         token_id, additional_epochs, token_amount
         | 
| 706 | 
            +
                #     )
         | 
| 707 | 
            +
             | 
| 708 | 
            +
                #     return {
         | 
| 709 | 
            +
                #         "UAL": ual,
         | 
| 710 | 
            +
                #         "operation": json.loads(Web3.to_json(receipt)),
         | 
| 711 | 
            +
                #     }
         | 
| 712 | 
            +
             | 
| 713 | 
            +
                _get_assertion_size = Method(BlockchainRequest.get_assertion_size)
         | 
| 714 | 
            +
             | 
| 715 | 
            +
                def to_jsonld(self, nquads: str):
         | 
| 716 | 
            +
                    options = {
         | 
| 717 | 
            +
                        "algorithm": "URDNA2015",
         | 
| 718 | 
            +
                        "format": "application/n-quads",
         | 
| 719 | 
            +
                    }
         | 
| 720 | 
            +
             | 
| 721 | 
            +
                    return jsonld.from_rdf(nquads, options)
         | 
| 722 | 
            +
             | 
| 723 | 
            +
                def to_nquads(self, content, input_format):
         | 
| 724 | 
            +
                    options = {
         | 
| 725 | 
            +
                        "algorithm": "URDNA2015",
         | 
| 726 | 
            +
                        "format": "application/n-quads",
         | 
| 727 | 
            +
                    }
         | 
| 728 | 
            +
             | 
| 729 | 
            +
                    if input_format:
         | 
| 730 | 
            +
                        options["inputFormat"] = input_format
         | 
| 731 | 
            +
                    try:
         | 
| 732 | 
            +
                        jsonld_data = jsonld.from_rdf(content, options)
         | 
| 733 | 
            +
                        canonized = jsonld.to_rdf(jsonld_data, options)
         | 
| 734 | 
            +
             | 
| 735 | 
            +
                        if isinstance(canonized, str):
         | 
| 736 | 
            +
                            return [line for line in canonized.split("\n") if line.strip()]
         | 
| 737 | 
            +
             | 
| 738 | 
            +
                    except Exception as e:
         | 
| 739 | 
            +
                        raise ValueError(f"Error processing content: {e}")
         |