diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3fa2f34c..55ea66fef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 6.4.4 / 2023-12-14 + +## What's Changed + +* Merge/master642 staging no-ff by @ifrit98 in https://github.com/opentensor/bittensor/pull/1615 +* print help message on error for subcommands by @ifrit98 in https://github.com/opentensor/bittensor/pull/1618 +* Metadata/commitments by @ifrit98 in https://github.com/opentensor/bittensor/pull/1621 + +## New Contributors +* @omahs made their first contribution in https://github.com/opentensor/bittensor/pull/1553 +* @surcyf123 made their first contribution in https://github.com/opentensor/bittensor/pull/1569 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.4.2...v6.4.4 + + ## 6.4.2 / 2023-12-07 ## What's Changed diff --git a/VERSION b/VERSION index 04757a5d3c..8b38d69c52 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.4.2 \ No newline at end of file +6.4.4 \ No newline at end of file diff --git a/bittensor/__init__.py b/bittensor/__init__.py index 3006a8de15..eaaedba33a 100644 --- a/bittensor/__init__.py +++ b/bittensor/__init__.py @@ -27,7 +27,8 @@ nest_asyncio.apply() # Bittensor code and protocol version. -__version__ = "6.4.2" +__version__ = "6.4.4" + version_split = __version__.split(".") __version_as_int__ = ( (100 * int(version_split[0])) diff --git a/bittensor/cli.py b/bittensor/cli.py index fae049c34d..dea70c8cd1 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -134,6 +134,20 @@ } +class CLIErrorParser(argparse.ArgumentParser): + """ + Custom ArgumentParser for better error messages. + """ + + def error(self, message): + """ + This method is called when an error occurs. It prints a custom error message. + """ + sys.stderr.write(f"Error: {message}\n") + self.print_help() + sys.exit(2) + + class cli: """ Implementation of the Command Line Interface (CLI) class for the Bittensor protocol. @@ -190,7 +204,7 @@ def __create_parser__() -> "argparse.ArgumentParser": argparse.ArgumentParser: An argument parser object for Bittensor CLI. """ # Define the basic argument parser. - parser = argparse.ArgumentParser( + parser = CLIErrorParser( description=f"bittensor cli v{bittensor.__version__}", usage="btcli ", add_help=True, 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 dd08c75953..4553529a26 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, @@ -2111,6 +2116,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 #### ########################