From b562af8d8c36d306abab1cf9235738d4c6cabe24 Mon Sep 17 00:00:00 2001 From: C H Date: Fri, 29 Mar 2024 01:09:35 +0800 Subject: [PATCH 01/14] Added sign to keypair --- cybertensor/keypair.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index 416aa05..9827640 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -157,6 +157,31 @@ def create_from_private_key( prefix=prefix, ) + def sign(self, data: Union[bytes, str]) -> bytes: + """ + Creates a signature for given data + + Parameters + ---------- + data: data to sign in bytes or hex string format + + Returns + ------- + signature in bytes + + """ + if data[0:2] == '0x': + data = bytes.fromhex(data[2:]) + elif type(data) is str: + data = data.encode() + + if not self.private_key: + raise ValueError('No private key set to create signatures') + + # return signature + # TODO think about update to ADR-36 + return PrivateKey(self.private_key).sign(data, deterministic=True) + def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: """ Verifies data with specified signature @@ -182,14 +207,8 @@ def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: if type(signature) is not bytes: raise TypeError("Signature should be of type bytes or a hex-string") - # TODO replace verify function to correct - verified = True - # verified = crypto_verify_fn(signature, data, self.public_key) - # - # if not verified: - # verified = crypto_verify_fn(signature, b'' + data + b'', self.public_key) - # - return verified + # TODO think about update to ADR-36 + return PublicKey(self.public_key).verify(data, signature) def __repr__(self): if self.address: From 99b1afab253049b3da97baae33f5b1e3142f2fa9 Mon Sep 17 00:00:00 2001 From: C H Date: Fri, 29 Mar 2024 01:10:07 +0800 Subject: [PATCH 02/14] Added catch to txs simulations errors --- cybertensor/cwtensor.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cybertensor/cwtensor.py b/cybertensor/cwtensor.py index b0c0409..6c72d96 100644 --- a/cybertensor/cwtensor.py +++ b/cybertensor/cwtensor.py @@ -345,7 +345,11 @@ def make_call_with_retry(self, wait_for_finalization: bool, self.contract.execute(msg, signer_wallet, gas, funds=funds) return True else: - tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) + try: + # NOTE will raise exeption if tx simulation is not successful, need to catch it + tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) + except Exception as e: + raise error(e.__str__()) try: tx.wait_to_complete() if tx.response.is_successful(): @@ -406,7 +410,12 @@ def make_call_with_retry_2(self, wait_for_finalization: bool, self.contract.execute(msg, signer_wallet, gas, funds=funds) return True, None else: - tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) + try: + # NOTE will raise exeption if tx simulation is not successful, need to catch it + tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) + except Exception as e: + return False, e.__str__() + try: tx.wait_to_complete() if tx.response.is_successful(): From 3cf85702ebb9f71a017d8a6009906382a3c76e25 Mon Sep 17 00:00:00 2001 From: C H Date: Fri, 29 Mar 2024 01:10:27 +0800 Subject: [PATCH 03/14] Updated serve msgs api --- cybertensor/contract/schema/execute.json | 16 +++++++++------- cybertensor/contract/schema/query.json | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/cybertensor/contract/schema/execute.json b/cybertensor/contract/schema/execute.json index a9599e1..1070417 100644 --- a/cybertensor/contract/schema/execute.json +++ b/cybertensor/contract/schema/execute.json @@ -177,9 +177,7 @@ ], "properties": { "ip": { - "type": "integer", - "format": "uint128", - "minimum": 0.0 + "$ref": "#/definitions/Uint128" }, "ip_type": { "type": "integer", @@ -239,9 +237,7 @@ ], "properties": { "ip": { - "type": "integer", - "format": "uint128", - "minimum": 0.0 + "$ref": "#/definitions/Uint128" }, "ip_type": { "type": "integer", @@ -1449,5 +1445,11 @@ }, "additionalProperties": false } - ] + ], + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } } diff --git a/cybertensor/contract/schema/query.json b/cybertensor/contract/schema/query.json index d2bf796..3904130 100644 --- a/cybertensor/contract/schema/query.json +++ b/cybertensor/contract/schema/query.json @@ -648,6 +648,29 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "get_subnetwork_n" + ], + "properties": { + "get_subnetwork_n": { + "type": "object", + "required": [ + "netuid" + ], + "properties": { + "netuid": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ From e8168ac0a12d0cad6b5e998bea34cedf41c3c9e4 Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Sat, 30 Mar 2024 13:32:51 +0700 Subject: [PATCH 04/14] - update error handlers for data signature --- cybertensor/errors.py | 8 +++++++- cybertensor/keypair.py | 15 ++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cybertensor/errors.py b/cybertensor/errors.py index 8ae0e83..10fa638 100644 --- a/cybertensor/errors.py +++ b/cybertensor/errors.py @@ -105,6 +105,11 @@ class KeyFileError(Exception): pass +class ConfigurationError(Exception): + r"""Error raised when a private key is not found.""" + pass + + class MetadataError(ChainTransactionError): r"""Error raised when metadata commitment transaction fails.""" @@ -112,7 +117,7 @@ class MetadataError(ChainTransactionError): class InvalidRequestNameError(Exception): - r"""This exception is raised when the request name is invalid. Ususally indicates a broken URL.""" + r"""This exception is raised when the request name is invalid. Usually indicates a broken URL.""" pass @@ -146,6 +151,7 @@ class PriorityException(Exception): pass + class PostProcessException(Exception): r"""This exception is raised when the response headers cannot be updated.""" diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index 9827640..5093700 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -27,6 +27,7 @@ ) from cybertensor import __chain_address_prefix__ +from cybertensor.errors import ConfigurationError class Keypair: @@ -62,7 +63,7 @@ def __init__( address = Address(PublicKey(private_key_obj.public_key), prefix).__str__() if not public_key: - raise ValueError("No public key provided") + raise ConfigurationError("No public key provided") self.public_key: bytes = public_key @@ -174,13 +175,15 @@ def sign(self, data: Union[bytes, str]) -> bytes: data = bytes.fromhex(data[2:]) elif type(data) is str: data = data.encode() + elif type(data) is not bytes: + raise TypeError(f"Signed data should be of type bytes or hex-string, given data type is {type(data)}") if not self.private_key: - raise ValueError('No private key set to create signatures') + raise ConfigurationError("No private key set to create signatures") # return signature # TODO think about update to ADR-36 - return PrivateKey(self.private_key).sign(data, deterministic=True) + return PrivateKey(self.private_key).sign(message=data, deterministic=True) def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: """ @@ -200,15 +203,17 @@ def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: data = bytes.fromhex(data[2:]) elif type(data) is str: data = data.encode() + elif type(data) is not bytes: + raise TypeError(f"Signed data should be of type bytes or hex-string, given data type is {type(data)}") if type(signature) is str and signature[0:2] == "0x": signature = bytes.fromhex(signature[2:]) if type(signature) is not bytes: - raise TypeError("Signature should be of type bytes or a hex-string") + raise TypeError(f"Signature should be of type bytes or a hex-string, given signature type is {type(signature)}") # TODO think about update to ADR-36 - return PublicKey(self.public_key).verify(data, signature) + return PublicKey(self.public_key).verify(message=data, signature=signature) def __repr__(self): if self.address: From 745235f179684991be1afb97fd2919496751751a Mon Sep 17 00:00:00 2001 From: C H Date: Sun, 31 Mar 2024 19:34:51 +0800 Subject: [PATCH 05/14] Added signer and verifier --- cybertensor/__init__.py | 2 +- cybertensor/axon.py | 10 +++- cybertensor/dendrite.py | 9 +++- cybertensor/keypair.py | 111 +++++++++++++++++++++++++++++++++------- cybertensor/synapse.py | 9 ++++ 5 files changed, 119 insertions(+), 22 deletions(-) diff --git a/cybertensor/__init__.py b/cybertensor/__init__.py index d10f1c7..eedc51d 100644 --- a/cybertensor/__init__.py +++ b/cybertensor/__init__.py @@ -30,7 +30,7 @@ nest_asyncio.apply() # Cybertensor code and protocol version. -__version__ = "0.1.6" +__version__ = "0.1.7" version_split = __version__.split(".") __version_as_int__ = ( (100 * int(version_split[0])) diff --git a/cybertensor/axon.py b/cybertensor/axon.py index 84da36b..d970fbe 100644 --- a/cybertensor/axon.py +++ b/cybertensor/axon.py @@ -847,7 +847,9 @@ async def default_verify(self, synapse: cybertensor.Synapse): """ # Build the keypair from the dendrite_hotkey if synapse.dendrite is not None: - keypair = Keypair(address=synapse.dendrite.hotkey) # type: ignore + cybertensor.logging.info(f"dendrite: {synapse.dendrite}") + # keypair = Keypair(address=synapse.dendrite.hotkey, public_key=synapse.dendrite.pubkey) + keypair = Keypair(address=synapse.dendrite.hotkey) # Build the signature messages. message = (f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{self.wallet.hotkey.address}." f"{synapse.dendrite.uuid}.{synapse.computed_body_hash}") @@ -864,7 +866,11 @@ async def default_verify(self, synapse: cybertensor.Synapse): ): raise Exception("Nonce is too small") - if not keypair.verify(message, synapse.dendrite.signature): # type: ignore + verified = keypair.verify(message, synapse.dendrite.signature) + cybertensor.logging.info(f"\nAXON VERIFY MSG: {message}") + cybertensor.logging.info(f"AXON VERIFY SGN: {synapse.dendrite.signature}") + cybertensor.logging.info(f"AXON VERIFY : {verified}") + if not verified: raise Exception( f"Signature mismatch with {message} and {synapse.dendrite.signature}" ) diff --git a/cybertensor/dendrite.py b/cybertensor/dendrite.py index d919592..7132e06 100644 --- a/cybertensor/dendrite.py +++ b/cybertensor/dendrite.py @@ -620,6 +620,7 @@ def preprocess_synapse_for_request( nonce=time.monotonic_ns(), uuid=self.uuid, hotkey=self.keypair.address, + pubkey=self.keypair.public_key ) # Build the Axon headers using the target axon's details @@ -627,11 +628,17 @@ def preprocess_synapse_for_request( ip=target_axon_info.ip, port=target_axon_info.port, hotkey=target_axon_info.hotkey, + pubkey=None ) # Sign the request using the dendrite, axon info, and the synapse body hash message = f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{synapse.axon.hotkey}.{synapse.dendrite.uuid}.{synapse.body_hash}" - synapse.dendrite.signature = f"0x{self.keypair.sign(message).hex()}" + signed = self.keypair.sign(message) + cybertensor.logging.info(f"\nDENDRITE ADDR {self.keypair.address}") + cybertensor.logging.info(f"DENDRITE MSG {message}") + cybertensor.logging.info(f"DENDRITE SGN {signed}") + cybertensor.logging.info(f"DENDRITE SGN 0x{signed.hex()}\n") + synapse.dendrite.signature = f"0x{signed.hex()}" return synapse diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index 5093700..de6101a 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -15,28 +15,40 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. +import base64 +import hashlib +import logging from typing import Optional, Union +from bech32 import ( # pylint: disable=wrong-import-order + bech32_encode, + convertbits, +) from bip39 import bip39_generate, bip39_validate from cosmpy.crypto.address import Address +from cosmpy.crypto.hashfuncs import ripemd160 from cosmpy.crypto.keypairs import PrivateKey, PublicKey from cosmpy.mnemonic import ( derive_child_key_from_mnemonic, COSMOS_HD_PATH, validate_mnemonic_and_normalise, ) - from cybertensor import __chain_address_prefix__ +from ecdsa import ( # type: ignore # pylint: disable=wrong-import-order + SECP256k1, + SigningKey, + VerifyingKey, +) from cybertensor.errors import ConfigurationError class Keypair: def __init__( - self, - address: str = None, - public_key: Union[bytes, str] = None, - private_key: Union[bytes, str] = None, - prefix: Optional[str] = None, + self, + address: str = None, + public_key: Union[bytes, str] = None, + private_key: Union[bytes, str] = None, + prefix: Optional[str] = None, ): """ Allows generation of Keypairs from a variety of input combination, such as a public/private key combination, @@ -62,8 +74,12 @@ def __init__( public_key = private_key_obj.public_key.public_key address = Address(PublicKey(private_key_obj.public_key), prefix).__str__() - if not public_key: - raise ConfigurationError("No public key provided") + if public_key and isinstance(public_key, str): + # self.public_key = bytes(public_key, 'utf-8') + self.public_key = public_key + + # if not public_key: + # raise ValueError("No public key provided") self.public_key: bytes = public_key @@ -105,7 +121,7 @@ def validate_mnemonic(cls, mnemonic: str) -> bool: @classmethod def create_from_mnemonic( - cls, mnemonic: str, prefix: Optional[str] = None + cls, mnemonic: str, prefix: Optional[str] = None ) -> "Keypair": """ Create a Keypair for given mnemonic @@ -135,7 +151,7 @@ def create_from_mnemonic( @classmethod def create_from_private_key( - cls, private_key: Union[bytes, str], prefix: Optional[str] = None + cls, private_key: Union[bytes, str], prefix: Optional[str] = None ) -> "Keypair": """ Creates Keypair for specified public/private keys @@ -158,6 +174,45 @@ def create_from_private_key( prefix=prefix, ) + def get_address_from_public_key(self, public_key: str) -> str: + """ + Get the address from the public key. + + :param public_key: the public key + :return: str + """ + public_key_bytes = bytes.fromhex(public_key) + s = hashlib.new("sha256", public_key_bytes).digest() + r = ripemd160(s) + five_bit_r = convertbits(r, 8, 5) + if five_bit_r is None: # pragma: nocover + raise TypeError("Unsuccessful bech32.convertbits call") + + ## TODO add configuration for chain prefix + address = bech32_encode(__chain_address_prefix__, five_bit_r) + return address + + def recover_message(self, message: bytes, signature: str) -> tuple[Address, ...]: + public_keys = self.recover_public_keys_from_message(message, signature) + addresses = [ + self.get_address_from_public_key(public_key) for public_key in public_keys + ] + return tuple(addresses) + + def recover_public_keys_from_message(self, message: bytes, signature: str) -> tuple[str, ...]: + signature_b64 = base64.b64decode(signature) + verifying_keys = VerifyingKey.from_public_key_recovery( + signature_b64, + message, + SECP256k1, + hashfunc=hashlib.sha256, + ) + public_keys = [ + verifying_key.to_string("compressed").hex() + for verifying_key in verifying_keys + ] + return tuple(public_keys) + def sign(self, data: Union[bytes, str]) -> bytes: """ Creates a signature for given data @@ -181,9 +236,14 @@ def sign(self, data: Union[bytes, str]) -> bytes: if not self.private_key: raise ConfigurationError("No private key set to create signatures") - # return signature - # TODO think about update to ADR-36 - return PrivateKey(self.private_key).sign(message=data, deterministic=True) + signature_compact = PrivateKey(self.private_key).sign(data, deterministic=True) + signature_base64_str = base64.b64encode(signature_compact).decode("utf-8").encode() + + logging.debug(f"\nKEYPAIR address: {self.address}") + logging.debug(f"KEYPAIR Signing data: {data}") + logging.debug(f"KEYPAIR Generated signature: {signature_base64_str}\n") + + return signature_base64_str def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: """ @@ -209,11 +269,26 @@ def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: if type(signature) is str and signature[0:2] == "0x": signature = bytes.fromhex(signature[2:]) - if type(signature) is not bytes: - raise TypeError(f"Signature should be of type bytes or a hex-string, given signature type is {type(signature)}") - - # TODO think about update to ADR-36 - return PublicKey(self.public_key).verify(message=data, signature=signature) + for address in self.recover_message(data, signature): + if self.address == address: + logging.debug(f"KEYPAIR Verified data: True") + return True + logging.debug(f"KEYPAIR Verified data: False") + return False + + # recovered_addresses = self.recover_message(data, signature) + # logging.debug(f"\nKEYPAIR Verifying data: {data}") + # logging.debug(f"KEYPAIR Recovered addresses: {recovered_addresses}") + # if self.address == recovered_addresses[0]: + # logging.debug(f"KEYPAIR Verified data: True 1") + # return True + # + # if self.address == recovered_addresses[1]: + # logging.debug(f"KEYPAIR Verified data: True 2\n") + # return True + # + # logging.debug(f"KEYPAIR Verified data: False\n") + # return False def __repr__(self): if self.address: diff --git a/cybertensor/synapse.py b/cybertensor/synapse.py index 20ca129..a92bb12 100644 --- a/cybertensor/synapse.py +++ b/cybertensor/synapse.py @@ -257,6 +257,15 @@ class Config: allow_mutation=True, ) + # The cybertensor version on the terminal as an int. + pubkey: Optional[str] = pydantic.Field( + title="pubkey", + description="The pubkey string of the terminal wallet.", + examples="", + default=None, + allow_mutation=True, + ) + class Synapse(pydantic.BaseModel): """ From c11e193371604444ed2e7cf05157b4c493fc3f1e Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Sun, 31 Mar 2024 19:08:14 +0700 Subject: [PATCH 06/14] - fix parameter `--axon.external_ip` - add tracing for a gas used amount and a transaction message --- cybertensor/axon.py | 13 +++---------- cybertensor/cwtensor.py | 28 +++++++++++++++------------- cybertensor/messages/prometheus.py | 4 ++-- cybertensor/messages/serving.py | 2 +- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/cybertensor/axon.py b/cybertensor/axon.py index 84da36b..ac1a1c1 100644 --- a/cybertensor/axon.py +++ b/cybertensor/axon.py @@ -40,6 +40,7 @@ from starlette.responses import Response import cybertensor +import cybertensor.utils.networking as net from cybertensor.config import Config from cybertensor.errors import * from cybertensor.keypair import Keypair @@ -309,16 +310,8 @@ def __init__( self.uuid = str(uuid.uuid1()) self.ip = self.config.axon.ip self.port = self.config.axon.port - self.external_ip = ( - self.config.axon.external_ip - if self.config.axon.external_ip is not None - else cybertensor.utils.networking.get_external_ip() - ) - self.external_port = ( - self.config.axon.external_port - if self.config.axon.external_port is not None - else self.config.axon.port - ) + self.external_ip = self.config.axon.external_ip or net.get_external_ip() + self.external_port = self.config.axon.external_port or self.config.axon.port self.full_address = str(self.config.axon.ip) + ":" + str(self.config.axon.port) self.started = False diff --git a/cybertensor/cwtensor.py b/cybertensor/cwtensor.py index 6c72d96..d83e14f 100644 --- a/cybertensor/cwtensor.py +++ b/cybertensor/cwtensor.py @@ -346,14 +346,14 @@ def make_call_with_retry(self, wait_for_finalization: bool, return True else: try: - # NOTE will raise exeption if tx simulation is not successful, need to catch it + # NOTE will raise exception if tx simulation is not successful, need to catch it tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) except Exception as e: raise error(e.__str__()) try: tx.wait_to_complete() if tx.response.is_successful(): - print(f'Gas used: {tx.response.gas_used}') + cybertensor.logging.trace(f'Gas used: {tx.response.gas_used}') return True else: raise error(tx.response.logs) @@ -375,6 +375,7 @@ def _execute_contract(self, wait_for_finalization: bool, signer_wallet = LocalWallet( PrivateKey(_private_key), self.address_prefix ) + cybertensor.logging.trace(f'tx msg {msg}\tsigner_wallet {signer_wallet}') res = self.make_call_with_retry( wait_for_finalization=wait_for_finalization, msg=msg, @@ -406,12 +407,13 @@ def _execute_contract(self, wait_for_finalization: bool, def make_call_with_retry_2(self, wait_for_finalization: bool, msg: dict, signer_wallet: LocalWallet, gas: Optional[int] = cybertensor.__default_gas__, funds: Optional[str] = None) -> [bool, Optional[str]]: + cybertensor.logging.trace(f'tx msg {msg}\tsigner_wallet {signer_wallet}') if not wait_for_finalization: self.contract.execute(msg, signer_wallet, gas, funds=funds) return True, None else: try: - # NOTE will raise exeption if tx simulation is not successful, need to catch it + # NOTE will raise exception if tx simulation is not successful, need to catch it tx = self.contract.execute(msg, signer_wallet, gas, funds=funds) except Exception as e: return False, e.__str__() @@ -426,9 +428,9 @@ def make_call_with_retry_2(self, wait_for_finalization: bool, except Exception as e: return False, e.__str__() - #################### - #### Websocket Interface related - #################### + ##################################### + #### Websocket Interface related #### + ##################################### def connect_websocket(self): """ (Re)creates the websocket connection, if the URL contains a 'ws' or 'wss' scheme @@ -1466,9 +1468,9 @@ def root_set_weights( prompt=prompt, ) - ##################################### - #### Hyper parameter calls. #### - ##################################### + ############################### + #### Hyper parameter calls #### + ############################### def difficulty(self, netuid: int, block: Optional[int] = None) -> Optional[int]: """ @@ -1758,9 +1760,9 @@ def tx_rate_limit(self, block: Optional[int] = None) -> Optional[int]: """ return self.contract.query({"get_tx_rate_limit": {}}) - ##################################### + ############################ #### Network Parameters #### - ##################################### + ############################ def get_subnet_burn_cost(self, block: Optional[int] = None) -> Optional[int]: lock_cost = self.contract.query({"get_network_registration_cost": {}}) @@ -2101,9 +2103,9 @@ def get_stake_info_for_coldkeys( return StakeInfo.list_of_tuple_from_list_any(result) - ######################################## + ####################################### #### Neuron information per subnet #### - ######################################## + ####################################### def is_hotkey_registered_any( self, hotkey: str, block: Optional[int] = None diff --git a/cybertensor/messages/prometheus.py b/cybertensor/messages/prometheus.py index d7d3262..a073cfe 100644 --- a/cybertensor/messages/prometheus.py +++ b/cybertensor/messages/prometheus.py @@ -51,14 +51,14 @@ def prometheus_message( or returns ``false`` if the extrinsic fails to be finalized within the timeout. Returns: success (bool): - Flag is ``true`` if extrinsic was finalized or uncluded in the block. + Flag is ``true`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``true``. """ # ---- Get external ip ---- if ip is None: try: - external_ip = net.get_external_ip() + external_ip = cwtensor.config.axon.external_ip or net.get_external_ip() console.print( f":white_heavy_check_mark: [green]Found external ip: {external_ip}[/green]" ) diff --git a/cybertensor/messages/serving.py b/cybertensor/messages/serving.py index bd962f4..759a520 100644 --- a/cybertensor/messages/serving.py +++ b/cybertensor/messages/serving.py @@ -169,7 +169,7 @@ def serve_axon_message( # ---- Get external ip ---- if axon.external_ip is None: try: - external_ip = net.get_external_ip() + external_ip = cwtensor.config.axon.external_ip or net.get_external_ip() console.print( f":white_heavy_check_mark: [green]Found external ip: {external_ip}[/green]" ) From 6feacf4c4f84e55bba988a06e70da30e02f3661a Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Mon, 1 Apr 2024 16:02:49 +0700 Subject: [PATCH 07/14] - add websocket package to the requirements --- requirements/prod.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/prod.txt b/requirements/prod.txt index 4a421de..94f76b3 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -37,3 +37,4 @@ cosmpy>=0.9.1 ecdsa<0.15 py-bip39-bindings urllib3==1.26.15 +websocket \ No newline at end of file From c7cd327638f4d770e5c85f8b1d4c98a381a346fc Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Thu, 4 Apr 2024 15:20:05 +0700 Subject: [PATCH 08/14] - update logging - update docs --- cybertensor/axon.py | 6 ++--- cybertensor/dendrite.py | 8 +++---- cybertensor/keypair.py | 33 ++++++++++++++-------------- cybertensor/messages/registration.py | 2 +- cybertensor/messages/serving.py | 9 +++----- cybertensor/synapse.py | 15 +++++++------ 6 files changed, 36 insertions(+), 37 deletions(-) diff --git a/cybertensor/axon.py b/cybertensor/axon.py index 5df40ff..b65c306 100644 --- a/cybertensor/axon.py +++ b/cybertensor/axon.py @@ -860,9 +860,9 @@ async def default_verify(self, synapse: cybertensor.Synapse): raise Exception("Nonce is too small") verified = keypair.verify(message, synapse.dendrite.signature) - cybertensor.logging.info(f"\nAXON VERIFY MSG: {message}") - cybertensor.logging.info(f"AXON VERIFY SGN: {synapse.dendrite.signature}") - cybertensor.logging.info(f"AXON VERIFY : {verified}") + cybertensor.logging.debug(f"\nAXON VERIFY MSG: {message}") + cybertensor.logging.debug(f"AXON VERIFY SGN: {synapse.dendrite.signature}") + cybertensor.logging.debug(f"AXON VERIFY : {verified}") if not verified: raise Exception( f"Signature mismatch with {message} and {synapse.dendrite.signature}" diff --git a/cybertensor/dendrite.py b/cybertensor/dendrite.py index 7132e06..2f8b5c3 100644 --- a/cybertensor/dendrite.py +++ b/cybertensor/dendrite.py @@ -634,10 +634,10 @@ def preprocess_synapse_for_request( # Sign the request using the dendrite, axon info, and the synapse body hash message = f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{synapse.axon.hotkey}.{synapse.dendrite.uuid}.{synapse.body_hash}" signed = self.keypair.sign(message) - cybertensor.logging.info(f"\nDENDRITE ADDR {self.keypair.address}") - cybertensor.logging.info(f"DENDRITE MSG {message}") - cybertensor.logging.info(f"DENDRITE SGN {signed}") - cybertensor.logging.info(f"DENDRITE SGN 0x{signed.hex()}\n") + cybertensor.logging.debug(f"\nDENDRITE ADDR {self.keypair.address}") + cybertensor.logging.debug(f"DENDRITE MSG {message}") + cybertensor.logging.debug(f"DENDRITE SGN {signed}") + cybertensor.logging.debug(f"DENDRITE SGN 0x{signed.hex()}\n") synapse.dendrite.signature = f"0x{signed.hex()}" return synapse diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index de6101a..c854555 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -17,7 +17,6 @@ import base64 import hashlib -import logging from typing import Optional, Union from bech32 import ( # pylint: disable=wrong-import-order @@ -25,6 +24,11 @@ convertbits, ) from bip39 import bip39_generate, bip39_validate +from ecdsa import ( # type: ignore # pylint: disable=wrong-import-order + SECP256k1, + SigningKey, + VerifyingKey, +) from cosmpy.crypto.address import Address from cosmpy.crypto.hashfuncs import ripemd160 from cosmpy.crypto.keypairs import PrivateKey, PublicKey @@ -33,12 +37,9 @@ COSMOS_HD_PATH, validate_mnemonic_and_normalise, ) + +import cybertensor as ct from cybertensor import __chain_address_prefix__ -from ecdsa import ( # type: ignore # pylint: disable=wrong-import-order - SECP256k1, - SigningKey, - VerifyingKey, -) from cybertensor.errors import ConfigurationError @@ -76,13 +77,13 @@ def __init__( if public_key and isinstance(public_key, str): # self.public_key = bytes(public_key, 'utf-8') - self.public_key = public_key + self.public_key: str = public_key + elif public_key and isinstance(public_key, bytes): + self.public_key: bytes = public_key # if not public_key: # raise ValueError("No public key provided") - self.public_key: bytes = public_key - self.address: str = address self.private_key: bytes = private_key @@ -236,12 +237,12 @@ def sign(self, data: Union[bytes, str]) -> bytes: if not self.private_key: raise ConfigurationError("No private key set to create signatures") - signature_compact = PrivateKey(self.private_key).sign(data, deterministic=True) + signature_compact = PrivateKey(self.private_key).sign(message=data, deterministic=True) signature_base64_str = base64.b64encode(signature_compact).decode("utf-8").encode() - logging.debug(f"\nKEYPAIR address: {self.address}") - logging.debug(f"KEYPAIR Signing data: {data}") - logging.debug(f"KEYPAIR Generated signature: {signature_base64_str}\n") + ct.logging.debug(f"\nKEYPAIR address: {self.address}") + ct.logging.debug(f"KEYPAIR Signing data: {data}") + ct.logging.debug(f"KEYPAIR Generated signature: {signature_base64_str}\n") return signature_base64_str @@ -269,11 +270,11 @@ def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: if type(signature) is str and signature[0:2] == "0x": signature = bytes.fromhex(signature[2:]) - for address in self.recover_message(data, signature): + for address in self.recover_message(message=data, signature=signature): if self.address == address: - logging.debug(f"KEYPAIR Verified data: True") + ct.logging.debug(f"KEYPAIR Verified data: True") return True - logging.debug(f"KEYPAIR Verified data: False") + ct.logging.debug(f"KEYPAIR Verified data: False") return False # recovered_addresses = self.recover_message(data, signature) diff --git a/cybertensor/messages/registration.py b/cybertensor/messages/registration.py index f2c3bf9..529ea76 100644 --- a/cybertensor/messages/registration.py +++ b/cybertensor/messages/registration.py @@ -88,7 +88,7 @@ def register_message( ) if not neuron.is_null: cybertensor.logging.debug( - f"Wallet {wallet} is already registered on {neuron.netuid} with {neuron.uid}" + f"Wallet {wallet} is already registered on network: {neuron.netuid} with user id: {neuron.uid}" ) return True diff --git a/cybertensor/messages/serving.py b/cybertensor/messages/serving.py index 759a520..ffecf82 100644 --- a/cybertensor/messages/serving.py +++ b/cybertensor/messages/serving.py @@ -96,11 +96,8 @@ def serve_message( "placeholder1": neuron.axon_info.placeholder1, "placeholder2": neuron.axon_info.placeholder2, } - output = params.copy() - output["coldkey"] = wallet.coldkeypub.address - output["hotkey"] = wallet.hotkey.address if neuron_up_to_date: - cybertensor.logging.debug( + cybertensor.logging.info( f"Axon already served on: AxonInfo({wallet.hotkey.address},{ip}:{port}) " ) return True @@ -135,8 +132,8 @@ def serve_message( f"Axon failed to served with error: {error_message} " ) return False - else: - return True + + return True def serve_axon_message( diff --git a/cybertensor/synapse.py b/cybertensor/synapse.py index a92bb12..96f4e9c 100644 --- a/cybertensor/synapse.py +++ b/cybertensor/synapse.py @@ -139,8 +139,9 @@ class TerminalInfo(pydantic.BaseModel): version=111, nonce=111111, uuid="5ecbd69c-1cec-11ee-b0dc-e29ce36fec1a", - hotkey="5EnjDGNqqWnuL2HCAdxeEtN2oqtXZw6BMBe936Kfy2PFz1J1", - signature="0x0813029319030129u4120u10841824y0182u091u230912u" + hotkey="pussy1tqhv59nmdwls2263gtwc86swgte20mtsqvhtrd", + signature="0x657a43695079644639586631434c4570593844337262684432436e3776503741495870626e656f4d6e4878426e566d61464854347274686142313155377865376d4f4e385057587856523746567a5169442f6d5757513d3d", + pubkey="AmET/9CWsKo3TWCg2zsaldtdgr2MLug4/yZQfxQiZafJ" ) # Accessing TerminalInfo attributes @@ -192,7 +193,7 @@ class Config: # The terminal ip. ip: Optional[str] = pydantic.Field( title="ip", - description="The ip of the axon recieving the request.", + description="The ip of the axon receiving the request.", examples="198.123.23.1", default=None, allow_mutation=True, @@ -243,7 +244,7 @@ class Config: hotkey: Optional[str] = pydantic.Field( title="hotkey", description="The hotkey string of the terminal wallet.", - examples="5EnjDGNqqWnuL2HCAdxeEtN2oqtXZw6BMBe936Kfy2PFz1J1", + examples="pussy1tqhv59nmdwls2263gtwc86swgte20mtsqvhtrd", default=None, allow_mutation=True, ) @@ -252,7 +253,7 @@ class Config: signature: Optional[str] = pydantic.Field( title="signature", description="A signature verifying the tuple (nonce, axon_hotkey, dendrite_hotkey, uuid)", - examples="0x0813029319030129u4120u10841824y0182u091u230912u", + examples="0x657a43695079644639586631434c4570593844337262684432436e3776503741495870626e656f4d6e4878426e566d61464854347274686142313155377865376d4f4e385057587856523746567a5169442f6d5757513d3d", default=None, allow_mutation=True, ) @@ -260,8 +261,8 @@ class Config: # The cybertensor version on the terminal as an int. pubkey: Optional[str] = pydantic.Field( title="pubkey", - description="The pubkey string of the terminal wallet.", - examples="", + description="The hotkey pubkey string of the terminal wallet.", + examples="AmET/9CWsKo3TWCg2zsaldtdgr2MLug4/yZQfxQiZafJ", default=None, allow_mutation=True, ) From d0a3c9d6782450f63a5c4495157286dfecbece50 Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Thu, 4 Apr 2024 15:36:00 +0700 Subject: [PATCH 09/14] - fix axon checking --- cybertensor/messages/serving.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cybertensor/messages/serving.py b/cybertensor/messages/serving.py index ffecf82..fc2f357 100644 --- a/cybertensor/messages/serving.py +++ b/cybertensor/messages/serving.py @@ -90,8 +90,8 @@ def serve_message( "port": neuron.axon_info.port, "ip_type": neuron.axon_info.ip_type, "netuid": neuron.netuid, - "hotkey": neuron.hotkey, - "coldkey": neuron.coldkey, + # "hotkey": neuron.hotkey, + # "coldkey": neuron.coldkey, "protocol": neuron.axon_info.protocol, "placeholder1": neuron.axon_info.placeholder1, "placeholder2": neuron.axon_info.placeholder2, From 8942344bee1db07d33bd09b60890d68edc17eba7 Mon Sep 17 00:00:00 2001 From: Serge Nedashkovsky Date: Tue, 9 Apr 2024 19:57:51 +1000 Subject: [PATCH 10/14] - correct typos - remove unused code --- cybertensor/__init__.py | 10 ---------- cybertensor/axon.py | 24 +++++++++++++----------- cybertensor/subnets.py | 3 ++- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/cybertensor/__init__.py b/cybertensor/__init__.py index eedc51d..12a935b 100644 --- a/cybertensor/__init__.py +++ b/cybertensor/__init__.py @@ -65,15 +65,6 @@ def turn_console_on(): turn_console_on() -# Logging helpers. -def trace(on: bool = True): - logging.set_trace(on) - - -def debug(on: bool = True): - logging.set_debug(on) - - class NetworkConfigCwTensor(NetworkConfig): def __init__( self, @@ -196,7 +187,6 @@ def __init__( from cybertensor.dendrite import dendrite from cybertensor.config import Config from cybertensor.mock import MockCwtensor, MockWallet -# from .subnets import SubnetsAPI configs = [ axon.config(), diff --git a/cybertensor/axon.py b/cybertensor/axon.py index b65c306..856fc50 100644 --- a/cybertensor/axon.py +++ b/cybertensor/axon.py @@ -185,7 +185,7 @@ class MySynapse( cybertensor.Synapse ): output: int = None # Define a custom request forwarding function - def forward( synapse: MySynapse ) -> MySynapse: + def forward_my_synapse( synapse: MySynapse ) -> MySynapse: # Apply custom logic to synapse and return it synapse.output = 2 return synapse @@ -195,13 +195,13 @@ def verify_my_synapse( synapse: MySynapse ): # Apply custom verification logic to synapse # Optionally raise Exception - # Define a custom request blacklist fucntion + # Define a custom request blacklist function def blacklist_my_synapse( synapse: MySynapse ) -> bool: # Apply custom blacklist # return False ( if non blacklisted ) or True ( if blacklisted ) - # Define a custom request priority fucntion - def prioritize_my_synape( synapse: MySynapse ) -> float: + # Define a custom request priority function + def prioritize_my_synapse( synapse: MySynapse ) -> float: # Apply custom priority return 1.0 @@ -213,12 +213,12 @@ def prioritize_my_synape( synapse: MySynapse ) -> float: forward_fn = forward_my_synapse, verify_fn = verify_my_synapse, blacklist_fn = blacklist_my_synapse, - priority_fn = prioritize_my_synape + priority_fn = prioritize_my_synapse ).attach( forward_fn = forward_my_synapse_2, verify_fn = verify_my_synapse_2, blacklist_fn = blacklist_my_synapse_2, - priority_fn = prioritize_my_synape_2 + priority_fn = prioritize_my_synapse_2 ).serve( netuid = ... cwtensor = ... @@ -680,12 +680,12 @@ def check_config(cls, config: "Config"): Raises: AssertionError: If the axon or external ports are not in range [1024, 65535] """ - assert ( - config.axon.port > 1024 and config.axon.port < 65535 + assert config.axon.port is None or ( + 1024 < config.axon.port < 65535 ), "Axon port must be in range [1024, 65535]" assert config.axon.external_port is None or ( - config.axon.external_port > 1024 and config.axon.external_port < 65535 + 1024 < config.axon.external_port < 65535 ), "External port must be in range [1024, 65535]" def to_string(self): @@ -980,11 +980,13 @@ async def dispatch( # Logs the start of the request processing if synapse.dendrite is not None: cybertensor.logging.trace( - f"axon | <-- | {request.headers.get('content-length', -1)} B | {synapse.name} | {synapse.dendrite.hotkey} | {synapse.dendrite.ip}:{synapse.dendrite.port} | 200 | Success " + f"axon | <-- | {request.headers.get('content-length', -1)} B | {synapse.name} | " + f"{synapse.dendrite.hotkey} | {synapse.dendrite.ip}:{synapse.dendrite.port} | 200 | Success " ) else: cybertensor.logging.trace( - f"axon | <-- | {request.headers.get('content-length', -1)} B | {synapse.name} | None | None | 200 | Success " + f"axon | <-- | {request.headers.get('content-length', -1)} B | {synapse.name} | " + f"None | None | 200 | Success " ) # Call the blacklist function diff --git a/cybertensor/subnets.py b/cybertensor/subnets.py index 140a6dd..483ce10 100644 --- a/cybertensor/subnets.py +++ b/cybertensor/subnets.py @@ -2,6 +2,7 @@ # Copyright © 2021 Yuma Rao # Copyright © 2023 Opentensor Foundation # Copyright © 2023 Opentensor Technologies Inc +# Copyright © 2024 cyber~Congress # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # documentation files (the “Software”), to deal in the Software without restriction, including without limitation @@ -68,7 +69,7 @@ async def query_api( Any: The result of the process_responses_fn. """ synapse = self.prepare_synapse(**kwargs) - cybertensor.logging.debug(f"Quering valdidator axons with synapse {synapse.name}...") + cybertensor.logging.debug(f"Querying validator axons with synapse {synapse.name}...") responses = await self.dendrite( axons=axons, synapse=synapse, From c1d98b416d08b12e106f31761ccc30be7cd09cac Mon Sep 17 00:00:00 2001 From: C H Date: Wed, 10 Apr 2024 12:38:19 +0800 Subject: [PATCH 11/14] Deleted debug information --- cybertensor/axon.py | 11 ++++------ cybertensor/dendrite.py | 7 +----- cybertensor/keypair.py | 33 +++++----------------------- cybertensor/messages/registration.py | 1 + 4 files changed, 12 insertions(+), 40 deletions(-) diff --git a/cybertensor/axon.py b/cybertensor/axon.py index 856fc50..70c5c3b 100644 --- a/cybertensor/axon.py +++ b/cybertensor/axon.py @@ -841,8 +841,9 @@ async def default_verify(self, synapse: cybertensor.Synapse): # Build the keypair from the dendrite_hotkey if synapse.dendrite is not None: cybertensor.logging.info(f"dendrite: {synapse.dendrite}") - # keypair = Keypair(address=synapse.dendrite.hotkey, public_key=synapse.dendrite.pubkey) - keypair = Keypair(address=synapse.dendrite.hotkey) + # NOTE added public key to the synapese for correct keypair initialization + # keypair = Keypair(address=synapse.dendrite.hotkey) + keypair = Keypair(address=synapse.dendrite.hotkey, public_key=synapse.dendrite.pubkey) # Build the signature messages. message = (f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{self.wallet.hotkey.address}." f"{synapse.dendrite.uuid}.{synapse.computed_body_hash}") @@ -859,11 +860,7 @@ async def default_verify(self, synapse: cybertensor.Synapse): ): raise Exception("Nonce is too small") - verified = keypair.verify(message, synapse.dendrite.signature) - cybertensor.logging.debug(f"\nAXON VERIFY MSG: {message}") - cybertensor.logging.debug(f"AXON VERIFY SGN: {synapse.dendrite.signature}") - cybertensor.logging.debug(f"AXON VERIFY : {verified}") - if not verified: + if not keypair.verify(message, synapse.dendrite.signature): raise Exception( f"Signature mismatch with {message} and {synapse.dendrite.signature}" ) diff --git a/cybertensor/dendrite.py b/cybertensor/dendrite.py index 2f8b5c3..043871c 100644 --- a/cybertensor/dendrite.py +++ b/cybertensor/dendrite.py @@ -633,12 +633,7 @@ def preprocess_synapse_for_request( # Sign the request using the dendrite, axon info, and the synapse body hash message = f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{synapse.axon.hotkey}.{synapse.dendrite.uuid}.{synapse.body_hash}" - signed = self.keypair.sign(message) - cybertensor.logging.debug(f"\nDENDRITE ADDR {self.keypair.address}") - cybertensor.logging.debug(f"DENDRITE MSG {message}") - cybertensor.logging.debug(f"DENDRITE SGN {signed}") - cybertensor.logging.debug(f"DENDRITE SGN 0x{signed.hex()}\n") - synapse.dendrite.signature = f"0x{signed.hex()}" + synapse.dendrite.signature = f"0x{self.keypair.sign(message).hex()}" return synapse diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index c854555..be24db2 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -75,14 +75,13 @@ def __init__( public_key = private_key_obj.public_key.public_key address = Address(PublicKey(private_key_obj.public_key), prefix).__str__() - if public_key and isinstance(public_key, str): - # self.public_key = bytes(public_key, 'utf-8') - self.public_key: str = public_key - elif public_key and isinstance(public_key, bytes): - self.public_key: bytes = public_key + if not public_key: + raise ValueError("No public key provided") - # if not public_key: - # raise ValueError("No public key provided") + if isinstance(public_key, str): + self.public_key = bytes(public_key, 'utf-8') + else: + self.public_key: bytes = public_key self.address: str = address @@ -240,10 +239,6 @@ def sign(self, data: Union[bytes, str]) -> bytes: signature_compact = PrivateKey(self.private_key).sign(message=data, deterministic=True) signature_base64_str = base64.b64encode(signature_compact).decode("utf-8").encode() - ct.logging.debug(f"\nKEYPAIR address: {self.address}") - ct.logging.debug(f"KEYPAIR Signing data: {data}") - ct.logging.debug(f"KEYPAIR Generated signature: {signature_base64_str}\n") - return signature_base64_str def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: @@ -272,25 +267,9 @@ def verify(self, data: Union[bytes, str], signature: Union[bytes, str]) -> bool: for address in self.recover_message(message=data, signature=signature): if self.address == address: - ct.logging.debug(f"KEYPAIR Verified data: True") return True - ct.logging.debug(f"KEYPAIR Verified data: False") return False - # recovered_addresses = self.recover_message(data, signature) - # logging.debug(f"\nKEYPAIR Verifying data: {data}") - # logging.debug(f"KEYPAIR Recovered addresses: {recovered_addresses}") - # if self.address == recovered_addresses[0]: - # logging.debug(f"KEYPAIR Verified data: True 1") - # return True - # - # if self.address == recovered_addresses[1]: - # logging.debug(f"KEYPAIR Verified data: True 2\n") - # return True - # - # logging.debug(f"KEYPAIR Verified data: False\n") - # return False - def __repr__(self): if self.address: return f"" diff --git a/cybertensor/messages/registration.py b/cybertensor/messages/registration.py index 529ea76..d4e2268 100644 --- a/cybertensor/messages/registration.py +++ b/cybertensor/messages/registration.py @@ -266,6 +266,7 @@ def burned_register_message( if not Confirm.ask(f"Recycle {recycle_amount} to register on subnet:{netuid}?"): return False + # TODO Update to configured token with console.status(":satellite: Recycling BOOT for Registration..."): success, err_msg = cwtensor._do_burned_register( netuid=netuid, From 601e766fa6394733f40e38e6cbd4293be01891af Mon Sep 17 00:00:00 2001 From: C H Date: Wed, 10 Apr 2024 12:38:43 +0800 Subject: [PATCH 12/14] Added websockets deps to requirements --- requirements/prod.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements/prod.txt b/requirements/prod.txt index 94f76b3..5ab4517 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -37,4 +37,5 @@ cosmpy>=0.9.1 ecdsa<0.15 py-bip39-bindings urllib3==1.26.15 -websocket \ No newline at end of file +websocket>=0.2.1 +websocket-client==1.7.0 \ No newline at end of file From d859d60e9207ef3584b339cb970906d284e46e13 Mon Sep 17 00:00:00 2001 From: C H Date: Wed, 10 Apr 2024 13:16:08 +0800 Subject: [PATCH 13/14] Updated keypair initialization --- cybertensor/keypair.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cybertensor/keypair.py b/cybertensor/keypair.py index be24db2..972d98a 100644 --- a/cybertensor/keypair.py +++ b/cybertensor/keypair.py @@ -75,13 +75,13 @@ def __init__( public_key = private_key_obj.public_key.public_key address = Address(PublicKey(private_key_obj.public_key), prefix).__str__() - if not public_key: - raise ValueError("No public key provided") - if isinstance(public_key, str): - self.public_key = bytes(public_key, 'utf-8') + self.public_key = public_key else: - self.public_key: bytes = public_key + self.public_key: str = public_key.decode("utf-8") + + if not self.public_key: + raise ValueError("No public key provided") self.address: str = address From f1a1615fd94c9f31ab8a356fa6861e08a9715587 Mon Sep 17 00:00:00 2001 From: C H Date: Wed, 10 Apr 2024 13:16:50 +0800 Subject: [PATCH 14/14] Updated version --- cybertensor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybertensor/__init__.py b/cybertensor/__init__.py index 12a935b..7679771 100644 --- a/cybertensor/__init__.py +++ b/cybertensor/__init__.py @@ -30,7 +30,7 @@ nest_asyncio.apply() # Cybertensor code and protocol version. -__version__ = "0.1.7" +__version__ = "0.2.0" version_split = __version__.split(".") __version_as_int__ = ( (100 * int(version_split[0]))