diff --git a/bittensor/errors.py b/bittensor/errors.py index 36dac7c588..5be2090441 100644 --- a/bittensor/errors.py +++ b/bittensor/errors.py @@ -80,3 +80,8 @@ class KeyFileError(Exception): """Error thrown when the keyfile is corrupt, non-writable, non-readable or the password used to decrypt is invalid.""" pass + + +class MetadataError(ChainTransactionError): + r"""Error raised when metadata commitment transaction fails.""" + pass diff --git a/bittensor/extrinsics/serving.py b/bittensor/extrinsics/serving.py index 4bdfde7f76..11eb8e24e4 100644 --- a/bittensor/extrinsics/serving.py +++ b/bittensor/extrinsics/serving.py @@ -20,6 +20,7 @@ from dataclasses import asdict import bittensor.utils.networking as net from rich.prompt import Confirm +from ..errors import MetadataError def serve_extrinsic( @@ -203,3 +204,89 @@ def serve_axon_extrinsic( prompt=prompt, ) return serve_success + + +def publish_metadata( + subtensor: "bittensor.subtensor", + wallet: "bittensor.wallet", + netuid: int, + type: str, + data: str, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, +) -> bool: + """ + Publishes metadata on the bittensor network using the specified wallet and network identifier. + + Args: + subtensor (bittensor.subtensor): + The subtensor instance representing the bittensor blockchain connection. + wallet (bittensor.wallet): + The wallet object used for authentication in the transaction. + netuid (int): + Network UID on which the metadata is to be published. + type (str): + The data type of the information being submitted. It should be one of the following: + 'Sha256', 'Blake256', 'Keccak256', or 'Raw0-128'. This specifies the format or + hashing algorithm used for the data. + data (str): + The actual metadata content to be published. This should be formatted or hashed + according to the 'type' specified. (Note: max str length of 128 bytes) + wait_for_inclusion (bool, optional): + If True, the function will wait for the extrinsic to be included in a block before returning. + Defaults to False. + wait_for_finalization (bool, optional): + If True, the function will wait for the extrinsic to be finalized on the chain before returning. + Defaults to True. + + Returns: + bool: + True if the metadata was successfully published (and finalized if specified). False otherwise. + + Raises: + MetadataError: + If there is an error in submitting the extrinsic or if the response from the blockchain indicates failure. + """ + + wallet.hotkey + + with subtensor.substrate as substrate: + call = substrate.compose_call( + call_module="Commitments", + call_function="set_commitment", + call_params={"netuid": netuid, "info": {"fields": [[{f"{type}": data}]]}}, + ) + + extrinsic = substrate.create_signed_extrinsic(call=call, keypair=wallet.hotkey) + response = substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True + response.process_events() + if response.is_success: + return True + else: + raise MetadataError(response.error_message) + + +from retry import retry +from typing import Optional + + +def get_metadata(self, netuid: int, hotkey: str, block: Optional[int] = None) -> str: + @retry(delay=2, tries=3, backoff=2, max_delay=4) + def make_substrate_call_with_retry(): + with self.substrate as substrate: + return substrate.query( + module="Commitments", + storage_function="CommitmentOf", + params=[netuid, hotkey], + block_hash=None if block == None else substrate.get_block_hash(block), + ) + + commit_data = make_substrate_call_with_retry() + return commit_data.value diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 9e9e361db7..bdf9622f07 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -52,7 +52,12 @@ ) from .extrinsics.staking import add_stake_extrinsic, add_stake_multiple_extrinsic from .extrinsics.unstaking import unstake_extrinsic, unstake_multiple_extrinsic -from .extrinsics.serving import serve_extrinsic, serve_axon_extrinsic +from .extrinsics.serving import ( + serve_extrinsic, + serve_axon_extrinsic, + publish_metadata, + get_metadata, +) from .extrinsics.registration import ( register_extrinsic, burned_register_extrinsic, @@ -2143,6 +2148,17 @@ def make_substrate_call_with_retry(): return make_substrate_call_with_retry() + """ Make some commitment on-chain about arbitary data """ + + def commit(self, wallet, netuid: int, data: str): + publish_metadata(self, wallet, netuid, f"Raw{len(data)}", data.encode()) + + def get_commitment(self, netuid: int, uid: int, block: Optional[int] = None) -> str: + metagraph = self.metagraph(netuid) + hotkey = metagraph.hotkeys[uid] + + return get_metadata(self, netuid, hotkey, block) + ######################## #### Standard Calls #### ########################