From c68d3bea802654971c2dab4770d9e4f3fb718e3f Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Mon, 27 Nov 2023 16:25:57 +0000 Subject: [PATCH 01/15] update metagraph docstrings --- bittensor/metagraph.py | 507 +++++++++++++++++++++++++++++++++-------- 1 file changed, 407 insertions(+), 100 deletions(-) diff --git a/bittensor/metagraph.py b/bittensor/metagraph.py index 789fb3b7a8..d92b075fe7 100644 --- a/bittensor/metagraph.py +++ b/bittensor/metagraph.py @@ -70,168 +70,323 @@ def latest_block_path(dir_path: str) -> int: class metagraph(torch.nn.Module): """ - Metagraph class representing the neural network graph. + The metagraph class is a core component of the Bittensor network, representing the neural graph + that forms the backbone of the decentralized machine learning system. It is a dynamic representation + of the network's state, capturing the interconnectedness and attributes of neurons (participants) + in the Bittensor ecosystem. This class is not just a static structure but a live reflection of the + network, constantly updated and synchronized with the state of the blockchain. + + In Bittensor, neurons are akin to nodes in a distributed system, each contributing computational + resources and participating in the network's collective intelligence. The metagraph tracks various + attributes of these neurons, such as stake, trust, and consensus, which are crucial for the network's + incentive mechanisms and the Yuma Consensus algorithm as outlined in the NeurIPS paper. These attributes + govern how neurons interact, how they are incentivized, and their roles within the network's + decision-making processes. Attributes: - netuid (int): Network UID. - network (str): Network name. - version (torch.nn.Parameter): Version of the network. - n (torch.nn.Parameter): Number of neurons in the graph. - block (torch.nn.Parameter): Current block number. - stake (torch.nn.Parameter): Stake of the neurons. - total_stake (torch.nn.Parameter): Total stake of the neurons. - ranks (torch.nn.Parameter): Ranks of the neurons. - trust (torch.nn.Parameter): Trust values of the neurons. - consensus (torch.nn.Parameter): Consensus values of the neurons. - validator_trust (torch.nn.Parameter): Validator trust values of the neurons. - incentive (torch.nn.Parameter): Incentive values of the neurons. - emission (torch.nn.Parameter): Emission values of the neurons. - dividends (torch.nn.Parameter): Dividends of the neurons. - active (torch.nn.Parameter): Activation state of the neurons. - last_update (torch.nn.Parameter): Last update time of the neurons. - validator_permit (torch.nn.Parameter): Validator permit state of the neurons. - weights (torch.nn.Parameter): Weights of the neurons. - bonds (torch.nn.Parameter): Bonds of the neurons. - uids (torch.nn.Parameter): UID values of the neurons. - axons (List): List of axon information for the neurons. + netuid (int): A unique identifier that distinguishes between different instances or versions + of the Bittensor network. + network (str): The name of the network, signifying specific configurations or iterations within + the Bittensor ecosystem. + version (torch.nn.Parameter): The version number of the network, formatted for compatibility with + PyTorch models, integral for tracking network updates. + n (torch.nn.Parameter): The total number of neurons in the network, reflecting its size and complexity. + block (torch.nn.Parameter): The current block number in the blockchain, crucial for synchronizing + with the network's latest state. + stake, total_stake, ranks, trust, consensus, validator_trust, incentive, emission, dividends, + active, last_update, validator_permit, weights, bonds, uids (torch.nn.Parameter): + - Stake: Represents the cryptocurrency staked by neurons, impacting their influence and + earnings within the network. + - Total Stake: The cumulative stake across all neurons. + - Ranks: Neuron rankings as per the Yuma Consensus algorithm, influencing their incentive + distribution and network authority. + - Trust: Scores indicating the reliability of neurons, mainly miners, within the network's + operational context. + - Consensus: Scores reflecting each neuron's alignment with the network's collective decisions. + - Validator Trust: Trust scores for validator neurons, crucial for network security and validation. + - Incentive: Rewards allocated to neurons, particularly miners, for their network contributions. + - Emission: The rate at which rewards are distributed to neurons. + - Dividends: Rewards received primarily by validators as part of the incentive mechanism. + - Active: Status indicating whether a neuron is actively participating in the network. + - Last Update: Timestamp of the latest update to a neuron's data. + - Validator Permit: Indicates if a neuron is authorized to act as a validator. + - Weights: Inter-neuronal weights set by each neuron, influencing network dynamics. + - Bonds: Represents speculative investments by neurons in others, part of the reward mechanism. + - UIDs: Unique identifiers for each neuron, essential for network operations. + axons (List): Details about each neuron's axon, critical for facilitating network communication. + + The metagraph plays a pivotal role in Bittensor's decentralized AI operations, influencing everything + from data propagation to reward distribution. It embodies the principles of decentralized governance + and collaborative intelligence, ensuring that the network remains adaptive, secure, and efficient. + + Example Usage: + # Initializing the metagraph to represent the current state of the Bittensor network. + metagraph = bt.metagraph(netuid=config.netuid, network=subtensor.network, sync=False) + + # Synchronizing the metagraph with the network to reflect the latest state and neuron data. + metagraph.sync(subtensor=subtensor) + + # Accessing metagraph properties to inform network interactions and decisions. + total_stake = metagraph.S + neuron_ranks = metagraph.R + neuron_incentives = metagraph.I + ... + # Maintaining a local copy of hotkeys for querying and interacting with network entities. + hotkeys = deepcopy(metagraph.hotkeys) """ @property def S(self) -> torch.FloatTensor: """ - Total stake of the neurons. + Represents the stake of each neuron in the Bittensor network. Stake is an important concept in the + Bittensor ecosystem, signifying the amount of network weight (or “stake”) each neuron holds, + represented on a digital ledger. The stake influences a neuron's ability to contribute to and benefit + from the network, playing a crucial role in the distribution of incentives and decision-making processes. Returns: - torch.FloatTensor: Total stake. + torch.FloatTensor: A tensor representing the stake of each neuron in the network. Higher values + signify a greater stake held by the respective neuron. + + Reference: + Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.total_stake @property def R(self) -> torch.FloatTensor: """ - Ranks of the neurons. + Contains the ranks of neurons in the Bittensor network. Ranks are determined by the network based + on each neuron's performance and contributions. Higher ranks typically indicate a greater level of + contribution or performance by a neuron. These ranks are crucial in determining the distribution of + incentives within the network, with higher-ranked neurons receiving more incentive. Returns: - torch.FloatTensor: Ranks. + torch.FloatTensor: A tensor where each element represents the rank of a neuron. Higher values + indicate higher ranks within the network. + + Reference: + Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.ranks @property def I(self) -> torch.FloatTensor: """ - Incentive values of the neurons. + Incentive values of neurons represent the rewards they receive for their contributions to the network. + The Bittensor network employs an incentive mechanism that rewards neurons based on their + informational value, stake, and consensus with other peers. This ensures that the most valuable and + trusted contributions are incentivized. Returns: - torch.FloatTensor: Incentive values. + torch.FloatTensor: A tensor of incentive values, indicating the rewards or benefits accrued by each + neuron based on their contributions and network consensus. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.incentive @property def E(self) -> torch.FloatTensor: """ - Emission values of the neurons. + Denotes the emission values of neurons in the Bittensor network. Emissions refer to the distribution or + release of rewards (often in the form of cryptocurrency) to neurons, typically based on their stake and + performance. This mechanism is central to the network's incentive model, ensuring that active and + contributing neurons are appropriately rewarded. Returns: - torch.FloatTensor: Emission values. + torch.FloatTensor: A tensor where each element represents the emission value for a neuron, indicating + the amount of reward distributed to that neuron. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.emission @property def C(self) -> torch.FloatTensor: """ - Consensus values of the neurons. + Represents the consensus values of neurons in the Bittensor network. Consensus is a measure of how + much a neuron's contributions are trusted and agreed upon by the majority of the network. It is + calculated based on a staked weighted trust system, where the network leverages the collective + judgment of all participating peers. Higher consensus values indicate that a neuron's contributions + are more widely trusted and valued across the network. Returns: - torch.FloatTensor: Consensus values. + torch.FloatTensor: A tensor of consensus values, where each element reflects the level of trust and + agreement a neuron has achieved within the network. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.consensus @property def T(self) -> torch.FloatTensor: """ - Trust values of the neurons. + Represents the trust values assigned to each neuron in the Bittensor network. Trust is a key metric that + reflects the reliability and reputation of a neuron based on its past behavior and contributions. It is + an essential aspect of the network's functioning, influencing decision-making processes and interactions + between neurons. + + The trust matrix is inferred from the network's inter-peer weights, indicating the level of trust each neuron + has in others. A higher value in the trust matrix suggests a stronger trust relationship between neurons. Returns: - torch.FloatTensor: Trust values. + torch.FloatTensor: A tensor of trust values, where each element represents the trust level of a neuron. + Higher values denote a higher level of trust within the network. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.trust @property def Tv(self) -> torch.FloatTensor: """ - Validator trust values of the neurons. + Contains the validator trust values of neurons in the Bittensor network. Validator trust is specifically + associated with neurons that act as validators within the network. This specialized form of trust reflects + the validators' reliability and integrity in their role, which is crucial for maintaining the network's + stability and security. + + Validator trust values are particularly important for the network's consensus and validation processes, + determining the validators' influence and responsibilities in these critical functions. Returns: - torch.FloatTensor: Validator trust values. + torch.FloatTensor: A tensor of validator trust values, specifically applicable to neurons serving as + validators, where higher values denote greater trustworthiness in their validation roles. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer """ return self.validator_trust @property def D(self) -> torch.FloatTensor: """ - Dividends of the neurons. + Represents the dividends received by neurons in the Bittensor network. Dividends are a form of reward or + distribution, typically given to neurons based on their stake, performance, and contribution to the network. + They are an integral part of the network's incentive structure, encouraging active and beneficial participation. Returns: - torch.FloatTensor: Dividends. + torch.FloatTensor: A tensor of dividend values, where each element indicates the dividends received by + a neuron, reflecting their share of network rewards. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.dividends @property def B(self) -> torch.FloatTensor: """ - Bonds of the neurons. + Bonds in the Bittensor network represent a speculative reward mechanism where neurons can accumulate + bonds in other neurons. Bonds are akin to investments or stakes in other neurons, reflecting a belief in + their future value or performance. This mechanism encourages correct weighting and collaboration + among neurons while providing an additional layer of incentive. Returns: - torch.FloatTensor: Bonds. + torch.FloatTensor: A tensor representing the bonds held by each neuron, where each value signifies + the proportion of bonds owned by one neuron in another. + + Reference: + Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.bonds @property def W(self) -> torch.FloatTensor: """ - Weights of the neurons. + Represents the weights assigned to each neuron in the Bittensor network. In the context of Bittensor, + weights are crucial for determining the influence and interaction between neurons. Each neuron is responsible + for setting its weights, which are then recorded on a digital ledger. These weights are reflective of the + neuron's assessment or judgment of other neurons in the network. + + The weight matrix W = [wij] is a key component of the network's architecture, where the ith row is set by + neuron i and represents its weights towards other neurons. These weights influence the ranking and incentive + mechanisms within the network. Higher weights from a neuron towards another can imply greater trust or value + placed on that neuron's contributions. Returns: - torch.FloatTensor: Weights. + torch.FloatTensor: A tensor of inter-peer weights, where each element wij represents the weight assigned + by neuron i to neuron j. This matrix is fundamental to the network's functioning, + influencing the distribution of incentives and the inter-neuronal dynamics. + + Reference: + Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.weights @property def hotkeys(self) -> List[str]: """ - List of hotkeys for the neurons. + Represents a list of 'hotkeys' for each neuron in the Bittensor network. Hotkeys are unique identifiers + used by neurons for active participation in the network, such as sending and receiving information or + transactions. They are akin to public keys in cryptographic systems and are essential for identifying + and authenticating neurons within the network's operations. Returns: - List[str]: List of hotkeys. + List[str]: A list of hotkeys, with each string representing the hotkey of a corresponding neuron. + These keys are crucial for the network's security and integrity, ensuring proper + identification and authorization of network participants. + + Note: + While the NeurIPS paper may not explicitly detail the concept of hotkeys, they are a fundamental aspect + of decentralized networks for secure and authenticated interactions. """ return [axon.hotkey for axon in self.axons] @property def coldkeys(self) -> List[str]: """ - List of coldkeys for the neurons. + Contains a list of 'coldkeys' for each neuron in the Bittensor network. Coldkeys are similar to hotkeys + but are typically used for more secure, offline activities such as storing assets or offline signing of + transactions. They are an important aspect of a neuron's security, providing an additional layer of + protection for sensitive operations and assets. Returns: - List[str]: List of coldkeys. + List[str]: A list of coldkeys, each string representing the coldkey of a neuron. These keys play a + vital role in the secure management of assets and sensitive operations within the network. + + Note: + The concept of coldkeys, while not explicitly covered in the NeurIPS paper, is a standard practice in + blockchain and decentralized networks for enhanced security and asset protection. """ return [axon.coldkey for axon in self.axons] @property def addresses(self) -> List[str]: """ - List of IP addresses for the neurons. + Provides a list of IP addresses for each neuron in the Bittensor network. These addresses are used for + network communication, allowing neurons to connect, interact, and exchange information with each other. + IP addresses are fundamental for the network's peer-to-peer communication infrastructure. Returns: - List[str]: List of IP addresses. + List[str]: A list of IP addresses, with each string representing the address of a neuron. These + addresses enable the decentralized, distributed nature of the network, facilitating + direct communication and data exchange among neurons. + + Note: + While IP addresses are a basic aspect of network communication, specific details about their use in + the Bittensor network may not be covered in the NeurIPS paper. They are, however, integral to the + functioning of any distributed network. """ return [axon.ip_str() for axon in self.axons] def __str__(self) -> str: """ - String representation of the metagraph. + Provides a human-readable string representation of the metagraph object. This representation + includes key identifiers and attributes of the metagraph, making it easier to quickly understand + the state and configuration of the metagraph in a simple format. Returns: - str: String representation. + str: A string that succinctly represents the metagraph, including its network UID, the total + number of neurons (n), the current block number, and the network's name. This format is + particularly useful for logging, debugging, and displaying the metagraph in a concise manner. + + Example: + # When printing the metagraph object or using it in a string context, this method is automatically invoked. + print(metagraph) # Output: "metagraph(netuid:1, n:100, block:500, network:finney)" """ return "metagraph(netuid:{}, n:{}, block:{}, network:{})".format( self.netuid, self.n.item(), self.block.item(), self.network @@ -239,19 +394,39 @@ def __str__(self) -> str: def __repr__(self) -> str: """ - String representation of the metagraph. + Provides a detailed string representation of the metagraph object, intended for unambiguous + understanding and debugging purposes. This method simply calls the `__str__` method, ensuring + consistency between the informal and formal string representations of the metagraph. Returns: - str: String representation. + str: The same string representation as provided by the `__str__` method, detailing the metagraph's + key attributes including network UID, number of neurons, block number, and network name. + + Example: + # The __repr__ output can be used in debugging to get a clear and concise description of the metagraph. + metagraph_repr = repr(metagraph) + print(metagraph_repr) # Output mirrors that of __str__ """ return self.__str__() def metadata(self) -> dict: """ - Get the metadata of the metagraph. + Retrieves the metadata of the metagraph, providing key information about the current state of the + Bittensor network. This metadata includes details such as the network's unique identifier (netuid), + the total number of neurons (n), the current block number, the network's name, and the version of + the Bittensor network. Returns: - dict: Metadata dictionary. + dict: A dictionary containing essential metadata about the metagraph, including: + - 'netuid': The unique identifier for the network. + - 'n': The total number of neurons in the network. + - 'block': The current block number in the network's blockchain. + - 'network': The name of the Bittensor network. + - 'version': The version number of the Bittensor software. + + Note: + This metadata is crucial for understanding the current state and configuration of the network, + as well as for tracking its evolution over time. """ return { "netuid": self.netuid, @@ -265,13 +440,24 @@ def __init__( self, netuid: int, network: str = "finney", lite: bool = True, sync: bool = True ) -> "metagraph": """ - Initialize the metagraph object. + Initializes a new instance of the metagraph object, setting up the basic structure and parameters + based on the provided arguments. This method is the entry point for creating a metagraph object, + which is a central component in representing the state of the Bittensor network. Args: - netuid (int): Network UID. - network (str): Network name. - lite (bool): Whether to use lite version of the metagraph. - sync (bool): Whether to synchronize the metagraph. + netuid (int): The unique identifier for the network, distinguishing this instance of the metagraph + within potentially multiple network configurations. + network (str): The name of the network, which can indicate specific configurations or versions + of the Bittensor network. + lite (bool): A flag indicating whether to use a lite version of the metagraph. The lite version + may contain less detailed information but can be quicker to initialize and sync. + sync (bool): A flag indicating whether to synchronize the metagraph with the network upon initialization. + Synchronization involves updating the metagraph's parameters to reflect the current state + of the network. + + Example: + # Initializing a metagraph object for the Bittensor network with a specific network UID. + metagraph = metagraph(netuid=123, network="finney", lite=True, sync=True) """ super(metagraph, self).__init__() self.netuid = netuid @@ -342,15 +528,33 @@ def sync( subtensor: Optional["bittensor.subtensor"] = None, ) -> "metagraph": """ - Initiates the synchronization process of the metagraph. + Synchronizes the metagraph with the Bittensor network's current state. It updates the metagraph's attributes + to reflect the latest data from the network, ensuring the metagraph represents the most current state of the network. Args: - block (int, optional): Block number to sync. If None, the current block is used. - lite (bool): Whether to use lite version of the metagraph. - subtensor (bittensor.subtensor, optional): Subtensor object to use for syncing. + block (Optional[int]): A specific block number to synchronize with. If None, the metagraph syncs with the latest block. + This allows for historical analysis or specific state examination of the network. + lite (bool): If True, a lite version of the metagraph is used for quicker synchronization. This is beneficial + when full detail is not necessary, allowing for reduced computational and time overhead. + subtensor (Optional[bittensor.subtensor]): An instance of the subtensor class from Bittensor, providing an + interface to the underlying blockchain data. If provided, this + instance is used for data retrieval during synchronization. Returns: - metagraph: Updated metagraph object. + metagraph: The metagraph instance, updated to the state of the specified block or the latest network state. + + Example: + # Sync the metagraph with the latest block from the subtensor, using the lite version for efficiency. + metagraph.sync(subtensor=subtensor) + + # Sync with a specific block number for detailed analysis. + metagraph.sync(block=12345, lite=False, subtensor=subtensor) + + NOTE: If attempting to access data beyond the previous 300 blocks, you **must** use the `archive` network for subtensor. + Light nodes are configured only to store the previous 300 blocks if connecting to finney or test networks. + + For example: + subtensor = bittensor.subtensor(network='archive') """ # Initialize subtensor subtensor = self._initialize_subtensor(subtensor) @@ -367,13 +571,22 @@ def sync( def _initialize_subtensor(self, subtensor): """ - Initializes the subtensor to be used for syncing. + Initializes the subtensor to be used for syncing the metagraph. This method ensures that a subtensor + instance is available and properly set up for data retrieval during the synchronization process. + + If no subtensor is provided, this method is responsible for creating a new instance of the subtensor, + configured according to the current network settings. Args: - subtensor: The subtensor to initialize. If None, a new subtensor is created. + subtensor: The subtensor instance provided for initialization. If None, a new subtensor + instance is created using the current network configuration. Returns: - subtensor: The initialized subtensor. + subtensor: The initialized subtensor instance, ready to be used for syncing the metagraph. + + Internal Usage: + # Used internally during the sync process to ensure a valid subtensor instance is available. + subtensor = self._initialize_subtensor(subtensor) """ if not subtensor: # TODO: Check and test the initialization of the new subtensor @@ -382,15 +595,20 @@ def _initialize_subtensor(self, subtensor): def _assign_neurons(self, block, lite, subtensor): """ - Assigns neurons to the metagraph based on the 'lite' flag. + Assigns neurons to the metagraph based on the provided block number and the lite flag. This method + is responsible for fetching and setting the neuron data in the metagraph, which includes neuron + attributes like UID, stake, trust, and other relevant information. Args: - block: The block number for which the neurons need to be assigned. - lite: Flag to decide the type of neurons to be assigned. - subtensor: The subtensor to use for syncing. + block: The block number for which the neuron data needs to be fetched. If None, the latest block + data is used. + lite: A boolean flag indicating whether to use a lite version of the neuron data. The lite version + typically includes essential information and is quicker to fetch and process. + subtensor: The subtensor instance used for fetching neuron data from the network. - Returns: - None. + Internal Usage: + # Used internally during the sync process to fetch and set neuron data. + self._assign_neurons(block, lite, subtensor) """ # TODO: Check and test the conditions for assigning neurons if lite: @@ -401,14 +619,18 @@ def _assign_neurons(self, block, lite, subtensor): def _set_metagraph_attributes(self, block, subtensor): """ - Sets attributes for the metagraph. + Sets various attributes of the metagraph based on the latest network data fetched from the subtensor. + This method updates parameters like the number of neurons, block number, stakes, trusts, ranks, and other + neuron-specific information. Args: - block: The block number for which the attributes need to be set. - subtensor: The subtensor to use for syncing. + block: The block number for which the metagraph attributes need to be set. If None, the latest block + data is used. + subtensor: The subtensor instance used for fetching the latest network data. - Returns: - None. + Internal Usage: + # Used internally during the sync process to update the metagraph's attributes. + self._set_metagraph_attributes(block, subtensor) """ # TODO: Check and test the setting of each attribute self.n = self._create_tensor(len(self.neurons), dtype=torch.int64) @@ -461,24 +683,37 @@ def _set_metagraph_attributes(self, block, subtensor): def _create_tensor(self, data, dtype) -> torch.nn.Parameter: """ - Creates a tensor parameter with the given data and dtype. + Creates a tensor parameter with the given data and data type. This method is a utility function used + internally to encapsulate data into a PyTorch tensor, making it compatible with the metagraph's PyTorch + model structure. Args: - data: The data to be included in the tensor. - dtype: The datatype for the tensor. + data: The data to be included in the tensor. This could be any numeric data, like stakes, ranks, etc. + dtype: The data type for the tensor, typically a PyTorch data type like torch.float32 or torch.int64. Returns: - A tensor parameter. + A tensor parameter encapsulating the provided data. + + Internal Usage: + # Used internally to create tensor parameters for various metagraph attributes. + self.stake = self._create_tensor(neuron_stakes, dtype=torch.float32) """ # TODO: Check and test the creation of tensor return torch.nn.Parameter(torch.tensor(data, dtype=dtype), requires_grad=False) def _set_weights_and_bonds(self, subtensor: bittensor.subtensor = None): """ - Computes and sets weights and bonds for each neuron. + Computes and sets the weights and bonds for each neuron in the metagraph. This method is responsible for + processing the raw weight and bond data obtained from the network and converting it into a structured format + suitable for the metagraph model. - Returns: - None. + Args: + subtensor: The subtensor instance used for fetching weights and bonds data. If None, the weights and + bonds are not updated. + + Internal Usage: + # Used internally during the sync process to update the weights and bonds of the neurons. + self._set_weights_and_bonds(subtensor=subtensor) """ # TODO: Check and test the computation of weights and bonds if self.netuid == 0: @@ -495,14 +730,21 @@ def _set_weights_and_bonds(self, subtensor: bittensor.subtensor = None): def _process_weights_or_bonds(self, data, attribute: str) -> torch.nn.Parameter: """ - Processes weights or bonds based on the given attribute. + Processes the raw weights or bonds data and converts it into a structured tensor format. This method handles + the transformation of neuron connection data (weights or bonds) from a list or other unstructured format + into a tensor that can be utilized within the metagraph model. Args: - data: The weights or bonds data to be processed. - attribute: The attribute to decide the type of processing ('weights' or 'bonds'). + data: The raw weights or bonds data to be processed. This data typically comes from the subtensor. + attribute: A string indicating whether the data is 'weights' or 'bonds', which determines the + specific processing steps to be applied. Returns: - The processed tensor parameter. + A tensor parameter encapsulating the processed weights or bonds data. + + Internal Usage: + # Used internally to process and set weights or bonds for the neurons. + self.weights = self._process_weights_or_bonds(raw_weights_data, "weights") """ data_array = [] for item in data: @@ -538,14 +780,20 @@ def _process_root_weights( self, data, attribute: str, subtensor: bittensor.subtensor ) -> torch.nn.Parameter: """ - Processes root weights based on the given attribute. + Specifically processes the root weights data for the metagraph. This method is similar to _process_weights_or_bonds + but is tailored for processing root weights, which have a different structure and significance in the network. Args: - data: The weights or bonds data to be processed. - attribute: The attribute to decide the type of processing ('weights' or 'bonds'). + data: The raw root weights data to be processed. + attribute: A string indicating the attribute type, here it's typically 'weights'. + subtensor: The subtensor instance used for additional data and context needed in processing. Returns: - The processed tensor parameter. + A tensor parameter encapsulating the processed root weights data. + + Internal Usage: + # Used internally to process and set root weights for the metagraph. + self.root_weights = self._process_root_weights(raw_root_weights_data, "weights", subtensor) """ data_array = [] n_subnets = subtensor.get_total_subnets() @@ -575,10 +823,24 @@ def _process_root_weights( def save(self) -> "metagraph": """ - Save the state of the metagraph object. + Saves the current state of the metagraph to a file on disk. This function is crucial for persisting the current + state of the network's metagraph, which can later be reloaded or analyzed. The save operation includes all neuron + attributes and parameters, ensuring a complete snapshot of the metagraph's state. Returns: - metagraph: Updated metagraph object. + metagraph: The metagraph instance after saving its state. + + Example: + # Save the current state of the metagraph to the default directory. + metagraph.save() + + # The saved state can later be loaded to restore or analyze the metagraph's state at this point. + + # If using the default save path + metagraph.load() + + # If using a custom save path + metagraph.load_from_path(dir_path) """ save_directory = get_save_dir(self.network, self.netuid) os.makedirs(save_directory, exist_ok=True) @@ -591,22 +853,67 @@ def save(self) -> "metagraph": def load(self) -> "metagraph": """ - Load the state of the metagraph object. + Loads the state of the metagraph from the default save directory. This method is instrumental for restoring + the metagraph to its last saved state. It automatically identifies the save directory based on the network + and netuid properties of the metagraph, locates the latest block file in that directory, and loads all + metagraph parameters from it. + + This functionality is particularly beneficial when continuity in the state of the metagraph is necessary + across different runtime sessions, or after a restart of the system. It ensures that the metagraph reflects + the exact state it was in at the last save point, maintaining consistency in the network's representation. + + The method delegates to `load_from_path`, supplying it with the directory path constructed from the metagraph's + current network and netuid properties. This abstraction simplifies the process of loading the metagraph's state + for the user, requiring no direct path specifications. Returns: - metagraph: Updated metagraph object. + metagraph: The metagraph instance after loading its state from the default directory. + + Example: + # Load the metagraph state from the last saved snapshot in the default directory. + metagraph.load() + + # After this operation, the metagraph's parameters and neuron data are restored to their state + # at the time of the last save in the default directory. + + Note: + The default save directory is determined based on the metagraph's network and netuid attributes. It is + important to ensure that these attributes are set correctly and that the default save directory contains + the appropriate state files for the metagraph. """ self.load_from_path(get_save_dir(self.network, self.netuid)) def load_from_path(self, dir_path: str) -> "metagraph": """ - Load the state of the metagraph object from the specified path. + Loads the state of the metagraph from a specified directory path. This method is crucial for restoring + the metagraph to a specific state based on saved data. It locates the latest block file in the given + directory and loads all metagraph parameters from it. This is particularly useful for analyses that + require historical states of the network or for restoring previous states of the metagraph in different + execution environments. + + The method first identifies the latest block file in the specified directory, then loads the metagraph state + including neuron attributes and parameters from this file. This ensures that the metagraph is accurately + reconstituted to reflect the network state at the time of the saved block. Args: - dir_path (str): Directory path. + dir_path (str): The directory path where the metagraph's state files are stored. This path should + contain one or more saved state files, typically named in a format that includes + the block number. Returns: - metagraph: Updated metagraph object. + metagraph: The metagraph instance after loading its state from the specified directory path. + + Example: + # Load the metagraph state from a specific directory. + dir_path = "/path/to/saved/metagraph/states" + metagraph.load_from_path(dir_path) + + # The metagraph is now restored to the state it was in at the time of the latest saved block in the specified directory. + + Note: + This method assumes that the state files in the specified directory are correctly formatted and + contain valid data for the metagraph. It is essential to ensure that the directory path and the + state files within it are accurate and consistent with the expected metagraph structure. """ graph_file = latest_block_path(dir_path) state_dict = torch.load(graph_file) From b9b27bea9f3e8a63fa8ef6d1aba2c764ebfeee0b Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Mon, 27 Nov 2023 16:36:20 +0000 Subject: [PATCH 02/15] remove references, uneccessary --- bittensor/metagraph.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/bittensor/metagraph.py b/bittensor/metagraph.py index d92b075fe7..612a49755a 100644 --- a/bittensor/metagraph.py +++ b/bittensor/metagraph.py @@ -146,9 +146,6 @@ def S(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor representing the stake of each neuron in the network. Higher values signify a greater stake held by the respective neuron. - - Reference: - Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.total_stake @@ -163,9 +160,6 @@ def R(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor where each element represents the rank of a neuron. Higher values indicate higher ranks within the network. - - Reference: - Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.ranks @@ -180,9 +174,6 @@ def I(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor of incentive values, indicating the rewards or benefits accrued by each neuron based on their contributions and network consensus. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.incentive @@ -197,9 +188,6 @@ def E(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor where each element represents the emission value for a neuron, indicating the amount of reward distributed to that neuron. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.emission @@ -216,8 +204,6 @@ def C(self) -> torch.FloatTensor: torch.FloatTensor: A tensor of consensus values, where each element reflects the level of trust and agreement a neuron has achieved within the network. - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.consensus @@ -235,9 +221,6 @@ def T(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor of trust values, where each element represents the trust level of a neuron. Higher values denote a higher level of trust within the network. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.trust @@ -255,9 +238,6 @@ def Tv(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor of validator trust values, specifically applicable to neurons serving as validators, where higher values denote greater trustworthiness in their validation roles. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer """ return self.validator_trust @@ -271,9 +251,6 @@ def D(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor of dividend values, where each element indicates the dividends received by a neuron, reflecting their share of network rewards. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.dividends @@ -288,9 +265,6 @@ def B(self) -> torch.FloatTensor: Returns: torch.FloatTensor: A tensor representing the bonds held by each neuron, where each value signifies the proportion of bonds owned by one neuron in another. - - Reference: - Bittensor NeurIPS Paper, Section 3: Blockchain Layer. """ return self.bonds @@ -311,9 +285,6 @@ def W(self) -> torch.FloatTensor: torch.FloatTensor: A tensor of inter-peer weights, where each element wij represents the weight assigned by neuron i to neuron j. This matrix is fundamental to the network's functioning, influencing the distribution of incentives and the inter-neuronal dynamics. - - Reference: - Bittensor NeurIPS Paper, Section 2: AI Layer. """ return self.weights From 74f261352066a65d4092eff2212ea83d03b00c16 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Mon, 27 Nov 2023 22:35:40 +0000 Subject: [PATCH 03/15] subtensor docs overhaul --- bittensor/subtensor.py | 1679 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 1512 insertions(+), 167 deletions(-) diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 2982e458ad..a60aa02ea4 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -87,10 +87,50 @@ class ParamWithTypes(TypedDict): class subtensor: - """Factory Class for bittensor.subtensor - - The Subtensor class handles interactions with the substrate subtensor chain. - By default, the Subtensor class connects to the Finney which serves as the main bittensor network. + """ + The Subtensor class in Bittensor serves as a crucial interface for interacting with the Bittensor blockchain, + facilitating a range of operations essential for the decentralized machine learning network. This class + enables neurons (network participants) to engage in activities such as registering on the network, managing + staked weights, setting inter-neuronal weights, and participating in consensus mechanisms. + + The Bittensor network operates on a digital ledger where each neuron holds stakes (S) and learns a set + of inter-peer weights (W). These weights, set by the neurons themselves, play a critical role in determining + the ranking and incentive mechanisms within the network. Higher-ranked neurons, as determined by their + contributions and trust within the network, receive more incentives. + + The Subtensor class connects to various Bittensor networks like the main 'finney' network or local test + networks, providing a gateway to the blockchain layer of Bittensor. It leverages a staked weighted trust + system and consensus to ensure fair and distributed incentive mechanisms, where incentives (I) are + primarily allocated to neurons that are trusted by the majority of the network​``【oaicite:1】``​. + + Additionally, Bittensor introduces a speculation-based reward mechanism in the form of bonds (B), allowing + neurons to accumulate bonds in other neurons, speculating on their future value. This mechanism aligns + with market-based speculation, incentivizing neurons to make judicious decisions in their inter-neuronal + investments. + + Attributes: + network (str): The name of the Bittensor network (e.g., 'finney', 'local') the instance is + connected to, determining the blockchain interaction context. + chain_endpoint (str): The blockchain node endpoint URL, enabling direct communication + with the Bittensor blockchain for transaction processing and data retrieval. + + Example Usage: + # Connect to the main Bittensor network (Finney). + finney_subtensor = subtensor(network='finney') + + # Register a new neuron on the network. + wallet = bittensor.wallet(...) # Assuming a wallet instance is created. + success = finney_subtensor.register(wallet=wallet, netuid=netuid) + + # Set inter-neuronal weights for collaborative learning. + success = finney_subtensor.set_weights(wallet=wallet, netuid=netuid, uids=[...], weights=[...]) + + # Speculate by accumulating bonds in other promising neurons. + success = finney_subtensor.delegate(wallet=wallet, delegate_ss58=other_neuron_ss58, amount=bond_amount) + + By facilitating these operations, the Subtensor class is instrumental in maintaining the decentralized + intelligence and dynamic learning environment of the Bittensor network, as envisioned in its foundational + principles and mechanisms described in the NeurIPS paper. """ @staticmethod @@ -111,18 +151,18 @@ def help(cls): def add_args(cls, parser: argparse.ArgumentParser, prefix: str = None): prefix_str = "" if prefix == None else prefix + "." try: - default_network = os.getenv("BT_SUBTENSOR_NETWORK") or "finney" + default_network = os.getenv("BT_SUBTENSOR_NETWORK") or "local" default_chain_endpoint = ( os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") - or bittensor.__finney_entrypoint__ + or bittensor.__local_entrypoint__ ) parser.add_argument( "--" + prefix_str + "subtensor.network", default=default_network, type=str, help="""The subtensor network flag. The likely choices are: - -- finney (main network) -- local (local running network) + -- finney (main network) If this option is set it overloads subtensor.chain_endpoint with an entry point node from that network. """, @@ -253,17 +293,20 @@ def __init__( config: "bittensor.config" = None, _mock: bool = False, ) -> None: - r"""Initializes a subtensor chain interface. - Args: - config (:obj:`bittensor.config`, `optional`): - bittensor.subtensor.config() - network (default='local or ws://127.0.0.1:9946', type=str) - The subtensor network flag. The likely choices are: - -- local (local running network) - -- finney (main network) - or subtensor endpoint flag. If set, overrides the network argument. """ + Initializes a Subtensor interface for interacting with the Bittensor blockchain. + + Args: + network (str, optional): The network name to connect to (e.g., 'finney', 'local'). + Defaults to the main Bittensor network if not specified. + config (bittensor.config, optional): Configuration object for the subtensor. + If not provided, a default configuration is used. + _mock (bool, optional): If set to True, uses a mocked connection for testing purposes. + + This initialization sets up the connection to the specified Bittensor network, allowing for various + blockchain operations such as neuron registration, stake management, and setting weights. + """ # Determine config.subtensor.chain_endpoint and config.subtensor.network config. # If chain_endpoint is set, we override the network flag, otherwise, the chain_endpoint is assigned by the network. # Argument importance: network > chain_endpoint > config.subtensor.chain_endpoint > config.subtensor.network @@ -312,7 +355,22 @@ def nominate( wait_for_finalization: bool = False, wait_for_inclusion: bool = True, ) -> bool: - """Becomes a delegate for the hotkey.""" + """ + Becomes a delegate for the hotkey associated with the given wallet. This method is used to nominate + a neuron (identified by the hotkey in the wallet) as a delegate on the Bittensor network, allowing it + to participate in consensus and validation processes. + + Args: + wallet (bittensor.wallet): The wallet containing the hotkey to be nominated. + wait_for_finalization (bool, optional): If True, waits until the transaction is finalized on the blockchain. + wait_for_inclusion (bool, optional): If True, waits until the transaction is included in a block. + + Returns: + bool: True if the nomination process is successful, False otherwise. + + This function is a key part of the decentralized governance mechanism of Bittensor, allowing for the + dynamic selection and participation of validators in the network's consensus process. + """ return nominate_extrinsic( subtensor=self, wallet=wallet, @@ -329,7 +387,22 @@ def delegate( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Adds the specified amount of stake to the passed delegate using the passed wallet.""" + """ + Becomes a delegate for the hotkey associated with the given wallet. This method is used to nominate + a neuron (identified by the hotkey in the wallet) as a delegate on the Bittensor network, allowing it + to participate in consensus and validation processes. + + Args: + wallet (bittensor.wallet): The wallet containing the hotkey to be nominated. + wait_for_finalization (bool, optional): If True, waits until the transaction is finalized on the blockchain. + wait_for_inclusion (bool, optional): If True, waits until the transaction is included in a block. + + Returns: + bool: True if the nomination process is successful, False otherwise. + + This function is a key part of the decentralized governance mechanism of Bittensor, allowing for the + dynamic selection and participation of validators in the network's consensus process. + """ return delegate_extrinsic( subtensor=self, wallet=wallet, @@ -349,7 +422,24 @@ def undelegate( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Removes the specified amount of stake from the passed delegate using the passed wallet.""" + """ + Removes a specified amount of stake from a delegate neuron using the provided wallet. This action + reduces the staked amount on another neuron, effectively withdrawing support or speculation. + + Args: + wallet (bittensor.wallet): The wallet used for the undelegation process. + delegate_ss58 (Optional[str]): The SS58 address of the delegate neuron. + amount (Union[Balance, float]): The amount of TAO to undelegate. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the undelegation is successful, False otherwise. + + This function reflects the dynamic and speculative nature of the Bittensor network, allowing neurons + to adjust their stakes and investments based on changing perceptions and performances within the network. + """ return undelegate_extrinsic( subtensor=self, wallet=wallet, @@ -367,7 +457,22 @@ def set_delegate_take( wait_for_finalization: bool = False, wait_for_inclusion: bool = True, ) -> bool: - """Set your delegate take percentage.""" + """ + Sets the percentage of incentives that a delegate neuron takes from its delegators. This action adjusts + the reward distribution mechanism between a delegate and its supporting neurons. + + Args: + wallet (bittensor.wallet): The wallet of the delegate neuron. + take (int): The percentage (0-100) of incentives to take from delegators. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + + Returns: + bool: True if the setting of delegate take percentage is successful, False otherwise. + + This method allows a delegate neuron to specify its share of the incentives generated through delegation, + aligning with the network's principles of decentralized governance and fair incentive distribution. + """ return set_delegate_take_extrinsic( subtensor=self, wallet=wallet, @@ -390,6 +495,27 @@ def set_weights( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: + """ + Sets the inter-neuronal weights for the specified neuron. This process involves specifying the + influence or trust a neuron places on other neurons in the network, which is a fundamental aspect + of Bittensor's decentralized learning architecture. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron setting the weights. + netuid (int): The unique identifier of the subnet. + uids (Union[torch.LongTensor, list]): The list of neuron UIDs that the weights are being set for. + weights (Union[torch.FloatTensor, list]): The corresponding weights to be set for each UID. + version_key (int, optional): Version key for compatibility with the network. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the setting of weights is successful, False otherwise. + + This function is crucial in shaping the network's collective intelligence, where each neuron's + learning and contribution are influenced by the weights it sets towards others【81†source】. + """ return set_weights_extrinsic( subtensor=self, wallet=wallet, @@ -412,6 +538,27 @@ def _do_set_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, ) -> Tuple[bool, Optional[str]]: # (success, error_message) + """ + Internal method to send a transaction to the Bittensor blockchain, setting weights + for specified neurons. This method constructs and submits the transaction, handling + retries and blockchain communication. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron setting the weights. + uids (List[int]): List of neuron UIDs for which weights are being set. + vals (List[int]): List of weight values corresponding to each UID. + netuid (int): Unique identifier for the network. + version_key (int, optional): Version key for compatibility with the network. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + + Returns: + Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + + This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their + trust in other neurons based on observed performance and contributions. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -465,7 +612,24 @@ def register( update_interval: Optional[int] = None, log_verbose: bool = False, ) -> bool: - """Registers the wallet to chain.""" + """ + Registers a neuron on the Bittensor network using the provided wallet. Registration + is a critical step for a neuron to become an active participant in the network, enabling + it to stake, set weights, and receive incentives. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron to be registered. + netuid (int): The unique identifier of the subnet. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + Other arguments: Various optional parameters to customize the registration process. + + Returns: + bool: True if the registration is successful, False otherwise. + + This function facilitates the entry of new neurons into the network, supporting the decentralized + growth and scalability of the Bittensor ecosystem. + """ return register_extrinsic( subtensor=self, wallet=wallet, @@ -498,7 +662,21 @@ def run_faucet( update_interval: Optional[int] = None, log_verbose: bool = False, ) -> bool: - """Registers the wallet to chain.""" + """ + Facilitates a faucet transaction, allowing new neurons to receive an initial amount of TAO + for participating in the network. This function is particularly useful for newcomers to the + Bittensor network, enabling them to start with a small stake. + + Args: + wallet (bittensor.wallet): The wallet for which the faucet transaction is to be run. + Other arguments: Various optional parameters to customize the faucet transaction process. + + Returns: + bool: True if the faucet transaction is successful, False otherwise. + + This function is part of Bittensor's onboarding process, ensuring that new neurons have + the necessary resources to begin their journey in the decentralized AI network. + """ return run_faucet_extrinsic( subtensor=self, wallet=wallet, @@ -523,7 +701,23 @@ def burned_register( wait_for_finalization: bool = True, prompt: bool = False, ) -> bool: - """Registers the wallet to chain by recycling TAO.""" + """ + Registers a neuron on the Bittensor network by burning TAO. This method of registration + involves recycling TAO tokens, contributing to the network's deflationary mechanism. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron to be registered. + netuid (int): The unique identifier of the subnet. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the registration is successful, False otherwise. + + This function offers an alternative registration path, aligning with the network's principles + of token circulation and value conservation. + """ return burned_register_extrinsic( subtensor=self, wallet=wallet, @@ -646,7 +840,25 @@ def transfer( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Transfers funds from this wallet to the destination public key address""" + """ + Executes a transfer of funds from the provided wallet to the specified destination address. + This function is used to move TAO tokens within the Bittensor network, facilitating transactions + between neurons. + + Args: + wallet (bittensor.wallet): The wallet from which funds are being transferred. + dest (str): The destination public key address. + amount (Union[Balance, float]): The amount of TAO to be transferred. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the transfer is successful, False otherwise. + + This function is essential for the fluid movement of tokens in the network, supporting + various economic activities such as staking, delegation, and reward distribution. + """ return transfer_extrinsic( subtensor=self, wallet=wallet, @@ -660,6 +872,24 @@ def transfer( def get_transfer_fee( self, wallet: "bittensor.wallet", dest: str, value: Union[Balance, float, int] ) -> Balance: + """ + Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. + This function simulates the transfer to estimate the associated cost, taking into account the current + network conditions and transaction complexity. + + Args: + wallet (bittensor.wallet): The wallet from which the transfer is initiated. + dest (str): The SS58 address of the destination account. + value (Union[Balance, float, int]): The amount of tokens to be transferred, specified as a Balance object, + or in Tao (float) or Rao (int) units. + + Returns: + Balance: The estimated transaction fee for the transfer, represented as a Balance object. + + Estimating the transfer fee is essential for planning and executing token transactions, ensuring that the + wallet has sufficient funds to cover both the transfer amount and the associated costs. This function + provides a crucial tool for managing financial operations within the Bittensor network. + """ if isinstance(value, float): transfer_balance = Balance.from_tao(value) elif isinstance(value, int): @@ -740,7 +970,21 @@ def make_substrate_call_with_retry(): return make_substrate_call_with_retry() def get_existential_deposit(self, block: Optional[int] = None) -> Optional[Balance]: - """Returns the existential deposit for the chain.""" + """ + Retrieves the existential deposit amount for the Bittensor blockchain. The existential deposit + is the minimum amount of TAO required for an account to exist on the blockchain. Accounts with + balances below this threshold can be reaped to conserve network resources. + + Args: + block (Optional[int], optional): Block number at which to query the deposit amount. If None, + the current block is used. + + Returns: + Optional[Balance]: The existential deposit amount, or None if the query fails. + + The existential deposit is a fundamental economic parameter in the Bittensor network, ensuring + efficient use of storage and preventing the proliferation of dust accounts. + """ result = self.query_constant( module_name="Balances", constant_name="ExistentialDeposit", block=block ) @@ -760,6 +1004,23 @@ def register_subnetwork( wait_for_finalization=True, prompt: bool = False, ) -> bool: + """ + Registers a new subnetwork on the Bittensor network using the provided wallet. This function + is used for the creation and registration of subnetworks, which are specialized segments of the + overall Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet to be used for registration. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the subnetwork registration is successful, False otherwise. + + This function allows for the expansion and diversification of the Bittensor network, supporting + its decentralized and adaptable architecture. + """ return register_subnetwork_extrinsic( self, wallet=wallet, @@ -778,6 +1039,26 @@ def set_hyperparameter( wait_for_finalization=True, prompt: bool = False, ) -> bool: + """ + Sets a specific hyperparameter for a given subnetwork on the Bittensor blockchain. This action + involves adjusting network-level parameters, influencing the behavior and characteristics of the + subnetwork. + + Args: + wallet (bittensor.wallet): The wallet used for setting the hyperparameter. + netuid (int): The unique identifier of the subnetwork. + parameter (str): The name of the hyperparameter to be set. + value: The new value for the hyperparameter. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the hyperparameter setting is successful, False otherwise. + + This function plays a critical role in the dynamic governance and adaptability of the Bittensor + network, allowing for fine-tuning of network operations and characteristics. + """ return set_hyperparameter_extrinsic( self, wallet=wallet, @@ -805,6 +1086,28 @@ def serve( wait_for_finalization=True, prompt: bool = False, ) -> bool: + """ + Registers a neuron's serving endpoint on the Bittensor network. This function announces the + IP address and port where the neuron is available to serve requests, facilitating peer-to-peer + communication within the network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron being served. + ip (str): The IP address of the serving neuron. + port (int): The port number on which the neuron is serving. + protocol (int): The protocol type used by the neuron (e.g., GRPC, HTTP). + netuid (int): The unique identifier of the subnetwork. + Other arguments: Placeholder parameters for future extensions. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the serve registration is successful, False otherwise. + + This function is essential for establishing the neuron's presence in the network, enabling + it to participate in the decentralized machine learning processes of Bittensor. + """ return serve_extrinsic( self, wallet, @@ -826,6 +1129,24 @@ def serve_axon( wait_for_finalization: bool = True, prompt: bool = False, ) -> bool: + """ + Registers an Axon serving endpoint on the Bittensor network for a specific neuron. This function + is used to set up the Axon, a key component of a neuron that handles incoming queries and data + processing tasks. + + Args: + netuid (int): The unique identifier of the subnetwork. + axon (bittensor.Axon): The Axon instance to be registered for serving. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the Axon serve registration is successful, False otherwise. + + By registering an Axon, the neuron becomes an active part of the network's distributed + computing infrastructure, contributing to the collective intelligence of Bittensor. + """ return serve_axon_extrinsic( self, netuid, axon, wait_for_inclusion, wait_for_finalization ) @@ -837,6 +1158,23 @@ def _do_serve_axon( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, ) -> Tuple[bool, Optional[str]]: + """ + Internal method to submit a serve axon transaction to the Bittensor blockchain. This method + creates and submits a transaction, enabling a neuron's Axon to serve requests on the network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron. + call_params (AxonServeCallParams): Parameters required for the serve axon call. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + + Returns: + Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + + This function is crucial for initializing and announcing a neuron's Axon service on the network, + enhancing the decentralized computation capabilities of Bittensor. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -992,7 +1330,25 @@ def add_stake( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Adds the specified amount of stake to passed hotkey uid.""" + """ + Adds the specified amount of stake to a neuron identified by the hotkey SS58 address. Staking + is a fundamental process in the Bittensor network that enables neurons to participate actively + and earn incentives. + + Args: + wallet (bittensor.wallet): The wallet to be used for staking. + hotkey_ss58 (Optional[str]): The SS58 address of the hotkey associated with the neuron. + amount (Union[Balance, float]): The amount of TAO to stake. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the staking is successful, False otherwise. + + This function enables neurons to increase their stake in the network, enhancing their influence + and potential rewards in line with Bittensor's consensus and reward mechanisms. + """ return add_stake_extrinsic( subtensor=self, wallet=wallet, @@ -1012,7 +1368,24 @@ def add_stake_multiple( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Adds stake to each hotkey_ss58 in the list, using each amount, from a common coldkey.""" + """ + Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation + allows for efficient staking across different neurons from a single wallet. + + Args: + wallet (bittensor.wallet): The wallet used for staking. + hotkey_ss58s (List[str]): List of SS58 addresses of hotkeys to stake to. + amounts (List[Union[Balance, float]], optional): Corresponding amounts of TAO to stake for each hotkey. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the staking is successful for all specified neurons, False otherwise. + + This function is essential for managing stakes across multiple neurons, reflecting the dynamic + and collaborative nature of the Bittensor network. + """ return add_stake_multiple_extrinsic( self, wallet, @@ -1084,7 +1457,25 @@ def unstake_multiple( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Removes stake from each hotkey_ss58 in the list, using each amount, to a common coldkey.""" + """ + Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts + efficiently. This function is useful for managing the distribution of stakes across multiple neurons. + + Args: + wallet (bittensor.wallet): The wallet linked to the coldkey from which the stakes are being withdrawn. + hotkey_ss58s (List[str]): A list of hotkey SS58 addresses to unstake from. + amounts (List[Union[Balance, float]], optional): The amounts of TAO to unstake from each hotkey. + If not provided, unstakes all available stakes. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the batch unstaking is successful, False otherwise. + + This function allows for strategic reallocation or withdrawal of stakes, aligning with the dynamic + stake management aspect of the Bittensor network. + """ return unstake_multiple_extrinsic( self, wallet, @@ -1104,7 +1495,24 @@ def unstake( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Removes stake into the wallet coldkey from the specified hotkey uid.""" + """ + Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting + individual neuron stakes within the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron from which the stake is being removed. + hotkey_ss58 (Optional[str]): The SS58 address of the hotkey account to unstake from. + amount (Union[Balance, float], optional): The amount of TAO to unstake. If not specified, unstakes all. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the unstaking process is successful, False otherwise. + + This function supports flexible stake management, allowing neurons to adjust their network participation + and potential reward accruals. + """ return unstake_extrinsic( self, wallet, @@ -1175,6 +1583,24 @@ def register_senate( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: + """ + Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting + individual neuron stakes within the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron from which the stake is being removed. + hotkey_ss58 (Optional[str]): The SS58 address of the hotkey account to unstake from. + amount (Union[Balance, float], optional): The amount of TAO to unstake. If not specified, unstakes all. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the unstaking process is successful, False otherwise. + + This function supports flexible stake management, allowing neurons to adjust their network participation + and potential reward accruals. + """ return register_senate_extrinsic( self, wallet, wait_for_inclusion, wait_for_finalization, prompt ) @@ -1186,6 +1612,24 @@ def leave_senate( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: + """ + Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting + individual neuron stakes within the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron from which the stake is being removed. + hotkey_ss58 (Optional[str]): The SS58 address of the hotkey account to unstake from. + amount (Union[Balance, float], optional): The amount of TAO to unstake. If not specified, unstakes all. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the unstaking process is successful, False otherwise. + + This function supports flexible stake management, allowing neurons to adjust their network participation + and potential reward accruals. + """ return leave_senate_extrinsic( self, wallet, wait_for_inclusion, wait_for_finalization, prompt ) @@ -1200,6 +1644,24 @@ def vote_senate( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: + """ + Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting + individual neuron stakes within the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron from which the stake is being removed. + hotkey_ss58 (Optional[str]): The SS58 address of the hotkey account to unstake from. + amount (Union[Balance, float], optional): The amount of TAO to unstake. If not specified, unstakes all. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the unstaking process is successful, False otherwise. + + This function supports flexible stake management, allowing neurons to adjust their network participation + and potential reward accruals. + """ return vote_senate_extrinsic( self, wallet, @@ -1212,6 +1674,21 @@ def vote_senate( ) def is_senate_member(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: + """ + Checks if a given neuron (identified by its hotkey SS58 address) is a member of the Bittensor senate. + The senate is a key governance body within the Bittensor network, responsible for overseeing and + approving various network operations and proposals. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number at which to check senate membership. + + Returns: + bool: True if the neuron is a senate member at the given block, False otherwise. + + This function is crucial for understanding the governance dynamics of the Bittensor network and for + identifying the neurons that hold decision-making power within the network. + """ senate_members = self.query_module( module="SenateMembers", name="Members", block=block ).serialize() @@ -1220,6 +1697,20 @@ def is_senate_member(self, hotkey_ss58: str, block: Optional[int] = None) -> boo def get_vote_data( self, proposal_hash: str, block: Optional[int] = None ) -> Optional[ProposalVoteData]: + """ + Retrieves the voting data for a specific proposal on the Bittensor blockchain. This data includes + information about how senate members have voted on the proposal. + + Args: + proposal_hash (str): The hash of the proposal for which voting data is requested. + block (Optional[int], optional): The blockchain block number to query the voting data. + + Returns: + Optional[ProposalVoteData]: An object containing the proposal's voting data, or None if not found. + + This function is important for tracking and understanding the decision-making processes within + the Bittensor network, particularly how proposals are received and acted upon by the governing body. + """ vote_data = self.query_module( module="Triumvirate", name="Voting", block=block, params=[proposal_hash] ) @@ -1228,6 +1719,19 @@ def get_vote_data( get_proposal_vote_data = get_vote_data def get_senate_members(self, block: Optional[int] = None) -> Optional[List[str]]: + """ + Retrieves the list of current senate members from the Bittensor blockchain. Senate members are + responsible for governance and decision-making within the network. + + Args: + block (Optional[int], optional): The blockchain block number at which to retrieve the senate members. + + Returns: + Optional[List[str]]: A list of SS58 addresses of current senate members, or None if not available. + + Understanding the composition of the senate is key to grasping the governance structure and + decision-making authority within the Bittensor network. + """ senate_members = self.query_module("SenateMembers", "Members", block=block) return senate_members.serialize() if senate_members != None else None @@ -1235,6 +1739,20 @@ def get_senate_members(self, block: Optional[int] = None) -> Optional[List[str]] def get_proposal_call_data( self, proposal_hash: str, block: Optional[int] = None ) -> Optional["bittensor.ProposalCallData"]: + """ + Retrieves the call data of a specific proposal on the Bittensor blockchain. This data provides + detailed information about the proposal, including its purpose and specifications. + + Args: + proposal_hash (str): The hash of the proposal. + block (Optional[int], optional): The blockchain block number at which to query the proposal call data. + + Returns: + Optional[bittensor.ProposalCallData]: An object containing the proposal's call data, or None if not found. + + This function is crucial for analyzing the types of proposals made within the network and the + specific changes or actions they intend to implement or address. + """ proposal_data = self.query_module( module="Triumvirate", name="ProposalOf", block=block, params=[proposal_hash] ) @@ -1242,6 +1760,19 @@ def get_proposal_call_data( return proposal_data.serialize() if proposal_data != None else None def get_proposal_hashes(self, block: Optional[int] = None) -> Optional[List[str]]: + """ + Retrieves the list of proposal hashes currently present on the Bittensor blockchain. Each hash + uniquely identifies a proposal made within the network. + + Args: + block (Optional[int], optional): The blockchain block number to query the proposal hashes. + + Returns: + Optional[List[str]]: A list of proposal hashes, or None if not available. + + This function enables tracking and reviewing the proposals made in the network, offering insights + into the active governance and decision-making processes. + """ proposal_hashes = self.query_module( module="Triumvirate", name="Proposals", block=block ) @@ -1253,6 +1784,21 @@ def get_proposals( ) -> Optional[ Dict[str, Tuple["bittensor.ProposalCallData", "bittensor.ProposalVoteData"]] ]: + """ + Retrieves all active proposals on the Bittensor blockchain, along with their call and voting data. + This comprehensive view allows for a thorough understanding of the proposals and their reception + by the senate. + + Args: + block (Optional[int], optional): The blockchain block number to query the proposals. + + Returns: + Optional[Dict[str, Tuple[bittensor.ProposalCallData, bittensor.ProposalVoteData]]]: + A dictionary mapping proposal hashes to their corresponding call and vote data, or None if not available. + + This function is integral for analyzing the governance activity on the Bittensor network, + providing a holistic view of the proposals and their impact or potential changes within the network. + """ proposals = {} proposal_hashes: List = self.get_proposal_hashes(block=block) @@ -1275,7 +1821,22 @@ def root_register( wait_for_finalization: bool = True, prompt: bool = False, ) -> bool: - """Registers the wallet to root network.""" + """ + Registers the neuron associated with the wallet on the root network. This process is integral for + participating in the highest layer of decision-making and governance within the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron to be registered on the root network. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the registration on the root network is successful, False otherwise. + + This function enables neurons to engage in the most critical and influential aspects of the network's + governance, signifying a high level of commitment and responsibility in the Bittensor ecosystem. + """ return root_register_extrinsic( subtensor=self, wallet=wallet, @@ -1332,7 +1893,25 @@ def root_set_weights( wait_for_finalization: bool = False, prompt: bool = False, ) -> bool: - """Sets weights for the root network.""" + """ + Sets the weights for neurons on the root network. This action is crucial for defining the influence + and interactions of neurons at the root level of the Bittensor network. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron setting the weights. + netuids (Union[torch.LongTensor, list]): The list of neuron UIDs for which weights are being set. + weights (Union[torch.FloatTensor, list]): The corresponding weights to be set for each UID. + version_key (int, optional): Version key for compatibility with the network. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + prompt (bool, optional): If True, prompts for user confirmation before proceeding. + + Returns: + bool: True if the setting of root-level weights is successful, False otherwise. + + This function plays a pivotal role in shaping the root network's collective intelligence and decision-making + processes, reflecting the principles of decentralized governance and collaborative learning in Bittensor. + """ return set_root_weights_extrinsic( subtensor=self, wallet=wallet, @@ -1355,6 +1934,24 @@ def query_identity( key: str, block: Optional[int] = None, ) -> Optional[object]: + """ + Queries the identity of a neuron on the Bittensor blockchain using the given key. This function retrieves + detailed identity information about a specific neuron, which is a crucial aspect of the network's decentralized + identity and governance system. + + NOTE: See the bittensor cli documentation for supported identity parameters. + + Args: + key (str): The key used to query the neuron's identity, typically the neuron's SS58 address. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[object]: An object containing the identity information of the neuron if found, None otherwise. + + The identity information can include various attributes such as the neuron's stake, rank, and other + network-specific details, providing insights into the neuron's role and status within the Bittensor network. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1381,7 +1978,23 @@ def update_identity( wait_for_finalization: bool = False, ) -> bool: """ - Creates an identity extrinsics with the specific structure. + Updates the identity of a neuron on the Bittensor blockchain. This function allows neurons to modify their + identity attributes, reflecting changes in their roles, stakes, or other network-specific parameters. + + NOTE: See the bittensor cli documentation for supported identity parameters. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron whose identity is being updated. + identified (str, optional): The identified SS58 address of the neuron. Defaults to the wallet's coldkey address. + params (dict, optional): A dictionary of parameters to update in the neuron's identity. + wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. + wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. + + Returns: + bool: True if the identity update is successful, False otherwise. + + This function plays a vital role in maintaining the accuracy and currency of neuron identities in the + Bittensor network, ensuring that the network's governance and consensus mechanisms operate effectively. """ if identified == None: identified = wallet.coldkey.ss58_address @@ -1428,12 +2041,28 @@ def query_subtensor( block: Optional[int] = None, params: Optional[List[object]] = [], ) -> Optional[object]: - @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="SubtensorModule", - storage_function=name, + """ + Queries named storage from the Subtensor module on the Bittensor blockchain. This function is used to retrieve + specific data or parameters from the blockchain, such as stake, rank, or other neuron-specific attributes. + + Args: + name (str): The name of the storage function to query. + block (Optional[int], optional): The blockchain block number at which to perform the query. + params (Optional[List[object]], optional): A list of parameters to pass to the query function. + + Returns: + Optional[object]: An object containing the requested data if found, None otherwise. + + This query function is essential for accessing detailed information about the network and its neurons, + providing valuable insights into the state and dynamics of the Bittensor ecosystem. + """ + + @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="SubtensorModule", + storage_function=name, params=params, block_hash=None if block == None @@ -1450,6 +2079,22 @@ def query_map_subtensor( block: Optional[int] = None, params: Optional[List[object]] = [], ) -> QueryMapResult: + """ + Queries map storage from the Subtensor module on the Bittensor blockchain. This function is designed to + retrieve a map-like data structure, which can include various neuron-specific details or network-wide attributes. + + Args: + name (str): The name of the map storage function to query. + block (Optional[int], optional): The blockchain block number at which to perform the query. + params (Optional[List[object]], optional): A list of parameters to pass to the query function. + + Returns: + QueryMapResult: An object containing the map-like data structure, or None if not found. + + This function is particularly useful for analyzing and understanding complex network structures and + relationships within the Bittensor ecosystem, such as inter-neuronal connections and stake distributions. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1464,11 +2109,27 @@ def make_substrate_call_with_retry(): return make_substrate_call_with_retry() - """ Gets a constant from subtensor with module_name, constant_name, and block. """ - def query_constant( self, module_name: str, constant_name: str, block: Optional[int] = None ) -> Optional[object]: + """ + Retrieves a constant from the specified module on the Bittensor blockchain. This function is used to + access fixed parameters or values defined within the blockchain's modules, which are essential for + understanding the network's configuration and rules. + + Args: + module_name (str): The name of the module containing the constant. + constant_name (str): The name of the constant to retrieve. + block (Optional[int], optional): The blockchain block number at which to query the constant. + + Returns: + Optional[object]: The value of the constant if found, None otherwise. + + Constants queried through this function can include critical network parameters such as inflation rates, + consensus rules, or validation thresholds, providing a deeper understanding of the Bittensor network's + operational parameters. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1491,6 +2152,24 @@ def query_module( block: Optional[int] = None, params: Optional[List[object]] = [], ) -> Optional[object]: + """ + Queries any module storage on the Bittensor blockchain with the specified parameters and block number. + This function is a generic query interface that allows for flexible and diverse data retrieval from + various blockchain modules. + + Args: + module (str): The name of the module from which to query data. + name (str): The name of the storage function within the module. + block (Optional[int], optional): The blockchain block number at which to perform the query. + params (Optional[List[object]], optional): A list of parameters to pass to the query function. + + Returns: + Optional[object]: An object containing the requested data if found, None otherwise. + + This versatile query function is key to accessing a wide range of data and insights from different + parts of the Bittensor blockchain, enhancing the understanding and analysis of the network's state and dynamics. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1514,6 +2193,23 @@ def query_map( block: Optional[int] = None, params: Optional[List[object]] = [], ) -> Optional[object]: + """ + Queries map storage from any module on the Bittensor blockchain. This function retrieves data structures + that represent key-value mappings, essential for accessing complex and structured data within the blockchain modules. + + Args: + module (str): The name of the module from which to query the map storage. + name (str): The specific storage function within the module to query. + block (Optional[int], optional): The blockchain block number at which to perform the query. + params (Optional[List[object]], optional): Parameters to be passed to the query. + + Returns: + Optional[object]: A data structure representing the map storage if found, None otherwise. + + This function is particularly useful for retrieving detailed and structured data from various blockchain + modules, offering insights into the network's state and the relationships between its different components. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1534,6 +2230,22 @@ def state_call( data: str, block: Optional[int] = None, ) -> Optional[object]: + """ + Makes a state call to the Bittensor blockchain, allowing for direct queries of the blockchain's state. + This function is typically used for advanced queries that require specific method calls and data inputs. + + Args: + method (str): The method name for the state call. + data (str): The data to be passed to the method. + block (Optional[int], optional): The blockchain block number at which to perform the state call. + + Returns: + Optional[object]: The result of the state call if successful, None otherwise. + + The state call function provides a more direct and flexible way of querying blockchain data, + useful for specific use cases where standard queries are insufficient. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -1553,7 +2265,21 @@ def query_runtime_api( block: Optional[int] = None, ) -> Optional[bytes]: """ - Returns a Scale Bytes type that should be decoded. + Queries the runtime API of the Bittensor blockchain, providing a way to interact with the underlying + runtime and retrieve data encoded in Scale Bytes format. This function is essential for advanced users + who need to interact with specific runtime methods and decode complex data types. + + Args: + runtime_api (str): The name of the runtime API to query. + method (str): The specific method within the runtime API to call. + params (Optional[List[ParamWithTypes]], optional): The parameters to pass to the method call. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[bytes]: The Scale Bytes encoded result from the runtime API call, or None if the call fails. + + This function enables access to the deeper layers of the Bittensor blockchain, allowing for detailed + and specific interactions with the network's runtime environment. """ call_definition = bittensor.__type_registry__["runtime_api"][runtime_api][ "methods" @@ -1610,32 +2336,97 @@ def _encode_params( #### Hyper parameter calls. #### ##################################### - """ Returns network Rho hyper parameter """ - def rho(self, netuid: int, block: Optional[int] = None) -> Optional[int]: + """ + Retrieves the 'Rho' hyperparameter for a specified subnet within the Bittensor network. + 'Rho' represents the global inflation rate, which directly influences the network's + token emission rate and economic model. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to query the parameter. + + Returns: + Optional[int]: The value of the 'Rho' hyperparameter if the subnet exists, None otherwise. + + Mathematical Context: + Rho (p) is calculated based on the network's target inflation and actual neuron staking. + It adjusts the emission rate of the TAO cryptocurrency to balance the network's economy. + The formula for Rho is defined as: p = (Staking_Target / Staking_Actual) * Inflation_Target. + Here, Staking_Target and Staking_Actual represent the desired and actual total stakes in the network, + while Inflation_Target is the predefined inflation rate goal​``【oaicite:0】``​. + + 'Rho' is essential for understanding the network's economic dynamics, affecting the reward distribution + and incentive structures across the network's neurons. + """ if not self.subnet_exists(netuid, block): return None return self.query_subtensor("Rho", block, [netuid]).value - """ Returns network Kappa hyper parameter """ - def kappa(self, netuid: int, block: Optional[int] = None) -> Optional[float]: + """ + Retrieves the 'Kappa' hyperparameter for a specified subnet. 'Kappa' is a critical parameter in + the Bittensor network that controls the distribution of stake weights among neurons, impacting their + rankings and incentive allocations. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[float]: The value of the 'Kappa' hyperparameter if the subnet exists, None otherwise. + + Mathematical Context: + Kappa (κ) is used in the calculation of neuron ranks, which determine their share of network incentives. + It is derived from the softmax function applied to the inter-neuronal weights set by each neuron. + The formula for Kappa is: κ_i = exp(w_i) / Σ(exp(w_j)), where w_i represents the weight set by neuron i, + and the denominator is the sum of exponential weights set by all neurons. + This mechanism ensures a normalized and probabilistic distribution of ranks based on relative weights​``【oaicite:0】``​. + + Understanding 'Kappa' is crucial for analyzing stake dynamics and the consensus mechanism within the network, + as it plays a significant role in neuron ranking and incentive allocation processes. + """ if not self.subnet_exists(netuid, block): return None return U16_NORMALIZED_FLOAT( self.query_subtensor("Kappa", block, [netuid]).value ) - """ Returns network Difficulty hyper parameter """ - def difficulty(self, netuid: int, block: Optional[int] = None) -> Optional[int]: + """ + Retrieves the 'Difficulty' hyperparameter for a specified subnet in the Bittensor network. + This parameter is instrumental in determining the computational challenge required for neurons + to participate in consensus and validation processes. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[int]: The value of the 'Difficulty' hyperparameter if the subnet exists, None otherwise. + + The 'Difficulty' parameter directly impacts the network's security and integrity by setting the + computational effort required for validating transactions and participating in the network's consensus mechanism. + """ if not self.subnet_exists(netuid, block): return None return self.query_subtensor("Difficulty", block, [netuid]).value - """ Returns network Burn hyper parameter """ - def burn(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]: + """ + Retrieves the 'Burn' hyperparameter for a specified subnet. The 'Burn' parameter represents the + amount of cryptocurrency that is effectively removed from circulation within the Bittensor network. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[Balance]: The value of the 'Burn' hyperparameter if the subnet exists, None otherwise. + + Understanding the 'Burn' rate is essential for analyzing the network's economic model, particularly + how it manages inflation and the overall supply of its native cryptocurrency. + """ if not self.subnet_exists(netuid, block): return None return Balance.from_rao(self.query_subtensor("Burn", block, [netuid]).value) @@ -1645,118 +2436,121 @@ def burn(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]: def immunity_period( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """ + Retrieves the 'ImmunityPeriod' hyperparameter for a specific subnet. This parameter defines the + duration during which new neurons are protected from certain network penalties or restrictions. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[int]: The value of the 'ImmunityPeriod' hyperparameter if the subnet exists, None otherwise. + + The 'ImmunityPeriod' is a critical aspect of the network's governance system, ensuring that new + participants have a grace period to establish themselves and contribute to the network without facing + immediate punitive actions. + """ if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ImmunityPeriod", block, [netuid]).value - """ Returns network ValidatorBatchSize hyper parameter """ - def validator_batch_size( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network ValidatorBatchSize hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ValidatorBatchSize", block, [netuid]).value - """ Returns network ValidatorPruneLen hyper parameter """ - def validator_prune_len(self, netuid: int, block: Optional[int] = None) -> int: + """Returns network ValidatorPruneLen hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ValidatorPruneLen", block, [netuid]).value - """ Returns network ValidatorLogitsDivergence hyper parameter """ - def validator_logits_divergence( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """Returns network ValidatorLogitsDivergence hyper parameter""" if not self.subnet_exists(netuid, block): return None return U16_NORMALIZED_FLOAT( self.query_subtensor("ValidatorLogitsDivergence", block, [netuid]).value ) - """ Returns network ValidatorSequenceLength hyper parameter """ - def validator_sequence_length( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network ValidatorSequenceLength hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ValidatorSequenceLength", block, [netuid]).value - """ Returns network ValidatorEpochsPerReset hyper parameter """ - def validator_epochs_per_reset( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network ValidatorEpochsPerReset hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ValidatorEpochsPerReset", block, [netuid]).value - """ Returns network ValidatorEpochLen hyper parameter """ - def validator_epoch_length( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network ValidatorEpochLen hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ValidatorEpochLen", block, [netuid]).value - """ Returns network ValidatorEpochLen hyper parameter """ - def validator_exclude_quantile( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """Returns network ValidatorEpochLen hyper parameter""" if not self.subnet_exists(netuid, block): return None return U16_NORMALIZED_FLOAT( self.query_subtensor("ValidatorExcludeQuantile", block, [netuid]).value ) - """ Returns network MaxAllowedValidators hyper parameter """ - def max_allowed_validators( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network MaxAllowedValidators hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("MaxAllowedValidators", block, [netuid]).value - """ Returns network MinAllowedWeights hyper parameter """ - def min_allowed_weights( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """Returns network MinAllowedWeights hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("MinAllowedWeights", block, [netuid]).value - """ Returns network MaxWeightsLimit hyper parameter """ - def max_weight_limit( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """Returns network MaxWeightsLimit hyper parameter""" if not self.subnet_exists(netuid, block): return None return U16_NORMALIZED_FLOAT( self.query_subtensor("MaxWeightsLimit", block, [netuid]).value ) - """ Returns network ScalingLawPower hyper parameter """ - def scaling_law_power( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """Returns network ScalingLawPower hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("ScalingLawPower", block, [netuid]).value / 100.0 - """ Returns network SynergyScalingLawPower hyper parameter """ - def synergy_scaling_law_power( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """Returns network SynergyScalingLawPower hyper parameter""" if not self.subnet_exists(netuid, block): return None return ( @@ -1764,30 +2558,26 @@ def synergy_scaling_law_power( / 100.0 ) - """ Returns network SubnetworkN hyper parameter """ - def subnetwork_n(self, netuid: int, block: Optional[int] = None) -> int: + """Returns network SubnetworkN hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("SubnetworkN", block, [netuid]).value - """ Returns network MaxAllowedUids hyper parameter """ - def max_n(self, netuid: int, block: Optional[int] = None) -> Optional[int]: + """Returns network MaxAllowedUids hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("MaxAllowedUids", block, [netuid]).value - """ Returns network BlocksSinceLastStep hyper parameter """ - def blocks_since_epoch(self, netuid: int, block: Optional[int] = None) -> int: + """Returns network BlocksSinceLastStep hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("BlocksSinceLastStep", block, [netuid]).value - """ Returns network Tempo hyper parameter """ - def tempo(self, netuid: int, block: Optional[int] = None) -> int: + """Returns network Tempo hyper parameter""" if not self.subnet_exists(netuid, block): return None return self.query_subtensor("Tempo", block, [netuid]).value @@ -1796,66 +2586,59 @@ def tempo(self, netuid: int, block: Optional[int] = None) -> int: #### Account functions ### ########################## - """ Returns the total stake held on a hotkey including delegative """ - def get_total_stake_for_hotkey( self, ss58_address: str, block: Optional[int] = None ) -> Optional["Balance"]: + """Returns the total stake held on a hotkey including delegative""" return Balance.from_rao( self.query_subtensor("TotalHotkeyStake", block, [ss58_address]).value ) - """ Returns the total stake held on a coldkey across all hotkeys including delegates""" - def get_total_stake_for_coldkey( self, ss58_address: str, block: Optional[int] = None ) -> Optional["Balance"]: + """Returns the total stake held on a coldkey across all hotkeys including delegates""" return Balance.from_rao( self.query_subtensor("TotalColdkeyStake", block, [ss58_address]).value ) - """ Returns the stake under a coldkey - hotkey pairing """ - def get_stake_for_coldkey_and_hotkey( self, hotkey_ss58: str, coldkey_ss58: str, block: Optional[int] = None ) -> Optional["Balance"]: + """Returns the stake under a coldkey - hotkey pairing""" return Balance.from_rao( self.query_subtensor("Stake", block, [hotkey_ss58, coldkey_ss58]).value ) - """ Returns a list of stake tuples (coldkey, balance) for each delegating coldkey including the owner""" - def get_stake( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[str, "Balance"]]: + """Returns a list of stake tuples (coldkey, balance) for each delegating coldkey including the owner""" return [ (r[0].value, Balance.from_rao(r[1].value)) for r in self.query_map_subtensor("Stake", block, [hotkey_ss58]) ] - """ Returns true if the hotkey is known by the chain and there are accounts. """ - def does_hotkey_exist(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: + """Returns true if the hotkey is known by the chain and there are accounts.""" return ( self.query_subtensor("Owner", block, [hotkey_ss58]).value != "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM" ) - """ Returns the coldkey owner of the passed hotkey """ - def get_hotkey_owner( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[str]: + """Returns the coldkey owner of the passed hotkey""" if self.does_hotkey_exist(hotkey_ss58, block): return self.query_subtensor("Owner", block, [hotkey_ss58]).value else: return None - """ Returns the axon information for this hotkey account """ - def get_axon_info( self, netuid: int, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[AxonInfo]: + """Returns the axon information for this hotkey account""" result = self.query_subtensor("Axons", block, [netuid, hotkey_ss58]) if result != None: return AxonInfo( @@ -1870,11 +2653,10 @@ def get_axon_info( else: return None - """ Returns the prometheus information for this hotkey account """ - def get_prometheus_info( self, netuid: int, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[AxonInfo]: + """Returns the prometheus information for this hotkey account""" result = self.query_subtensor("Prometheus", block, [netuid, hotkey_ss58]) if result != None: return PrometheusInfo( @@ -1901,14 +2683,57 @@ def block(self) -> int: return self.get_current_block() def total_issuance(self, block: Optional[int] = None) -> "Balance": + """ + Retrieves the total issuance of the Bittensor network's native cryptocurrency (TAO) as of a specific + blockchain block. This represents the total amount of currency that has been issued or mined on the network. + + Args: + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Balance: The total issuance of TAO, represented as a Balance object. + + The total issuance is a key economic indicator in the Bittensor network, reflecting the overall supply + of the currency and providing insights into the network's economic health and inflationary trends. + """ return Balance.from_rao(self.query_subtensor("TotalIssuance", block).value) def total_stake(self, block: Optional[int] = None) -> "Balance": + """ + Retrieves the total amount of TAO staked on the Bittensor network as of a specific blockchain block. + This represents the cumulative stake across all neurons in the network, indicating the overall level + of participation and investment by the network's participants. + + Args: + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Balance: The total amount of TAO staked on the network, represented as a Balance object. + + The total stake is an important metric for understanding the network's security, governance dynamics, + and the level of commitment by its participants. It is also a critical factor in the network's + consensus and incentive mechanisms. + """ return Balance.from_rao(self.query_subtensor("TotalStake", block).value) def serving_rate_limit( self, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """ + Retrieves the serving rate limit for a specific subnet within the Bittensor network. + This rate limit determines the maximum number of requests a neuron can serve within a given time frame. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[int]: The serving rate limit of the subnet if it exists, None otherwise. + + The serving rate limit is a crucial parameter for maintaining network efficiency and preventing + overuse of resources by individual neurons. It helps ensure a balanced distribution of service + requests across the network. + """ if not self.subnet_exists(netuid, block): return None return self.query_subtensor( @@ -1916,6 +2741,20 @@ def serving_rate_limit( ).value def tx_rate_limit(self, block: Optional[int] = None) -> Optional[int]: + """ + Retrieves the transaction rate limit for the Bittensor network as of a specific blockchain block. + This rate limit sets the maximum number of transactions that can be processed within a given time frame. + + Args: + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[int]: The transaction rate limit of the network, None if not available. + + The transaction rate limit is an essential parameter for ensuring the stability and scalability + of the Bittensor network. It helps in managing network load and preventing congestion, thereby + maintaining efficient and timely transaction processing. + """ return self.query_subtensor("TxRateLimit", block).value ##################################### @@ -1923,9 +2762,34 @@ def tx_rate_limit(self, block: Optional[int] = None) -> Optional[int]: ##################################### def subnet_exists(self, netuid: int, block: Optional[int] = None) -> bool: + """ + Checks if a subnet with the specified unique identifier (netuid) exists within the Bittensor network. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to check the subnet's existence. + + Returns: + bool: True if the subnet exists, False otherwise. + + This function is critical for verifying the presence of specific subnets in the network, + enabling a deeper understanding of the network's structure and composition. + """ return self.query_subtensor("NetworksAdded", block, [netuid]).value def get_all_subnet_netuids(self, block: Optional[int] = None) -> List[int]: + """ + Retrieves the list of all subnet unique identifiers (netuids) currently present in the Bittensor network. + + Args: + block (Optional[int], optional): The blockchain block number at which to retrieve the subnet netuids. + + Returns: + List[int]: A list of subnet netuids. + + This function provides a comprehensive view of the subnets within the Bittensor network, + offering insights into its diversity and scale. + """ subnet_netuids = [] result = self.query_map_subtensor("NetworksAdded", block) if result.records: @@ -1936,6 +2800,18 @@ def get_all_subnet_netuids(self, block: Optional[int] = None) -> List[int]: return subnet_netuids def get_total_subnets(self, block: Optional[int] = None) -> int: + """ + Retrieves the total number of subnets within the Bittensor network as of a specific blockchain block. + + Args: + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + int: The total number of subnets in the network. + + Understanding the total number of subnets is essential for assessing the network's growth and + the extent of its decentralized infrastructure. + """ return self.query_subtensor("TotalNetworks", block).value def get_subnet_modality( @@ -1951,6 +2827,20 @@ def get_subnet_connection_requirement( def get_emission_value_by_subnet( self, netuid: int, block: Optional[int] = None ) -> Optional[float]: + """ + Retrieves the emission value of a specific subnet within the Bittensor network. The emission value + represents the rate at which the subnet emits or distributes the network's native cryptocurrency. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[float]: The emission value of the subnet, None if not available. + + The emission value is a critical economic parameter, influencing the incentive distribution and + reward mechanisms within the subnet. + """ return Balance.from_rao( self.query_subtensor("EmissionValues", block, [netuid]).value ) @@ -1958,6 +2848,20 @@ def get_emission_value_by_subnet( def get_subnet_connection_requirements( self, netuid: int, block: Optional[int] = None ) -> Dict[str, int]: + """ + Retrieves the connection requirements for a specific subnet within the Bittensor network. This + function provides details on the criteria that must be met for neurons to connect to the subnet. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Dict[str, int]: A dictionary detailing the connection requirements for the subnet. + + Understanding these requirements is crucial for neurons looking to participate in or interact + with specific subnets, ensuring compliance with their connection standards. + """ result = self.query_map_subtensor("NetworkConnect", block, [netuid]) if result.records: requirements = {} @@ -1967,6 +2871,19 @@ def get_subnet_connection_requirements( return {} def get_subnets(self, block: Optional[int] = None) -> List[int]: + """ + Retrieves a list of all subnets currently active within the Bittensor network. This function + provides an overview of the various subnets and their identifiers. + + Args: + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[int]: A list of network UIDs representing each active subnet. + + This function is valuable for understanding the network's structure and the diversity of subnets + available for neuron participation and collaboration. + """ subnets = [] result = self.query_map_subtensor("NetworksAdded", block) if result.records: @@ -1977,6 +2894,20 @@ def get_subnets(self, block: Optional[int] = None) -> List[int]: return [] def get_all_subnets_info(self, block: Optional[int] = None) -> List[SubnetInfo]: + """ + Retrieves detailed information about all subnets within the Bittensor network. This function + provides comprehensive data on each subnet, including its characteristics and operational parameters. + + Args: + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[SubnetInfo]: A list of SubnetInfo objects, each containing detailed information about a subnet. + + Gaining insights into the subnets' details assists in understanding the network's composition, + the roles of different subnets, and their unique features. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -2000,6 +2931,21 @@ def make_substrate_call_with_retry(): def get_subnet_info( self, netuid: int, block: Optional[int] = None ) -> Optional[SubnetInfo]: + """ + Retrieves detailed information about a specific subnet within the Bittensor network. This function + provides key data on the subnet, including its operational parameters and network status. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[SubnetInfo]: Detailed information about the subnet, or None if not found. + + This function is essential for neurons and stakeholders interested in the specifics of a particular + subnet, including its governance, performance, and role within the broader network. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -2023,6 +2969,20 @@ def make_substrate_call_with_retry(): def get_subnet_hyperparameters( self, netuid: int, block: Optional[int] = None ) -> Optional[SubnetHyperparameters]: + """ + Retrieves the hyperparameters for a specific subnet within the Bittensor network. These hyperparameters + define the operational settings and rules governing the subnet's behavior. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[SubnetHyperparameters]: The subnet's hyperparameters, or None if not available. + + Understanding the hyperparameters is crucial for comprehending how subnets are configured and + managed, and how they interact with the network's consensus and incentive mechanisms. + """ hex_bytes_result = self.query_runtime_api( runtime_api="SubnetInfoRuntimeApi", method="get_subnet_hyperparams", @@ -2043,12 +3003,40 @@ def get_subnet_hyperparameters( def get_subnet_owner( self, netuid: int, block: Optional[int] = None ) -> Optional[str]: + """ + Retrieves the owner's address of a specific subnet within the Bittensor network. The owner is + typically the entity responsible for the creation and maintenance of the subnet. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[str]: The SS58 address of the subnet's owner, or None if not available. + + Knowing the subnet owner provides insights into the governance and operational control of the subnet, + which can be important for decision-making and collaboration within the network. + """ return self.query_subtensor("SubnetOwner", block, [netuid]).value #################### #### Nomination #### #################### def is_hotkey_delegate(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: + """ + Determines whether a given hotkey (public key) is a delegate on the Bittensor network. This function + checks if the neuron associated with the hotkey is part of the network's delegation system. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + bool: True if the hotkey is a delegate, False otherwise. + + Being a delegate is a significant status within the Bittensor network, indicating a neuron's + involvement in consensus and governance processes. + """ return hotkey_ss58 in [ info.hotkey_ss58 for info in self.get_delegates(block=block) ] @@ -2056,6 +3044,20 @@ def is_hotkey_delegate(self, hotkey_ss58: str, block: Optional[int] = None) -> b def get_delegate_take( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[float]: + """ + Retrieves the delegate 'take' percentage for a neuron identified by its hotkey. The 'take' + represents the percentage of rewards that the delegate claims from its nominators' stakes. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[float]: The delegate take percentage, None if not available. + + The delegate take is a critical parameter in the network's incentive structure, influencing + the distribution of rewards among neurons and their nominators. + """ return U16_NORMALIZED_FLOAT( self.query_subtensor("Delegates", block, [hotkey_ss58]).value ) @@ -2063,6 +3065,20 @@ def get_delegate_take( def get_nominators_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[str, Balance]]: + """ + Retrieves a list of nominators and their stakes for a neuron identified by its hotkey. + Nominators are neurons that stake their tokens on a delegate to support its operations. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[Tuple[str, Balance]]: A list of tuples containing each nominator's address and staked amount. + + This function provides insights into the neuron's support network within the Bittensor ecosystem, + indicating its trust and collaboration relationships. + """ result = self.query_map_subtensor("Stake", block, [hotkey_ss58]) if result.records: return [(record[0].value, record[1].value) for record in result.records] @@ -2072,6 +3088,21 @@ def get_nominators_for_hotkey( def get_delegate_by_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> Optional[DelegateInfo]: + """ + Retrieves detailed information about a delegate neuron based on its hotkey. This function provides + a comprehensive view of the delegate's status, including its stakes, nominators, and reward distribution. + + Args: + hotkey_ss58 (str): The SS58 address of the delegate's hotkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[DelegateInfo]: Detailed information about the delegate neuron, None if not found. + + This function is essential for understanding the roles and influence of delegate neurons within + the Bittensor network's consensus and governance structures. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(encoded_hotkey: List[int]): with self.substrate as substrate: @@ -2094,6 +3125,20 @@ def make_substrate_call_with_retry(encoded_hotkey: List[int]): return DelegateInfo.from_vec_u8(result) def get_delegates(self, block: Optional[int] = None) -> List[DelegateInfo]: + """ + Retrieves a list of all delegate neurons within the Bittensor network. This function provides an + overview of the neurons that are actively involved in the network's delegation system. + + Args: + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[DelegateInfo]: A list of DelegateInfo objects detailing each delegate's characteristics. + + Analyzing the delegate population offers insights into the network's governance dynamics and the + distribution of trust and responsibility among participating neurons. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -2117,7 +3162,20 @@ def make_substrate_call_with_retry(): def get_delegated( self, coldkey_ss58: str, block: Optional[int] = None ) -> List[Tuple[DelegateInfo, Balance]]: - """Returns the list of delegates that a given coldkey is staked to.""" + """ + Retrieves a list of delegates and their associated stakes for a given coldkey. This function + identifies the delegates that a specific account has staked tokens on. + + Args: + coldkey_ss58 (str): The SS58 address of the account's coldkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[Tuple[DelegateInfo, Balance]]: A list of tuples, each containing a delegate's information and staked amount. + + This function is important for account holders to understand their stake allocations and their + involvement in the network's delegation and consensus mechanisms. + """ @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(encoded_coldkey: List[int]): @@ -2147,8 +3205,20 @@ def make_substrate_call_with_retry(encoded_coldkey: List[int]): def get_stake_info_for_coldkey( self, coldkey_ss58: str, block: Optional[int] = None ) -> List[StakeInfo]: - """Returns the list of StakeInfo objects for this coldkey""" + """ + Retrieves stake information associated with a specific coldkey. This function provides details + about the stakes held by an account, including the staked amounts and associated delegates. + + Args: + coldkey_ss58 (str): The SS58 address of the account's coldkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[StakeInfo]: A list of StakeInfo objects detailing the stake allocations for the account. + Stake information is vital for account holders to assess their investment and participation + in the network's delegation and consensus processes. + """ encoded_coldkey = ss58_to_vec_u8(coldkey_ss58) hex_bytes_result = self.query_runtime_api( @@ -2171,7 +3241,20 @@ def get_stake_info_for_coldkey( def get_stake_info_for_coldkeys( self, coldkey_ss58_list: List[str], block: Optional[int] = None ) -> Dict[str, List[StakeInfo]]: - """Returns the list of StakeInfo objects for all coldkeys in the list.""" + """ + Retrieves stake information for a list of coldkeys. This function aggregates stake data for multiple + accounts, providing a collective view of their stakes and delegations. + + Args: + coldkey_ss58_list (List[str]): A list of SS58 addresses of the accounts' coldkeys. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Dict[str, List[StakeInfo]]: A dictionary mapping each coldkey to a list of its StakeInfo objects. + + This function is useful for analyzing the stake distribution and delegation patterns of multiple + accounts simultaneously, offering a broader perspective on network participation and investment strategies. + """ encoded_coldkeys = [ ss58_to_vec_u8(coldkey_ss58) for coldkey_ss58 in coldkey_ss58_list ] @@ -2200,11 +3283,37 @@ def get_stake_info_for_coldkeys( def is_hotkey_registered_any( self, hotkey_ss58: str, block: Optional[int] = None ) -> bool: + """ + Checks if a neuron's hotkey is registered on any subnet within the Bittensor network. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number at which to perform the check. + + Returns: + bool: True if the hotkey is registered on any subnet, False otherwise. + + This function is essential for determining the network-wide presence and participation of a neuron. + """ return len(self.get_netuids_for_hotkey(hotkey_ss58, block)) > 0 def is_hotkey_registered_on_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None ) -> bool: + """ + Checks if a neuron's hotkey is registered on a specific subnet within the Bittensor network. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to perform the check. + + Returns: + bool: True if the hotkey is registered on the specified subnet, False otherwise. + + This function helps in assessing the participation of a neuron in a particular subnet, + indicating its specific area of operation or influence within the network. + """ return self.get_uid_for_hotkey_on_subnet(hotkey_ss58, netuid, block) != None def is_hotkey_registered( @@ -2213,6 +3322,26 @@ def is_hotkey_registered( netuid: Optional[int] = None, block: Optional[int] = None, ) -> bool: + """ + Determines whether a given hotkey (public key) is registered in the Bittensor network, either + globally across any subnet or specifically on a specified subnet. This function checks the registration + status of a neuron identified by its hotkey, which is crucial for validating its participation and + activities within the network. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + netuid (Optional[int], optional): The unique identifier of the subnet to check the registration. + If None, the registration is checked across all subnets. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + bool: True if the hotkey is registered in the specified context (either any subnet or a specific subnet), + False otherwise. + + This function is important for verifying the active status of neurons in the Bittensor network. It aids + in understanding whether a neuron is eligible to participate in network processes such as consensus, + validation, and incentive distribution based on its registration status. + """ if netuid == None: return self.is_hotkey_registered_any(hotkey_ss58, block) else: @@ -2221,11 +3350,40 @@ def is_hotkey_registered( def get_uid_for_hotkey_on_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None ) -> Optional[int]: + """ + Retrieves the unique identifier (UID) for a neuron's hotkey on a specific subnet. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[int]: The UID of the neuron if it is registered on the subnet, None otherwise. + + The UID is a critical identifier within the network, linking the neuron's hotkey to its + operational and governance activities on a particular subnet. + """ return self.query_subtensor("Uids", block, [netuid, hotkey_ss58]).value def get_all_uids_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[int]: + """ + Retrieves all unique identifiers (UIDs) associated with a given hotkey across different subnets + within the Bittensor network. This function helps in identifying all the neuron instances that are + linked to a specific hotkey. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + List[int]: A list of UIDs associated with the given hotkey across various subnets. + + This function is important for tracking a neuron's presence and activities across different + subnets within the Bittensor ecosystem. + """ return [ self.get_uid_for_hotkey_on_subnet(hotkey_ss58, netuid, block) for netuid in self.get_netuids_for_hotkey(hotkey_ss58, block) @@ -2234,6 +3392,21 @@ def get_all_uids_for_hotkey( def get_netuids_for_hotkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[int]: + """ + Retrieves a list of subnet UIDs (netuids) for which a given hotkey is a member. This function + identifies the specific subnets within the Bittensor network where the neuron associated with + the hotkey is active. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + List[int]: A list of netuids where the neuron is a member. + + This function provides insights into a neuron's involvement and distribution across different + subnets, illustrating its role and contributions within the network. + """ result = self.query_map_subtensor("IsNetworkMember", block, [hotkey_ss58]) netuids = [] for netuid, is_member in result.records: @@ -2244,6 +3417,22 @@ def get_netuids_for_hotkey( def get_neuron_for_pubkey_and_subnet( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfo]: + """ + Retrieves information about a neuron based on its public key (hotkey SS58 address) and the specific + subnet UID (netuid). This function provides detailed neuron information for a particular subnet within + the Bittensor network. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[NeuronInfo]: Detailed information about the neuron if found, None otherwise. + + This function is crucial for accessing specific neuron data and understanding its status, stake, + and other attributes within a particular subnet of the Bittensor ecosystem. + """ return self.neuron_for_uid( self.get_uid_for_hotkey_on_subnet(hotkey_ss58, netuid, block=block), netuid, @@ -2253,6 +3442,21 @@ def get_neuron_for_pubkey_and_subnet( def get_all_neurons_for_pubkey( self, hotkey_ss58: str, block: Optional[int] = None ) -> List[NeuronInfo]: + """ + Retrieves information about all neuron instances associated with a given public key (hotkey SS58 + address) across different subnets of the Bittensor network. This function aggregates neuron data + from various subnets to provide a comprehensive view of a neuron's presence and status within the network. + + Args: + hotkey_ss58 (str): The SS58 address of the neuron's hotkey. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + List[NeuronInfo]: A list of NeuronInfo objects detailing the neuron's presence across various subnets. + + This function is valuable for analyzing a neuron's overall participation, influence, and + contributions across the Bittensor network. + """ netuids = self.get_netuids_for_hotkey(hotkey_ss58, block) uids = [self.get_uid_for_hotkey_on_subnet(hotkey_ss58, net) for net in netuids] return [self.neuron_for_uid(uid, net) for uid, net in list(zip(uids, netuids))] @@ -2260,11 +3464,43 @@ def get_all_neurons_for_pubkey( def neuron_has_validator_permit( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[bool]: + """ + Checks if a neuron, identified by its unique identifier (UID), has a validator permit on a specific + subnet within the Bittensor network. This function determines whether the neuron is authorized to + participate in validation processes on the subnet. + + Args: + uid (int): The unique identifier of the neuron. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + + Returns: + Optional[bool]: True if the neuron has a validator permit, False otherwise. + + This function is essential for understanding a neuron's role and capabilities within a specific + subnet, particularly regarding its involvement in network validation and governance. + """ return self.query_subtensor("ValidatorPermit", block, [netuid, uid]).value def neuron_for_wallet( self, wallet: "bittensor.wallet", netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfo]: + """ + Retrieves information about a neuron associated with a given wallet on a specific subnet. + This function provides detailed data about the neuron's status, stake, and activities based on + the wallet's hotkey address. + + Args: + wallet (bittensor.wallet): The wallet associated with the neuron. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number at which to perform the query. + + Returns: + Optional[NeuronInfo]: Detailed information about the neuron if found, None otherwise. + + This function is important for wallet owners to understand and manage their neuron's presence + and activities within a particular subnet of the Bittensor network. + """ return self.get_neuron_for_pubkey_and_subnet( wallet.hotkey.ss58_address, netuid=netuid, block=block ) @@ -2272,17 +3508,21 @@ def neuron_for_wallet( def neuron_for_uid( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfo]: - r"""Returns a list of neuron from the chain. + """ + Retrieves detailed information about a specific neuron identified by its unique identifier (UID) + within a specified subnet (netuid) of the Bittensor network. This function provides a comprehensive + view of a neuron's attributes, including its stake, rank, and operational status. + Args: - uid ( int ): - The uid of the neuron to query for. - netuid ( int ): - The uid of the network to query for. - block ( int ): - The neuron at a particular block + uid (int): The unique identifier of the neuron. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + Returns: - neuron (Optional[NeuronInfo]): - neuron metadata associated with uid or None if it does not exist. + Optional[NeuronInfo]: Detailed information about the neuron if found, None otherwise. + + This function is crucial for analyzing individual neurons' contributions and status within a specific + subnet, offering insights into their roles in the network's consensus and validation mechanisms. """ if uid == None: return NeuronInfo._null_neuron() @@ -2307,15 +3547,20 @@ def make_substrate_call_with_retry(): return NeuronInfo.from_vec_u8(result) def neurons(self, netuid: int, block: Optional[int] = None) -> List[NeuronInfo]: - r"""Returns a list of neuron from the chain. + """ + Retrieves a list of all neurons within a specified subnet of the Bittensor network. This function + provides a snapshot of the subnet's neuron population, including each neuron's attributes and network + interactions. + Args: - netuid ( int ): - The netuid of the subnet to pull neurons from. - block ( Optional[int] ): - block to sync from. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + Returns: - neuron (List[NeuronInfo]): - List of neuron metadata objects. + List[NeuronInfo]: A list of NeuronInfo objects detailing each neuron's characteristics in the subnet. + + Understanding the distribution and status of neurons within a subnet is key to comprehending the + network's decentralized structure and the dynamics of its consensus and governance processes. """ neurons_lite = self.neurons_lite(netuid=netuid, block=block) weights = self.weights(block=block, netuid=netuid) @@ -2336,17 +3581,20 @@ def neurons(self, netuid: int, block: Optional[int] = None) -> List[NeuronInfo]: def neuron_for_uid_lite( self, uid: int, netuid: int, block: Optional[int] = None ) -> Optional[NeuronInfoLite]: - r"""Returns a list of neuron lite from the chain. + """ + Retrieves a lightweight version of information about a neuron in a specific subnet, identified by + its UID. The 'lite' version focuses on essential attributes such as stake and network activity. + Args: - uid ( int ): - The uid of the neuron to query for. - netuid ( int ): - The uid of the network to query for. - block ( int ): - The neuron at a particular block + uid (int): The unique identifier of the neuron. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + Returns: - neuron (Optional[NeuronInfoLite]): - neuron metadata associated with uid or None if it does not exist. + Optional[NeuronInfoLite]: A simplified version of neuron information if found, None otherwise. + + This function is useful for quick and efficient analyses of neuron status and activities within a + subnet without the need for comprehensive data retrieval. """ if uid == None: return NeuronInfoLite._null_neuron() @@ -2374,15 +3622,20 @@ def neuron_for_uid_lite( def neurons_lite( self, netuid: int, block: Optional[int] = None ) -> List[NeuronInfoLite]: - r"""Returns a list of neuron lite from the chain. + """ + Retrieves a list of neurons in a 'lite' format from a specific subnet of the Bittensor network. + This function provides a streamlined view of the neurons, focusing on key attributes such as stake + and network participation. + Args: - netuid ( int ): - The netuid of the subnet to pull neurons from. - block ( Optional[int] ): - block to sync from. + netuid (int): The unique identifier of the subnet. + block (Optional[int], optional): The blockchain block number for the query. + Returns: - neuron (List[NeuronInfoLite]): - List of neuron lite metadata objects. + List[NeuronInfoLite]: A list of simplified neuron information for the subnet. + + This function offers a quick overview of the neuron population within a subnet, facilitating + efficient analysis of the network's decentralized structure and neuron dynamics. """ hex_bytes_result = self.query_runtime_api( runtime_api="NeuronInfoRuntimeApi", @@ -2407,17 +3660,20 @@ def metagraph( lite: bool = True, block: Optional[int] = None, ) -> "bittensor.Metagraph": - r"""Returns a synced metagraph for the subnet. + """ + Returns a synced metagraph for a specified subnet within the Bittensor network. The metagraph + represents the network's structure, including neuron connections and interactions. + Args: - netuid ( int ): - The network uid of the subnet to query. - lite (bool, default=True): - If true, returns a metagraph using the lite sync (no weights, no bonds) - block ( Optional[int] ): - block to sync from, or None for latest block. + netuid (int): The network UID of the subnet to query. + lite (bool, default=True): If true, returns a metagraph using a lightweight sync (no weights, no bonds). + block (Optional[int]): Block number for synchronization, or None for the latest block. + Returns: - metagraph ( `bittensor.Metagraph` ): - The metagraph for the subnet at the block. + bittensor.Metagraph: The metagraph representing the subnet's structure and neuron relationships. + + The metagraph is an essential tool for understanding the topology and dynamics of the Bittensor + network's decentralized architecture, particularly in relation to neuron interconnectivity and consensus processes. """ metagraph_ = bittensor.metagraph( network=self.network, netuid=netuid, lite=lite, sync=False @@ -2427,16 +3683,20 @@ def metagraph( return metagraph_ def incentive(self, netuid: int, block: Optional[int] = None) -> List[int]: - """Returns a list of incentives for the subnet. + """ + Retrieves the list of incentives for neurons within a specific subnet of the Bittensor network. + This function provides insights into the reward distribution mechanisms and the incentives allocated + to each neuron based on their contributions and activities. + Args: - netuid ( int ): - The network uid of the subnet to query. - block ( Optional[int] ): - block to sync from, or None for latest block. + netuid (int): The network UID of the subnet to query. + block (Optional[int]): The blockchain block number for the query. + Returns: - i_map ( List[int] ): - The list of incentives for the subnet at the block, - indexed by UID. + List[int]: The list of incentives for neurons within the subnet, indexed by UID. + + Understanding the incentive structure is crucial for analyzing the network's economic model and + the motivational drivers for neuron participation and collaboration. """ i_map = [] i_map_encoded = self.query_map_subtensor(name="Incentive", block=block) @@ -2451,6 +3711,21 @@ def incentive(self, netuid: int, block: Optional[int] = None) -> List[int]: def weights( self, netuid: int, block: Optional[int] = None ) -> List[Tuple[int, List[Tuple[int, int]]]]: + """ + Retrieves the weight distribution set by neurons within a specific subnet of the Bittensor network. + This function maps each neuron's UID to the weights it assigns to other neurons, reflecting the + network's trust and value assignment mechanisms. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int]): The blockchain block number for the query. + + Returns: + List[Tuple[int, List[Tuple[int, int]]]]: A list of tuples mapping each neuron's UID to its assigned weights. + + The weight distribution is a key factor in the network's consensus algorithm and the ranking of neurons, + influencing their influence and reward allocation within the subnet. + """ w_map = [] w_map_encoded = self.query_map_subtensor( name="Weights", block=block, params=[netuid] @@ -2464,6 +3739,23 @@ def weights( def bonds( self, netuid: int, block: Optional[int] = None ) -> List[Tuple[int, List[Tuple[int, int]]]]: + """ + Retrieves the bond distribution set by neurons within a specific subnet of the Bittensor network. + Bonds represent the investments or commitments made by neurons in one another, indicating a level + of trust and perceived value. This bonding mechanism is integral to the network's market-based approach + to measuring and rewarding machine intelligence. + + Args: + netuid (int): The network UID of the subnet to query. + block (Optional[int]): The blockchain block number for the query. + + Returns: + List[Tuple[int, List[Tuple[int, int]]]]: A list of tuples mapping each neuron's UID to its bonds with other neurons. + + Understanding bond distributions is crucial for analyzing the trust dynamics and market behavior + within the subnet. It reflects how neurons recognize and invest in each other's intelligence and + contributions, supporting diverse and niche systems within the Bittensor ecosystem. + """ b_map = [] b_map_encoded = self.query_map_subtensor( name="Bonds", block=block, params=[netuid] @@ -2477,19 +3769,19 @@ def bonds( def associated_validator_ip_info( self, netuid: int, block: Optional[int] = None ) -> Optional[List[IPInfo]]: - """Returns the list of all validator IPs associated with this subnet. + """ + Retrieves the list of all validator IP addresses associated with a specific subnet in the Bittensor + network. This information is crucial for network communication and the identification of validator nodes. Args: - netuid (int): - The network uid of the subnet to query. - block ( Optional[int] ): - block to sync from, or None for latest block. + netuid (int): The network UID of the subnet to query. + block (Optional[int]): The blockchain block number for the query. Returns: - validator_ip_info (Optional[List[IPInfo]]): - List of validator IP info objects for subnet. - or None if no validator IPs are associated with this subnet, - e.g. if the subnet does not exist. + Optional[List[IPInfo]]: A list of IPInfo objects for validator nodes in the subnet, or None if no validators are associated. + + Validator IP information is key for establishing secure and reliable connections within the network, + facilitating consensus and validation processes critical for the network's integrity and performance. """ hex_bytes_result = self.query_runtime_api( runtime_api="ValidatorIPRuntimeApi", @@ -2509,6 +3801,19 @@ def associated_validator_ip_info( return IPInfo.list_from_vec_u8(bytes_result) def get_subnet_burn_cost(self, block: Optional[int] = None) -> int: + """ + Retrieves the burn cost for registering a new subnet within the Bittensor network. This cost + represents the amount of cryptocurrency that needs to be locked or burned to establish a new subnet. + + Args: + block (Optional[int]): The blockchain block number for the query. + + Returns: + int: The burn cost for subnet registration. + + The subnet burn cost is an important economic parameter, reflecting the network's mechanisms for + controlling the proliferation of subnets and ensuring their commitment to the network's long-term viability. + """ lock_cost = self.query_runtime_api( runtime_api="SubnetRegistrationRuntimeApi", method="get_network_registration_cost", @@ -2670,13 +3975,19 @@ def make_substrate_call_with_retry(): ################ def get_balance(self, address: str, block: int = None) -> Balance: - r"""Returns the token balance for the passed ss58_address address + """ + Retrieves the token balance of a specific address within the Bittensor network. This function queries + the blockchain to determine the amount of cryptocurrency held by a given account. + Args: - address (Substrate address format, default = 42): - ss58 chain address. - Return: - balance (bittensor.utils.balance.Balance): - account balance + address (str): The Substrate address in ss58 format. + block (int, optional): The blockchain block number at which to perform the query. + + Returns: + Balance: The account balance at the specified block, represented as a Balance object. + + This function is important for monitoring account holdings and managing financial transactions + within the Bittensor ecosystem. It helps in assessing the economic status and capacity of network participants. """ try: @@ -2701,10 +4012,15 @@ def make_substrate_call_with_retry(): return Balance(result.value["data"]["free"]) def get_current_block(self) -> int: - r"""Returns the current block number on the chain. + """ + Returns the current block number on the Bittensor blockchain. This function provides the latest block + number, indicating the most recent state of the blockchain. + Returns: - block_number (int): - Current chain blocknumber. + int: The current chain block number. + + Knowing the current block number is essential for querying real-time data and performing time-sensitive + operations on the blockchain. It serves as a reference point for network activities and data synchronization. """ @retry(delay=2, tries=3, backoff=2, max_delay=4) @@ -2715,6 +4031,20 @@ def make_substrate_call_with_retry(): return make_substrate_call_with_retry() def get_balances(self, block: int = None) -> Dict[str, Balance]: + """ + Retrieves the token balances of all accounts within the Bittensor network as of a specific blockchain block. + This function provides a comprehensive view of the cryptocurrency distribution among different accounts. + + Args: + block (int, optional): The blockchain block number at which to perform the query. + + Returns: + Dict[str, Balance]: A dictionary mapping each account's ss58 address to its balance. + + This function is valuable for analyzing the overall economic landscape of the Bittensor network, + including the distribution of financial resources and the financial status of network participants. + """ + @retry(delay=2, tries=3, backoff=2, max_delay=4) def make_substrate_call_with_retry(): with self.substrate as substrate: @@ -2760,4 +4090,19 @@ def _null_neuron() -> NeuronInfo: return neuron def get_block_hash(self, block_id: int) -> str: + """ + Retrieves the hash of a specific block on the Bittensor blockchain. The block hash is a unique + identifier representing the cryptographic hash of the block's content, ensuring its integrity and + immutability. + + Args: + block_id (int): The block number for which the hash is to be retrieved. + + Returns: + str: The cryptographic hash of the specified block. + + The block hash is a fundamental aspect of blockchain technology, providing a secure reference to + each block's data. It is crucial for verifying transactions, ensuring data consistency, and + maintaining the trustworthiness of the blockchain. + """ return self.substrate.get_block_hash(block_id=block_id) From 5ca71e8a79521420ee7ebbd168c52a648807def0 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Mon, 27 Nov 2023 22:44:32 +0000 Subject: [PATCH 04/15] oops, revert change t local subtensor by default for now --- bittensor/subtensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index a60aa02ea4..2630cd803a 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -151,18 +151,18 @@ def help(cls): def add_args(cls, parser: argparse.ArgumentParser, prefix: str = None): prefix_str = "" if prefix == None else prefix + "." try: - default_network = os.getenv("BT_SUBTENSOR_NETWORK") or "local" + default_network = os.getenv("BT_SUBTENSOR_NETWORK") or "finney" default_chain_endpoint = ( os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") - or bittensor.__local_entrypoint__ + or bittensor.__finney_entrypoint__ ) parser.add_argument( "--" + prefix_str + "subtensor.network", default=default_network, type=str, help="""The subtensor network flag. The likely choices are: - -- local (local running network) -- finney (main network) + -- local (local running network) If this option is set it overloads subtensor.chain_endpoint with an entry point node from that network. """, From 1da39469b71a2b37ad2dea25c02191e0b08225b9 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 17:59:38 +0000 Subject: [PATCH 05/15] update axon.py docs overhaul --- bittensor/axon.py | 463 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 396 insertions(+), 67 deletions(-) diff --git a/bittensor/axon.py b/bittensor/axon.py index acce173b9d..a8e053fa76 100644 --- a/bittensor/axon.py +++ b/bittensor/axon.py @@ -45,19 +45,73 @@ from typing import Dict, Optional, Tuple, Union, List, Callable, Any -""" FastAPI server that runs in a thread. -""" - - class FastAPIThreadedServer(uvicorn.Server): + """ + The `FastAPIThreadedServer` class is a specialized server implementation for the Axon server in the + Bittensor network. It extends the functionality of `uvicorn.Server` to run the FastAPI application + in a separate thread, allowing the Axon server to handle HTTP requests concurrently and non-blocking. + + This class is designed to facilitate the integration of FastAPI with the Axon's asynchronous + architecture, ensuring efficient and scalable handling of network requests. + + Importance and Functionality: + Threaded Execution: The class allows the FastAPI application to run in a separate thread, + enabling concurrent handling of HTTP requests which is crucial for the performance and + scalability of the Axon server. + + Seamless Integration: By running FastAPI in a threaded manner, this class ensures seamless integration + of FastAPI's capabilities with the Axon server's asynchronous and multi-threaded architecture. + + Controlled Server Management: The methods start and stop provide controlled management of the server's + lifecycle, ensuring that the server can be started and stopped as needed, which is vital for + maintaining the Axon server's reliability and availability. + + Signal Handling: Overriding the default signal handlers prevents potential conflicts with the Axon + server's main application flow, ensuring stable operation in various network conditions. + + Use Cases: + Starting the Server: When the Axon server is initialized, it can use this class to start the + FastAPI application in a separate thread, enabling it to begin handling HTTP requests immediately. + Stopping the Server: During shutdown or maintenance of the Axon server, this class can be used + to stop the FastAPI application gracefully, ensuring that all resources are properly released. + + Attributes: + should_exit (bool): Flag to indicate whether the server should stop running. + is_running (bool): Flag to indicate whether the server is currently running. + + The server overrides the default signal handlers to prevent interference with the main application + flow and provides methods to start and stop the server in a controlled manner. + + Methods: + install_signal_handlers: Overrides the default signal handlers. + run_in_thread: Manages the execution of the server in a separate thread. + _wrapper_run: Wraps the run_in_thread method for thread execution. + start: Starts the server thread if it is not already running. + stop: Signals the server thread to stop running. + """ + should_exit: bool = False is_running: bool = False def install_signal_handlers(self): + """ + Overrides the default signal handlers provided by `uvicorn.Server`. This method is essential to + ensure that the signal handling in the threaded server does not interfere with the main + application's flow, especially in a complex asynchronous environment like the Axon server. + """ pass @contextlib.contextmanager def run_in_thread(self): + """ + Manages the execution of the server in a separate thread, allowing the FastAPI application to + run asynchronously without blocking the main thread of the Axon server. This method is a key + component in enabling concurrent request handling in the Axon server. + + Yields: + None: This method yields control back to the caller while the server is running in the + background thread. + """ thread = threading.Thread(target=self.run, daemon=True) thread.start() try: @@ -69,11 +123,23 @@ def run_in_thread(self): thread.join() def _wrapper_run(self): + """ + A wrapper method for the `run_in_thread` context manager. This method is used internally by the + `start` method to initiate the server's execution in a separate thread. + """ with self.run_in_thread(): while not self.should_exit: time.sleep(1e-3) def start(self): + """ + Starts the FastAPI server in a separate thread if it is not already running. This method sets up + the server to handle HTTP requests concurrently, enabling the Axon server to efficiently manage + incoming network requests. + + The method ensures that the server starts running in a non-blocking manner, allowing the Axon + server to continue its other operations seamlessly. + """ if not self.is_running: self.should_exit = False thread = threading.Thread(target=self._wrapper_run, daemon=True) @@ -81,26 +147,50 @@ def start(self): self.is_running = True def stop(self): + """ + Signals the FastAPI server to stop running. This method sets the `should_exit` flag to `True`, + indicating that the server should cease its operations and exit the running thread. + + Stopping the server is essential for controlled shutdowns and resource management in the Axon + server, especially during maintenance or when redeploying with updated configurations. + """ if self.is_running: self.should_exit = True class axon: """ - The `axon` class is an object that forms the core part of bittensor's serving synapses. - The class relies heavily on an underlying FastAPI router, which is utilized to create endpoints for different message types. - Methods in this class are equipped to deal with incoming requests from other scenarios in the network and serve as the server face for a neuron. - - It accepts multiple arguments, like wallet, configuration parameters, ip address, server binding port, external ip, external port and max workers. - Key methods involve managing and operating the FastAPI application router, including the attachment and operation of endpoints. - The `axon` class offers flexibility to specify custom rules to forward, blacklist, prioritize and verify incoming requests against custom functions. - - The class also encapsulates methods to add command-line arguments for user-friendly interaction with the program, and supports the handling of these arguments, - to define the behavior of the axon object. - - Internal mechanisms manage a thread pool to support concurrent requests handling, using defined priority levels. - - ### Example usage: + The `axon` class in Bittensor is a fundamental component that serves as the server-side + interface for a neuron within the Bittensor network. This class is responsible for managing + incoming requests from other neurons and implements various mechanisms to ensure efficient + and secure network interactions. + + An axon relies on a FastAPI router to create endpoints for different message types. These + endpoints are crucial for handling various request types that a neuron might receive. The + class is designed to be flexible and customizable, allowing users to specify custom rules + for forwarding, blacklisting, prioritizing, and verifying incoming requests. The class also + includes internal mechanisms to manage a thread pool, supporting concurrent handling of + requests with defined priority levels. + + Methods in this class are equipped to deal with incoming requests from other scenarios in the + network and serve as the server face for a neuron. It accepts multiple arguments, like wallet, + configuration parameters, ip address, server binding port, external ip, external port and max + workers. Key methods involve managing and operating the FastAPI application router, including + the attachment and operation of endpoints. + + The `axon` class offers flexibility to specify custom rules to forward, blacklist, prioritize and + verify incoming requests against custom functions. The class also encapsulates methods to add + command-line arguments for user-friendly interaction with the program, and supports the handling + of these arguments, to define the behavior of the axon object. + + Key Features: + - FastAPI router integration for endpoint creation and management. + - Customizable request handling including forwarding, blacklisting, and prioritization. + - Verification of incoming requests against custom-defined functions. + - Thread pool management for concurrent request handling. + - Command-line argument support for user-friendly program interaction. + + Example Usage: ```python import bittensor @@ -131,7 +221,14 @@ def prioritize_my_synape( synapse: MySyanpse ) -> float: return 1.0 # Initialize Axon object with a custom configuration - my_axon = bittensor.axon(config=my_config, wallet=my_wallet, port=9090, ip="192.0.2.0", external_ip="203.0.113.0", external_port=7070) + my_axon = bittensor.axon( + config=my_config, + wallet=my_wallet, + port=9090, + ip="192.0.2.0", + external_ip="203.0.113.0", + external_port=7070 + ) # Attach the endpoint with the specified verification and forwarding functions my_axon.attach( @@ -149,6 +246,42 @@ def prioritize_my_synape( synapse: MySyanpse ) -> float: subtensor = ... ).start() ``` + + Args: + wallet (bittensor.wallet, optional): Wallet with hotkey and coldkeypub. + config (bittensor.config, optional): Configuration parameters for the axon. + port (int, optional): Port for server binding. + ip (str, optional): Binding IP address. + external_ip (str, optional): External IP address to broadcast. + external_port (int, optional): External port to broadcast. + max_workers (int, optional): Number of active threads for request handling. + + Returns: + bittensor.axon: An instance of the axon class configured as per the provided arguments. + + Note: + This class is a core part of Bittensor's decentralized network for machine intelligence, + allowing neurons to communicate effectively and securely. + + Importance and Functionality + Endpoint Registration: This method dynamically registers API endpoints based on the Synapse class + used, allowing the Axon to respond to specific types of requests. + + Customization of Request Handling: By attaching different functions, the Axon can customize how it + handles, verifies, prioritizes, and potentially blocks incoming requests, making it adaptable + to various network scenarios. + + Security and Efficiency: The method contributes to both the security (via verification and blacklisting) + and efficiency (via prioritization) of request handling, which are crucial in a decentralized + network environment. + + Flexibility: The ability to define custom functions for different aspects of request handling + provides great flexibility, allowing the Axon to be tailored to specific needs and use cases + within the Bittensor network. + + Error Handling and Validation: The method ensures that the attached functions meet the required + signatures, providing error handling to prevent runtime issues. + """ def info(self) -> "bittensor.AxonInfo": @@ -273,19 +406,34 @@ def attach( verify_fn: Callable = None, ) -> "bittensor.axon": """ + + Attaches custom functions to the Axon server for handling incoming requests. This method enables + the Axon to define specific behaviors for request forwarding, verification, blacklisting, and + prioritization, thereby customizing its interaction within the Bittensor network. + Registers an API endpoint to the FastAPI application router. It uses the name of the first argument of the 'forward_fn' function as the endpoint name. + The attach method in the Bittensor framework's axon class is a crucial function for registering + API endpoints to the Axon's FastAPI application router. This method allows the Axon server to + define how it handles incoming requests by attaching functions for forwarding, verifying, + blacklisting, and prioritizing requests. It's a key part of customizing the server's behavior + and ensuring efficient and secure handling of requests within the Bittensor network. + Args: forward_fn (Callable): Function to be called when the API endpoint is accessed. It should have at least one argument. - blacklist_fn (Callable, optional): Function to filter out undesired requests. It should take the same arguments - as 'forward_fn' and return a boolean value. Defaults to None, meaning no blacklist filter will be used. - priority_fn (Callable, optional): Function to rank requests based on their priority. It should take the same arguments - as 'forward_fn' and return a numerical value representing the request's priority. - Defaults to None, meaning no priority sorting will be applied. - verify_fn (Callable, optional): Function to verify requests. It should take the same arguments as 'forward_fn' and return - a boolean value. If None, 'self.default_verify' function will be used. + blacklist_fn (Callable, optional): Function to filter out undesired requests. It should take + the same arguments as 'forward_fn' and return a boolean + value. Defaults to None, meaning no blacklist filter will + be used. + priority_fn (Callable, optional): Function to rank requests based on their priority. It should + take the same arguments as 'forward_fn' and return a numerical + value representing the request's priority. Defaults to None, + meaning no priority sorting will be applied. + verify_fn (Callable, optional): Function to verify requests. It should take the same arguments as + 'forward_fn' and return a boolean value. If None, + 'self.default_verify' function will be used. Note: 'forward_fn', 'blacklist_fn', 'priority_fn', and 'verify_fn' should be designed to receive the same parameters. @@ -297,6 +445,26 @@ def attach( Returns: self: Returns the instance of the AxonServer class for potential method chaining. + + Example Usage: + ```python + def forward_custom(synapse: MyCustomSynapse) -> MyCustomSynapse: + # Custom logic for processing the request + return synapse + + def verify_custom(synapse: MyCustomSynapse): + # Custom logic for verifying the request + pass + + my_axon = bittensor.axon(...) + my_axon.attach(forward_fn=forward_custom, verify_fn=verify_custom) + ``` + + Note: + The 'attach' method is fundamental in setting up the Axon server's request handling capabilities, + enabling it to participate effectively and securely in the Bittensor network. The flexibility + offered by this method allows developers to tailor the Axon's behavior to specific requirements and + use cases. """ # Assert 'forward_fn' has exactly one argument @@ -490,6 +658,10 @@ def add_args(cls, parser: argparse.ArgumentParser, prefix: str = None): async def verify_body_integrity(self, request: Request): """ + The verify_body_integrity method in the Bittensor framework is a key security function within the + Axon server's middleware. It is responsible for ensuring the integrity of the body of incoming HTTP + requests. + Asynchronously verifies the integrity of the body of a request by comparing the hash of required fields with the corresponding hashes provided in the request headers. This method is critical for ensuring that the incoming request payload has not been altered or tampered with during transmission, establishing @@ -507,6 +679,13 @@ async def verify_body_integrity(self, request: Request): indicating a potential integrity issue with the incoming request payload. The response includes the detailed error message specifying which field has a hash mismatch. + This method performs several key functions: + 1. Decoding and loading the request body for inspection. + 2. Gathering required field names for hash comparison from the Axon configuration. + 3. Loading and parsing the request body into a dictionary. + 4. Reconstructing the Synapse object and recomputing the hash for verification and logging. + 5. Comparing the recomputed hash with the hash provided in the request headers for verification. + Example: Assuming this method is set as a dependency in a route: @@ -515,6 +694,10 @@ async def some_endpoint(body_dict: dict = Depends(verify_body_integrity)): # body_dict is the parsed body of the request and is available for use in the route function. # The function only executes if the body integrity verification is successful. ... + Note: + The integrity verification is an essential step in ensuring the security of the data exchange + within the Bittensor network. It helps prevent tampering and manipulation of data during transit, + thereby maintaining the reliability and trust in the network communication. """ # Await and load the request body so we can inspect it body = await request.body() @@ -593,10 +776,26 @@ def __del__(self): def start(self) -> "bittensor.axon": """ - Starts the Axon server's GRPC server thread and marks the Axon as started. + Starts the Axon server and its underlying FastAPI server thread, transitioning the state of the + Axon instance to 'started'. This method initiates the server's ability to accept and process + incoming network requests, making it an active participant in the Bittensor network. + + The start method triggers the FastAPI server associated with the Axon to begin listening for + incoming requests. It is a crucial step in making the neuron represented by this Axon operational + within the Bittensor network. Returns: - bittensor.axon: The started Axon instance. + bittensor.axon: The Axon instance in the 'started' state. + + Example: + ```python + my_axon = bittensor.axon(...) + my_axon.start() # Starts the axon server + ``` + + Note: + After invoking this method, the Axon is ready to handle requests as per its configured + endpoints and custom logic. """ self.fast_server.start() self.started = True @@ -604,10 +803,28 @@ def start(self) -> "bittensor.axon": def stop(self) -> "bittensor.axon": """ - Stops the Axon server's GRPC server thread and marks the Axon as stopped. + Stops the Axon server and its underlying GRPC server thread, transitioning the state of the Axon + instance to 'stopped'. This method ceases the server's ability to accept new network requests, + effectively removing the neuron's server-side presence in the Bittensor network. + + By stopping the FastAPI server, the Axon ceases to listen for incoming requests, and any existing + connections are gracefully terminated. This function is typically used when the neuron is being + shut down or needs to temporarily go offline. Returns: - bittensor.axon: The stopped Axon instance. + bittensor.axon: The Axon instance in the 'stopped' state. + + Example: + ```python + my_axon = bittensor.axon(...) + my_axon.start() + ... + my_axon.stop() # Stops the axon server + ``` + + Note: + It is advisable to ensure that all ongoing processes or requests are completed or properly + handled before invoking this method. """ self.fast_server.stop() self.started = False @@ -617,15 +834,29 @@ def serve( self, netuid: int, subtensor: bittensor.subtensor = None ) -> "bittensor.axon": """ - Serves the axon on the passed subtensor connection using the axon wallet. + Serves the Axon on the specified subtensor connection using the configured wallet. This method + registers the Axon with a specific subnet within the Bittensor network, identified by the 'netuid'. + It links the Axon to the broader network, allowing it to participate in the decentralized exchange + of information. Args: - netuid: int - The subnet uid to register on. - subtensor: Optional[ bittensor.Subtensor ] - The subtensor connection to use for serving. + netuid (int): The unique identifier of the subnet to register on. This ID is essential for the + Axon to correctly position itself within the Bittensor network topology. + subtensor (bittensor.subtensor, optional): The subtensor connection to use for serving. If not + provided, a new connection is established based on default configurations. + Returns: - bittensor.axon: The served Axon instance. + bittensor.axon: The Axon instance that is now actively serving on the specified subtensor. + + Example: + ```python + my_axon = bittensor.axon(...) + my_axon.serve(netuid=12345) # Serves the axon on subnet with netuid 12345 + ``` + + Note: + The 'serve' method is crucial for integrating the Axon into the Bittensor network, allowing it + to start receiving and processing requests from other neurons. """ if subtensor == None: subtensor = bittensor.subtensor() @@ -635,8 +866,36 @@ def serve( async def default_verify(self, synapse: bittensor.Synapse): """ This method is used to verify the authenticity of a received message using a digital signature. + It ensures that the message was not tampered with and was sent by the expected sender. + The `default_verify` method in the Bittensor framework is a critical security function within the + Axon server. It is designed to authenticate incoming messages by verifying their digital + signatures. This verification ensures the integrity of the message and confirms that it was + indeed sent by the claimed sender. The method plays a pivotal role in maintaining the trustworthiness + and reliability of the communication within the Bittensor network. + + Key Features: + Security Assurance: The default_verify method is crucial for ensuring the security of the + Bittensor network. By verifying digital signatures, it guards against unauthorized access + and data manipulation. + + Preventing Replay Attacks: The method checks for increasing nonce values, which is a vital + step in preventing replay attacks. A replay attack involves an adversary reusing or + delaying the transmission of a valid data transmission to deceive the receiver. + + Authenticity and Integrity Checks: By verifying that the message's digital signature matches + its content, the method ensures the message's authenticity (it comes from the claimed + sender) and integrity (it hasn't been altered during transmission). + + Trust in Communication: This method fosters trust in the network communication. Neurons + (nodes in the Bittensor network) can confidently interact, knowing that the messages they + receive are genuine and have not been tampered with. + + Cryptographic Techniques: The method's reliance on asymmetric encryption techniques is a + cornerstone of modern cryptographic security, ensuring that only entities with the correct + cryptographic keys can participate in secure communication. + Args: synapse: bittensor.Synapse bittensor request synapse. @@ -650,7 +909,8 @@ async def default_verify(self, synapse: bittensor.Synapse): Note: The verification process assumes the use of an asymmetric encryption algorithm, - where the sender signs the message with their private key and the receiver verifies the signature using the sender's public key. + where the sender signs the message with their private key and the receiver verifies the + signature using the sender's public key. """ # Build the keypair from the dendrite_hotkey keypair = Keypair(ss58_address=synapse.dendrite.hotkey) @@ -678,10 +938,22 @@ async def default_verify(self, synapse: bittensor.Synapse): class AxonMiddleware(BaseHTTPMiddleware): """ - Class AxonMiddleware handles the entire process of the request to the Axon. - It fills the necessary information into the synapse and manages the logging of messages and errors. - It handles the verification, blacklist checking and running priority functions. - This class also runs the requested function and updates the headers of the response. + The `AxonMiddleware` class is a key component in the Axon server, responsible for processing all + incoming requests. It handles the essential tasks of verifying requests, executing blacklist checks, + running priority functions, and managing the logging of messages and errors. Additionally, the class + is responsible for updating the headers of the response and executing the requested functions. + + This middleware acts as an intermediary layer in request handling, ensuring that each request is + processed according to the defined rules and protocols of the Bittensor network. It plays a pivotal + role in maintaining the integrity and security of the network communication. + + Args: + app (FastAPI): An instance of the FastAPI application to which this middleware is attached. + axon (bittensor.axon): The Axon instance that will process the requests. + + The middleware operates by intercepting incoming requests, performing necessary preprocessing + (like verification and priority assessment), executing the request through the Axon's endpoints, and + then handling any postprocessing steps such as response header updating and logging. """ def __init__(self, app: "AxonMiddleware", axon: "bittensor.axon"): @@ -699,14 +971,29 @@ async def dispatch( self, request: Request, call_next: RequestResponseEndpoint ) -> Request: """ - Processes incoming requests. + Asynchronously processes incoming HTTP requests and returns the corresponding responses. This + method acts as the central processing unit of the AxonMiddleware, handling each step in the + request lifecycle. Args: - request(starlette: Request): The incoming request. - call_next(starlette: RequestResponseEndpoint): The function to call after processing the request. + request (Request): The incoming HTTP request to be processed. + call_next (RequestResponseEndpoint): A callable that processes the request and returns a + response. Returns: - response (starlette: Response): The processed request. + Response: The HTTP response generated after processing the request. + + This method performs several key functions: + 1. Request Preprocessing: Sets up Synapse object from request headers and fills necessary information. + 2. Logging: Logs the start of request processing. + 3. Blacklist Checking: Verifies if the request is blacklisted. + 4. Request Verification: Ensures the authenticity and integrity of the request. + 5. Priority Assessment: Evaluates and assigns priority to the request. + 6. Request Execution: Calls the next function in the middleware chain to process the request. + 7. Response Postprocessing: Updates response headers and logs the end of the request processing. + + The method also handles exceptions and errors that might occur during each stage, ensuring that + appropriate responses are returned to the client. """ # Records the start time of the request processing. start_time = time.time() @@ -784,10 +1071,24 @@ async def dispatch( async def preprocess(self, request: Request) -> bittensor.Synapse: """ - Perform preprocess operations for the request and generate the synapse state object. + Performs the initial processing of the incoming request. This method is responsible for + extracting relevant information from the request and setting up the Synapse object, which + represents the state and context of the request within the Axon server. Args: - synapse (Synapse): The synapse instance representing the request. + request (Request): The incoming request to be preprocessed. + + Returns: + bittensor.Synapse: The Synapse object representing the preprocessed state of the request. + + The preprocessing involves: + 1. Extracting the request name from the URL path. + 2. Creating a Synapse instance from the request headers using the appropriate class type. + 3. Filling in the Axon and Dendrite information into the Synapse object. + 4. Signing the Synapse from the Axon side using the wallet hotkey. + + This method sets the foundation for the subsequent steps in the request handling process, + ensuring that all necessary information is encapsulated within the Synapse object. """ # Extracts the request name from the URL path. request_name = request.url.path.split("/")[1] @@ -824,13 +1125,21 @@ async def preprocess(self, request: Request) -> bittensor.Synapse: async def verify(self, synapse: bittensor.Synapse): """ - Verify the request. + Verifies the authenticity and integrity of the request. This method ensures that the incoming + request meets the predefined security and validation criteria. Args: - synapse ( bittensor.Synapse ): The synapse instance representing the request. + synapse (bittensor.Synapse): The Synapse object representing the request. Raises: - Exception: If verification fails. + Exception: If the verification process fails due to unmet criteria or security concerns. + + The verification process involves: + 1. Retrieving the specific verification function for the request's Synapse type. + 2. Executing the verification function and handling any exceptions that arise. + + Successful verification allows the request to proceed further in the processing pipeline, while + failure results in an appropriate exception being raised. """ # Start of the verification process. Verification is the process where we ensure that # the incoming request is from a trusted source or fulfills certain requirements. @@ -861,13 +1170,22 @@ async def verify(self, synapse: bittensor.Synapse): async def blacklist(self, synapse: bittensor.Synapse): """ - Check if the request is blacklisted. + Checks if the request should be blacklisted. This method ensures that requests from disallowed + sources or with malicious intent are blocked from processing. This can be extremely useful for + preventing spam or other forms of abuse. The blacklist is a list of keys or identifiers that + are prohibited from accessing certain resources. Args: - synapse (Synapse): The synapse instance representing the request. + synapse (bittensor.Synapse): The Synapse object representing the request. Raises: - Exception: If the request is blacklisted. + Exception: If the request is found in the blacklist. + + The blacklist check involves: + 1. Retrieving the blacklist checking function for the request's Synapse type. + 2. Executing the check and handling the case where the request is blacklisted. + + If a request is blacklisted, it is blocked, and an exception is raised to halt further processing. """ # A blacklist is a list of keys or identifiers # that are prohibited from accessing certain resources. @@ -896,14 +1214,17 @@ async def blacklist(self, synapse: bittensor.Synapse): async def priority(self, synapse: bittensor.Synapse): """ - Execute the priority function for the request. + Executes the priority function for the request. This method assesses and assigns a priority + level to the request, determining its urgency and importance in the processing queue. - A priority function is a function that determines the priority or urgency of processing the request compared to other requests. Args: - synapse (bittensor.Synapse): The synapse instance representing the request. + synapse (bittensor.Synapse): The Synapse object representing the request. Raises: - Exception: If the priority function times out. + Exception: If the priority assessment process encounters issues, such as timeouts. + + The priority function plays a crucial role in managing the processing load and ensuring that + critical requests are handled promptly. """ # Retrieve the priority function from the 'priority_fns' dictionary that corresponds # to the request's name (synapse name). @@ -960,15 +1281,19 @@ async def run( request: Request, ) -> Response: """ - Execute the requested function. + Executes the requested function as part of the request processing pipeline. This method calls + the next function in the middleware chain to process the request and generate a response. Args: - synapse: ( bittensor.Synapse ): call state. - call_next: ( starlet RequestResponseEndpoint ): The function to call after processing the request. - request: ( starlet Request ): The incoming request. + synapse (bittensor.Synapse): The Synapse object representing the request. + call_next (RequestResponseEndpoint): The next function in the middleware chain to process requests. + request (Request): The original HTTP request. Returns: - response (starlet Response): The processed request. + Response: The HTTP response generated by processing the request. + + This method is a critical part of the request lifecycle, where the actual processing of the + request takes place, leading to the generation of a response. """ try: # The requested function is executed by calling the 'call_next' function, @@ -996,15 +1321,19 @@ async def postprocess( self, synapse: bittensor.Synapse, response: Response, start_time: float ) -> Response: """ - Perform post-processing operations on the request. + Performs the final processing on the response before sending it back to the client. This method + updates the response headers and logs the end of the request processing. Args: - synapse (bittensor.Synapse): The synapse instance representing the request. - response (starlet Response): The response from the requested function. - start_time (float): The start time of request processing. + synapse (bittensor.Synapse): The Synapse object representing the request. + response (Response): The response generated by processing the request. + start_time (float): The timestamp when the request processing started. Returns: - response (starlet Response): The processed request with updated headers. + Response: The final HTTP response, with updated headers, ready to be sent back to the client. + + Postprocessing is the last step in the request handling process, ensuring that the response is + properly formatted and contains all necessary information. """ # Set the status code of the synapse to "200" which indicates a successful response. synapse.axon.status_code = "200" From 8c09b15224f6a01d934c8b4799d094cbc755b42f Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 19:55:05 +0000 Subject: [PATCH 06/15] synapse update --- bittensor/synapse.py | 252 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 228 insertions(+), 24 deletions(-) diff --git a/bittensor/synapse.py b/bittensor/synapse.py index cb139d6648..5557d9f749 100644 --- a/bittensor/synapse.py +++ b/bittensor/synapse.py @@ -97,6 +97,55 @@ def cast_float(raw: str) -> float: class TerminalInfo(pydantic.BaseModel): + """ + TerminalInfo is a crucial class in the Bittensor framework, encapsulating detailed information about + a network synapse (node) involved in a communication process. This class serves as a metadata carrier, + providing essential details about the state and configuration of a terminal during network interactions. + + The TerminalInfo class contains information such as HTTP status codes and messages, processing times, + IP addresses, ports, Bittensor version numbers, and unique identifiers. These details are vital for + maintaining network reliability, security, and efficient data flow within the Bittensor network. + + This class includes Pydantic validators and root validators to enforce data integrity and format. It is + designed to be used natively within Synapses, so that you will not need to call this directly, but rather + is used as a helper class for Synapses. + + Attributes: + status_code (int): HTTP status code indicating the result of a network request. Essential for identifying the outcome of network interactions. + status_message (str): Descriptive message associated with the status code, providing additional context about the request's result. + process_time (float): Time taken by the terminal to process the call, important for performance monitoring and optimization. + ip (str): IP address of the terminal, crucial for network routing and data transmission. + port (int): Network port used by the terminal, key for establishing network connections. + version (int): Bittensor version running on the terminal, ensuring compatibility between different nodes in the network. + nonce (int): Unique, monotonically increasing number for each terminal, aiding in identifying and ordering network interactions. + uuid (str): Unique identifier for the terminal, fundamental for network security and identification. + hotkey (str): Encoded hotkey string of the terminal wallet, important for transaction and identity verification in the network. + signature (str): Digital signature verifying the tuple of nonce, axon_hotkey, dendrite_hotkey, and uuid, critical for ensuring data authenticity and security. + + Usage: + # Creating a TerminalInfo instance + terminal_info = TerminalInfo( + status_code=200, + status_message="Success", + process_time=0.1, + ip="198.123.23.1", + port=9282, + version=111, + nonce=111111, + uuid="5ecbd69c-1cec-11ee-b0dc-e29ce36fec1a", + hotkey="5EnjDGNqqWnuL2HCAdxeEtN2oqtXZw6BMBe936Kfy2PFz1J1", + signature="0x0813029319030129u4120u10841824y0182u091u230912u" + ) + + # Accessing TerminalInfo attributes + ip_address = terminal_info.ip + processing_duration = terminal_info.process_time + + # TerminalInfo can be used to monitor and verify network interactions, ensuring proper communication and security within the Bittensor network. + + TerminalInfo plays a pivotal role in providing transparency and control over network operations, making it an indispensable tool for developers and users interacting with the Bittensor ecosystem. + """ + class Config: validate_assignment = True @@ -203,6 +252,97 @@ class Config: class Synapse(pydantic.BaseModel): + """ + Represents a Synapse in the Bittensor network, serving as a communication schema between neurons (nodes). + Synapses ensure the format and correctness of transmission tensors according to the Bittensor protocol. + Each Synapse type is tailored for a specific machine learning (ML) task, following unique compression and + communication processes. This helps maintain sanitized, correct, and useful information flow across the network. + + The Synapse class encompasses essential network properties such as HTTP route names, timeouts, request sizes, and + terminal information. It also includes methods for serialization, deserialization, attribute setting, and hash + computation, ensuring secure and efficient data exchange in the network. + + The class includes Pydantic validators and root validators to enforce data integrity and format. Additionally, + properties like 'is_success', 'is_failure', 'is_timeout', etc., provide convenient status checks based on + dendrite responses. + + Think of Bittensor Synapses as glorified pydantic wrappers that have been designed to be used in a distributed + network. They provide a standardized way to communicate between neurons, and are the primary mechanism for + communication between neurons in Bittensor. + + Key Features: + + 1. HTTP Route Name (`name` attribute): + Enables the identification and proper routing of requests within the network. Essential for users + defining custom routes for specific machine learning tasks. + + 2. Query Timeout (`timeout` attribute): + Determines the maximum duration allowed for a query, ensuring timely responses and network + efficiency. Crucial for users to manage network latency and response times, particularly in + time-sensitive applications. + + 3. Request Sizes (`total_size`, `header_size` attributes): + Keeps track of the size of request bodies and headers, ensuring efficient data transmission without + overloading the network. Important for users to monitor and optimize the data payload, especially + in bandwidth-constrained environments. + + 4. Terminal Information (`dendrite`, `axon` attributes): + Stores information about the dendrite (receiving end) and axon (sending end), facilitating communication + between nodes. Users can access detailed information about the communication endpoints, aiding in + debugging and network analysis. + + 5. Body Hash Computation (`computed_body_hash`, `required_hash_fields`): + Ensures data integrity and security by computing hashes of transmitted data. Provides users with a + mechanism to verify data integrity and detect any tampering during transmission. + + 6. Serialization and Deserialization Methods: + Facilitates the conversion of Synapse objects to and from a format suitable for network transmission. + Essential for users who need to customize data formats for specific machine learning models or tasks. + + 7. Status Check Properties (`is_success`, `is_failure`, `is_timeout`, etc.): + Provides quick and easy methods to check the status of a request, improving error handling and + response management. Users can efficiently handle different outcomes of network requests, enhancing + the robustness of their applications. + + Example usage: + # Creating a Synapse instance with default values + synapse = Synapse() + + # Setting properties + synapse.timeout = 15.0 + synapse.name = "MySynapse" + + # Serializing and deserializing a Synapse instance + serialized_synapse = synapse.json() + deserialized_synapse = Synapse.parse_raw(serialized_synapse) + + # Checking the status of the dendrite + if synapse.is_success: + print("Request succeeded") + + Attributes: + name (str): HTTP route name, set on axon.attach. + timeout (float): Total query length, set by the dendrite terminal. + total_size (int): Total size of request body in bytes. + header_size (int): Size of request header in bytes. + dendrite (TerminalInfo): Information about the dendrite terminal. + axon (TerminalInfo): Information about the axon terminal. + computed_body_hash (str): Computed hash of the request body. + required_hash_fields (List[str]): Fields required to compute the body hash. + + Methods: + deserialize: Custom deserialization logic for subclasses. + __setattr__: Override method to make 'required_hash_fields' read-only. + get_total_size: Calculates and returns the total size of the object. + to_headers: Constructs a dictionary of headers from instance properties. + body_hash: Computes a SHA3-256 hash of the serialized body. + parse_headers_to_inputs: Parses headers to construct an inputs dictionary. + from_headers: Creates an instance from a headers dictionary. + + This class is a cornerstone in the Bittensor framework, providing the necessary tools for secure, efficient, and + standardized communication in a decentralized environment. + """ + class Config: validate_assignment = True @@ -218,6 +358,25 @@ def deserialize(self) -> "Synapse": By default, if a subclass does not provide its own implementation of this method, the Synapse's deserialize method will be used, returning the object instance as-is. + In its default form, this method simply returns the instance of the Synapse itself without any modifications. Subclasses of Synapse can override this method to add specific deserialization behaviors, such as converting serialized data back into complex object types or performing additional data integrity checks. + + Example: + class CustomSynapse(Synapse): + additional_data: str + + def deserialize(self) -> "CustomSynapse": + # Custom deserialization logic + # For example, decoding a base64 encoded string in 'additional_data' + if self.additional_data: + self.additional_data = base64.b64decode(self.additional_data).decode('utf-8') + return self + + serialized_data = '{"additional_data": "SGVsbG8gV29ybGQ="}' # Base64 for 'Hello World' + custom_synapse = CustomSynapse.parse_raw(serialized_data) + deserialized_synapse = custom_synapse.deserialize() + + # deserialized_synapse.additional_data would now be 'Hello World' + Returns: Synapse: The deserialized Synapse object. In this default implementation, it returns the object itself. """ @@ -318,6 +477,9 @@ def set_name_type(cls, values) -> dict: def __setattr__(self, name: str, value: Any): """ Override the __setattr__ method to make the `required_hash_fields` property read-only. + + This is a security mechanism such that the `required_hash_fields` property cannot be + overridden by the user or malicious code. """ if name == "body_hash": raise AttributeError( @@ -405,17 +567,27 @@ def failed_verification(self) -> bool: def to_headers(self) -> dict: """ - This function constructs a dictionary of headers from the properties of the instance. - - Headers for 'name' and 'timeout' are directly taken from the instance. - Further headers are constructed from the properties 'axon' and 'dendrite'. - - For non-optional objects, these are serialized and encoded before adding to the headers. - - Finally, the function adds the sizes of the headers and the total size to the headers. + Converts the state of a Synapse instance into a dictionary of HTTP headers. This method is essential for + packaging Synapse data for network transmission in the Bittensor framework, ensuring that each key aspect of + the Synapse is represented in a format suitable for HTTP communication. + + Process: + 1. Basic Information: It starts by including the 'name' and 'timeout' of the Synapse, which are fundamental + for identifying the query and managing its lifespan on the network. + 2. Complex Objects: The method serializes the 'axon' and 'dendrite' objects, if present, into strings. This + serialization is crucial for preserving the state and structure of these objects over the network. + 3. Encoding: Non-optional complex objects are serialized and encoded in base64, making them safe for HTTP + transport. + 4. Size Metrics: The method calculates and adds the size of headers and the total object size, providing + valuable information for network bandwidth management. + + Example Usage: + synapse = Synapse(name="ExampleSynapse", timeout=30) + headers = synapse.to_headers() + # headers now contains a dictionary representing the Synapse instance Returns: - dict: A dictionary of headers constructed from the properties of the instance. + dict: A dictionary containing key-value pairs representing the Synapse's properties, suitable for HTTP communication. """ # Initializing headers with 'name' and 'timeout' headers = {"name": self.name, "timeout": str(self.timeout)} @@ -476,15 +648,22 @@ def to_headers(self) -> dict: @property def body_hash(self) -> str: """ - Compute a SHA3-256 hash of the serialized body of the Synapse instance. + Computes a SHA3-256 hash of the serialized body of the Synapse instance. This hash is used to + ensure the data integrity and security of the Synapse instance when it's transmitted across the + network. It's a crucial feature for verifying that the data received is the same as the data sent. + + Process: + 1. Iterates over each required field as specified in `required_fields_hash`. + 2. Concatenates the string representation of these fields. + 3. Applies SHA3-256 hashing to the concatenated string to produce a unique fingerprint of the data. - The body of the Synapse instance comprises its serialized and encoded - non-optional fields. This property retrieves these fields using the - `required_fields_hash` field, then concatenates their string representations, - and finally computes a SHA3-256 hash of the resulting string. + Example: + synapse = Synapse(name="ExampleRoute", timeout=10) + hash_value = synapse.body_hash + # hash_value is the SHA3-256 hash of the serialized body of the Synapse instance Returns: - str: The hexadecimal representation of the SHA3-256 hash of the instance's body. + str: The SHA3-256 hash as a hexadecimal string, providing a fingerprint of the Synapse instance's data for integrity checks. """ # Hash the body for verification hashes = [] @@ -503,16 +682,29 @@ def body_hash(self) -> str: @classmethod def parse_headers_to_inputs(cls, headers: dict) -> dict: """ - This class method parses a given headers dictionary to construct an inputs dictionary. - Different types of fields ('axon', 'dendrite', 'tensor', and 'input_obj') are identified - by their prefixes, extracted, and transformed appropriately. - Remaining fields are directly assigned. + Interprets and transforms a given dictionary of headers into a structured dictionary, facilitating + the reconstruction of Synapse objects. This method is essential for parsing network-transmitted + data back into a Synapse instance, ensuring data consistency and integrity. + + Process: + 1. Separates headers into categories based on prefixes ('axon', 'dendrite', etc.). + 2. Decodes and deserializes 'input_obj' headers into their original objects. + 3. Assigns simple fields directly from the headers to the input dictionary. + + Example: + received_headers = { + 'bt_header_axon_address': '127.0.0.1', + 'bt_header_dendrite_port': '8080', + # Other headers... + } + inputs = Synapse.parse_headers_to_inputs(received_headers) + # inputs now contains a structured representation of Synapse properties based on the headers Args: - headers (dict): The dictionary of headers to parse + headers (dict): The headers dictionary to parse. Returns: - dict: The parsed inputs dictionary constructed from the headers + dict: A structured dictionary representing the inputs for constructing a Synapse instance. """ # Initialize the input dictionary with empty sub-dictionaries for 'axon' and 'dendrite' @@ -576,13 +768,25 @@ def parse_headers_to_inputs(cls, headers: dict) -> dict: @classmethod def from_headers(cls, headers: dict) -> "Synapse": """ - This class method creates an instance of the class from a given headers dictionary. + Constructs a new Synapse instance from a given headers dictionary, enabling the re-creation of the + Synapse's state as it was prior to network transmission. This method is a key part of the + deserialization process in the Bittensor network, allowing nodes to accurately reconstruct Synapse + objects from received data. + + Example: + received_headers = { + 'bt_header_axon_address': '127.0.0.1', + 'bt_header_dendrite_port': '8080', + # Other headers... + } + synapse = Synapse.from_headers(received_headers) + # synapse is a new Synapse instance reconstructed from the received headers Args: - headers (dict): The dictionary of headers to parse + headers (dict): The dictionary of headers containing serialized Synapse information. Returns: - Synapse: A new Synapse instance created from the parsed inputs + Synapse: A new instance of Synapse, reconstructed from the parsed header information, replicating the original instance's state. """ # Get the inputs dictionary from the headers From 63328a2499d1f02d895e7e22992c939bfa7cfdee Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 20:14:16 +0000 Subject: [PATCH 07/15] dendrite updates --- bittensor/dendrite.py | 125 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 15 deletions(-) diff --git a/bittensor/dendrite.py b/bittensor/dendrite.py index 3895d526c9..5217d29ed0 100644 --- a/bittensor/dendrite.py +++ b/bittensor/dendrite.py @@ -125,13 +125,21 @@ def __init__( @property async def session(self) -> aiohttp.ClientSession: """ - Asynchronous property that provides access to the internal aiohttp client session. + An asynchronous property that provides access to the internal aiohttp client session. - If the session is not already initialized, this property will instantiate a new - aiohttp.ClientSession and return it. + This property ensures the management of HTTP connections in an efficient way. It lazily + initializes the aiohttp.ClientSession on its first use. The session is then reused for subsequent + HTTP requests, offering performance benefits by reusing underlying connections. Returns: - aiohttp.ClientSession: The aiohttp client session instance. + aiohttp.ClientSession: The active aiohttp client session instance. If no session exists, a + new one is created and returned. This session is used for asynchronous HTTP requests within + the dendrite, adhering to the async nature of the network interactions in the Bittensor framework. + + Example: + async with dendrite_instance.session as session: + async with session.get('http://example.com') as response: + # Process the response """ if self._session is None: self._session = aiohttp.ClientSession() @@ -139,12 +147,17 @@ async def session(self) -> aiohttp.ClientSession: def close_session(self): """ - Closes the internal aiohttp client session in a synchronous manner. + Closes the internal aiohttp client session synchronously. - This method ensures that the resources tied with the aiohttp client session are released. - It should be called when the session is no longer needed, typically during the cleanup phase. + This method ensures the proper closure and cleanup of the aiohttp client session, releasing any + resources like open connections and internal buffers. It is crucial for preventing resource leakage + and should be called when the dendrite instance is no longer in use, especially in synchronous contexts. + + Note: This method utilizes asyncio's event loop to close the session asynchronously from a synchronous + context. It is advisable to use this method only when asynchronous context management is not feasible. Usage: + # When finished with dendrite in a synchronous context dendrite_instance.close_session() """ if self._session: @@ -156,17 +169,40 @@ async def aclose_session(self): """ Asynchronously closes the internal aiohttp client session. - Similar to the synchronous `close_session` method but designed to be used within - asynchronous contexts. This method ensures that all related resources are released. + This method is the asynchronous counterpart to the `close_session` method. It should be used in + asynchronous contexts to ensure that the aiohttp client session is closed properly. The method + releases resources associated with the session, such as open connections and internal buffers, + which is essential for resource management in asynchronous applications. Usage: + # When finished with dendrite in an asynchronous context await dendrite_instance.aclose_session() + + Example: + async with dendrite_instance: + # Operations using dendrite + pass + # The session will be closed automatically after the above block """ if self._session: await self._session.close() self._session = None def _get_endpoint_url(self, target_axon, request_name): + """ + Constructs the endpoint URL for a network request to a target axon. + + This internal method generates the full HTTP URL for sending a request to the specified axon. The + URL includes the IP address and port of the target axon, along with the specific request name. It + differentiates between requests to the local system (using '0.0.0.0') and external systems. + + Args: + target_axon: The target axon object containing IP and port information. + request_name: The specific name of the request being made. + + Returns: + str: A string representing the complete HTTP URL for the request. + """ endpoint = ( f"0.0.0.0:{str(target_axon.port)}" if target_axon.ip == str(self.external_ip) @@ -175,6 +211,20 @@ def _get_endpoint_url(self, target_axon, request_name): return f"http://{endpoint}/{request_name}" def _handle_request_errors(self, synapse, request_name, exception): + """ + Handles exceptions that occur during network requests, updating the synapse with appropriate status + codes and messages. + + This method interprets different types of exceptions and sets the corresponding status code and + message in the synapse object. It covers common network errors such as connection issues and timeouts. + + Args: + synapse: The synapse object associated with the request. + request_name: The name of the request during which the exception occurred. + exception: The exception object caught during the request. + + Note: This method updates the synapse object in-place. + """ if isinstance(exception, aiohttp.ClientConnectorError): synapse.dendrite.status_code = "503" synapse.dendrite.status_message = f"Service at {synapse.axon.ip}:{str(synapse.axon.port)}/{request_name} unavailable." @@ -190,11 +240,38 @@ def _handle_request_errors(self, synapse, request_name, exception): ) def _log_outgoing_request(self, synapse): + """ + Logs information about outgoing requests for debugging purposes. + + This internal method logs key details about each outgoing request, including the size of the + request, the name of the synapse, the axon's details, and a success indicator. This information + is crucial for monitoring and debugging network activity within the Bittensor network. + + To turn on debug messages, set the environment variable BITTENSOR_DEBUG to 1. or call the bittensor + debug method like so: + ```python + import bittensor + bittensor.debug() + ``` + + Args: + synapse: The synapse object representing the request being sent. + """ bittensor.logging.debug( f"dendrite | --> | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | 0 | Success" ) def _log_incoming_response(self, synapse): + """ + Logs information about incoming responses for debugging and monitoring. + + Similar to `_log_outgoing_request`, this method logs essential details of the incoming responses, + including the size of the response, synapse name, axon details, status code, and status message. + This logging is vital for troubleshooting and understanding the network interactions in Bittensor. + + Args: + synapse: The synapse object representing the received response. + """ bittensor.logging.debug( f"dendrite | <-- | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | {synapse.dendrite.status_code} | {synapse.dendrite.status_message}" ) @@ -300,13 +377,21 @@ async def query_all_axons( is_stream: bool, ) -> Union[AsyncGenerator[Any], bittenst.Synapse, bittensor.StreamingSynapse]: """ - Handles requests for all axons, either in streaming or non-streaming mode. + Handles the processing of requests to all targeted axons, accommodating both streaming and + non-streaming responses. + + This function manages the concurrent or sequential dispatch of requests to a list of axons. + It utilizes the `is_stream` parameter to determine the mode of response handling (streaming + or non-streaming). For each axon, it calls 'single_axon_response' and aggregates the responses. Args: - is_stream: If True, handles the axons in streaming mode. + is_stream (bool): Flag indicating whether the axon responses are expected to be streamed. + If True, responses are handled in streaming mode. Returns: - List of Synapse objects with responses. + List[Union[AsyncGenerator, bittensor.Synapse, bittensor.StreamingSynapse]]: A list + containing the responses from each axon. The type of each response depends on the + streaming mode and the type of synapse used. """ async def single_axon_response( @@ -315,13 +400,23 @@ async def single_axon_response( AsyncGenerator[Any], bittenst.Synapse, bittensor.StreamingSynapse ]: """ - Retrieve response for a single axon, either in streaming or non-streaming mode. + Manages the request and response process for a single axon, supporting both streaming and + non-streaming modes. + + This function is responsible for initiating a request to a single axon. Depending on the + 'is_stream' flag, it either uses 'call_stream' for streaming responses or 'call' for + standard responses. The function handles the response processing, catering to the specifics + of streaming or non-streaming data. Args: - target_axon: The target axon to send request to. + target_axon: The target axon object to which the request is to be sent. This object + contains the necessary information like IP address and port to formulate the request. Returns: - A Synapse object with the response. + Union[AsyncGenerator, bittensor.Synapse, bittensor.StreamingSynapse]: The response + from the targeted axon. In streaming mode, an AsyncGenerator is returned, yielding + data chunks. In non-streaming mode, a Synapse or StreamingSynapse object is returned + containing the response. """ if is_stream: # If in streaming mode, return the async_generator From 3bf93f29aa209410b02e7a4b12e02cde131b9522 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 20:19:54 +0000 Subject: [PATCH 08/15] wallet docstring --- bittensor/wallet.py | 50 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/bittensor/wallet.py b/bittensor/wallet.py index 19953f264a..d78405a01e 100644 --- a/bittensor/wallet.py +++ b/bittensor/wallet.py @@ -54,11 +54,51 @@ def display_mnemonic_msg(keypair: Keypair, key_type: str): class wallet: """ - Bittensor wallet maintenance class. Each wallet contains a coldkey and a hotkey. - The coldkey is the user's primary key for holding stake in their wallet - and is the only way that users can access Tao. Coldkeys can hold tokens and should be encrypted on your device. - The coldkey must be used to stake and unstake funds from a running node. The hotkey, on the other hand, is only used - for subscribing and setting weights from running code. Hotkeys are linked to coldkeys through the metagraph. + The wallet class in the Bittensor framework handles wallet functionality, crucial for participating in + the Bittensor network. It manages two types of keys: coldkey and hotkey, each serving different purposes + in network operations. Each wallet contains a coldkey and a hotkey. + + The coldkey is the user's primary key for holding stake in their wallet and is the only way that users + can access Tao. Coldkeys can hold tokens and should be encrypted on your device. + + The coldkey is the primary key used for securing the wallet's stake in the Bittensor network (Tao) and + is critical for financial transactions like staking and unstaking tokens. It's recommended to keep the + coldkey encrypted and secure, as it holds the actual tokens. + + The hotkey, in contrast, is used for operational tasks like subscribing to and setting weights in the + network. It's linked to the coldkey through the metagraph and does not directly hold tokens, thereby + offering a safer way to interact with the network during regular operations. + + Attributes: + name (str): The name of the wallet, used to identify it among possibly multiple wallets. + path (str): File system path where wallet keys are stored. + hotkey_str (str): String identifier for the hotkey. + _hotkey, _coldkey, _coldkeypub (bittensor.Keypair): Internal representations of the hotkey and coldkey. + + Methods: + create_if_non_existent, create, recreate: Methods to handle the creation of wallet keys. + get_coldkey, get_hotkey, get_coldkeypub: Methods to retrieve specific keys. + set_coldkey, set_hotkey, set_coldkeypub: Methods to set or update keys. + hotkey_file, coldkey_file, coldkeypub_file: Properties that return respective key file objects. + regenerate_coldkey, regenerate_hotkey, regenerate_coldkeypub: Methods to regenerate keys from different sources. + config, help, add_args: Utility methods for configuration and assistance. + + The wallet class is a fundamental component for users to interact securely with the Bittensor network, + facilitating both operational tasks and transactions involving value transfer across the network. + + Example Usage: + # Create a new wallet with default coldkey and hotkey names + my_wallet = wallet() + + # Access hotkey and coldkey + hotkey = my_wallet.get_hotkey() + coldkey = my_wallet.get_coldkey() + + # Update wallet hotkey + my_wallet.set_hotkey(new_hotkey) + + # Print wallet details + print(my_wallet) """ @classmethod From 5cce4463f3e8850abae3ece8c5f350c0955039a5 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 20:26:04 +0000 Subject: [PATCH 09/15] remove cryptocurrency mentions --- bittensor/commands/network.py | 2 +- bittensor/metagraph.py | 4 ++-- bittensor/subtensor.py | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bittensor/commands/network.py b/bittensor/commands/network.py index 37507f7241..32ae141cb6 100644 --- a/bittensor/commands/network.py +++ b/bittensor/commands/network.py @@ -93,7 +93,7 @@ class SubnetLockCostCommand: 1. It copies the user's current Bittensor configuration. 2. It initializes the Bittensor subtensor object with this configuration. 3. It then retrieves the subnet lock cost using the `get_subnet_burn_cost()` method from the subtensor object. - 4. The cost is displayed to the user in a readable format, indicating the amount of cryptocurrency required to lock for registering a new subnetwork. + 4. The cost is displayed to the user in a readable format, indicating the amount of Tao required to lock for registering a new subnetwork. In case of any errors during the process (e.g., network issues, configuration problems), the command will catch these exceptions and inform the user that it failed to retrieve the lock cost, along with the specific error encountered. diff --git a/bittensor/metagraph.py b/bittensor/metagraph.py index 612a49755a..8b308027b5 100644 --- a/bittensor/metagraph.py +++ b/bittensor/metagraph.py @@ -95,7 +95,7 @@ class metagraph(torch.nn.Module): with the network's latest state. stake, total_stake, ranks, trust, consensus, validator_trust, incentive, emission, dividends, active, last_update, validator_permit, weights, bonds, uids (torch.nn.Parameter): - - Stake: Represents the cryptocurrency staked by neurons, impacting their influence and + - Stake: Represents the amount of Tao staked by neurons, impacting their influence and earnings within the network. - Total Stake: The cumulative stake across all neurons. - Ranks: Neuron rankings as per the Yuma Consensus algorithm, influencing their incentive @@ -181,7 +181,7 @@ def I(self) -> torch.FloatTensor: def E(self) -> torch.FloatTensor: """ Denotes the emission values of neurons in the Bittensor network. Emissions refer to the distribution or - release of rewards (often in the form of cryptocurrency) to neurons, typically based on their stake and + release of rewards (often in the form of the Tao token) to neurons, typically based on their stake and performance. This mechanism is central to the network's incentive model, ensuring that active and contributing neurons are appropriately rewarded. diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 2630cd803a..0050ef0ee8 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -2342,6 +2342,8 @@ def rho(self, netuid: int, block: Optional[int] = None) -> Optional[int]: 'Rho' represents the global inflation rate, which directly influences the network's token emission rate and economic model. + Note: This is currently fixed such that the Bittensor blockchain emmits 7200 Tao per day. + Args: netuid (int): The unique identifier of the subnet. block (Optional[int], optional): The blockchain block number at which to query the parameter. @@ -2351,7 +2353,7 @@ def rho(self, netuid: int, block: Optional[int] = None) -> Optional[int]: Mathematical Context: Rho (p) is calculated based on the network's target inflation and actual neuron staking. - It adjusts the emission rate of the TAO cryptocurrency to balance the network's economy. + It adjusts the emission rate of the TAO token to balance the network's economy and dynamics. The formula for Rho is defined as: p = (Staking_Target / Staking_Actual) * Inflation_Target. Here, Staking_Target and Staking_Actual represent the desired and actual total stakes in the network, while Inflation_Target is the predefined inflation rate goal​``【oaicite:0】``​. @@ -2415,7 +2417,7 @@ def difficulty(self, netuid: int, block: Optional[int] = None) -> Optional[int]: def burn(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]: """ Retrieves the 'Burn' hyperparameter for a specified subnet. The 'Burn' parameter represents the - amount of cryptocurrency that is effectively removed from circulation within the Bittensor network. + amount of Tao that is effectively removed from circulation within the Bittensor network. Args: netuid (int): The unique identifier of the subnet. @@ -2425,7 +2427,7 @@ def burn(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]: Optional[Balance]: The value of the 'Burn' hyperparameter if the subnet exists, None otherwise. Understanding the 'Burn' rate is essential for analyzing the network's economic model, particularly - how it manages inflation and the overall supply of its native cryptocurrency. + how it manages inflation and the overall supply of its native token Tao. """ if not self.subnet_exists(netuid, block): return None @@ -2684,7 +2686,7 @@ def block(self) -> int: def total_issuance(self, block: Optional[int] = None) -> "Balance": """ - Retrieves the total issuance of the Bittensor network's native cryptocurrency (TAO) as of a specific + Retrieves the total issuance of the Bittensor network's native token (Tao) as of a specific blockchain block. This represents the total amount of currency that has been issued or mined on the network. Args: @@ -2829,7 +2831,7 @@ def get_emission_value_by_subnet( ) -> Optional[float]: """ Retrieves the emission value of a specific subnet within the Bittensor network. The emission value - represents the rate at which the subnet emits or distributes the network's native cryptocurrency. + represents the rate at which the subnet emits or distributes the network's native token (Tao). Args: netuid (int): The unique identifier of the subnet. @@ -3803,7 +3805,7 @@ def associated_validator_ip_info( def get_subnet_burn_cost(self, block: Optional[int] = None) -> int: """ Retrieves the burn cost for registering a new subnet within the Bittensor network. This cost - represents the amount of cryptocurrency that needs to be locked or burned to establish a new subnet. + represents the amount of Tao that needs to be locked or burned to establish a new subnet. Args: block (Optional[int]): The blockchain block number for the query. @@ -3977,7 +3979,7 @@ def make_substrate_call_with_retry(): def get_balance(self, address: str, block: int = None) -> Balance: """ Retrieves the token balance of a specific address within the Bittensor network. This function queries - the blockchain to determine the amount of cryptocurrency held by a given account. + the blockchain to determine the amount of Tao held by a given account. Args: address (str): The Substrate address in ss58 format. @@ -4033,7 +4035,7 @@ def make_substrate_call_with_retry(): def get_balances(self, block: int = None) -> Dict[str, Balance]: """ Retrieves the token balances of all accounts within the Bittensor network as of a specific blockchain block. - This function provides a comprehensive view of the cryptocurrency distribution among different accounts. + This function provides a comprehensive view of the token distribution among different accounts. Args: block (int, optional): The blockchain block number at which to perform the query. From d3b4fd6e900d68cf1f107a94338e4090d0bef55a Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:09:34 +0000 Subject: [PATCH 10/15] fix dendrite examples --- bittensor/dendrite.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/bittensor/dendrite.py b/bittensor/dendrite.py index 5217d29ed0..056977d975 100644 --- a/bittensor/dendrite.py +++ b/bittensor/dendrite.py @@ -131,15 +131,27 @@ async def session(self) -> aiohttp.ClientSession: initializes the aiohttp.ClientSession on its first use. The session is then reused for subsequent HTTP requests, offering performance benefits by reusing underlying connections. + This is used internally by the dendrite when querying axons, and should not be used directly + unless absolutely necessary for your application. + Returns: aiohttp.ClientSession: The active aiohttp client session instance. If no session exists, a new one is created and returned. This session is used for asynchronous HTTP requests within the dendrite, adhering to the async nature of the network interactions in the Bittensor framework. - Example: - async with dendrite_instance.session as session: - async with session.get('http://example.com') as response: - # Process the response + Example usage: + import bittensor as bt # Import bittensor + wallet = bt.wallet( ... ) # Initialize a wallet + dendrite = bt.dendrite( wallet ) # Initialize a dendrite instance with the wallet + + async with (await dendrite.session).post( # Use the session to make an HTTP POST request + url, # URL to send the request to + headers={...}, # Headers dict to be sent with the request + json={...}, # JSON body data to be sent with the request + timeout=10, # Timeout duration in seconds + ) as response: + json_response = await response.json() # Extract the JSON response from the server + """ if self._session is None: self._session = aiohttp.ClientSession() @@ -333,6 +345,18 @@ async def forward( the requests, and then sends them off. After getting the responses, it processes and collates them into a unified format. + When querying an Axon that sends a single response, this function returns a Synapse object + containing the response data. If multiple Axons are queried, a list of Synapse objects is + returned, each containing the response from the corresponding Axon. + + For example: + >>> ... + >>> wallet = bittensor.wallet() # Initialize a wallet + >>> synapse = bittensor.Synapse(...) # Create a synapse object that contains query data + >>> dendrte = bittensor.dendrite(wallet = wallet) # Initialize a dendrite instance + >>> axons = metagraph.axons # Create a list of axons to query + >>> responses = await dendrite(axons, synapse) # Send the query to all axons and await the responses + When querying an Axon that sends back data in chunks using the Dendrite, this function returns an AsyncGenerator that yields each chunk as it is received. The generator can be iterated over to process each chunk individually. @@ -765,8 +789,10 @@ async def __aexit__(self, exc_type, exc_value, traceback): where the exception was raised. Usage: - async with Dendrite() as dendrite: + async with bt.dendrite( wallet ) as dendrite: await dendrite.some_async_method() + + Note: This automatically closes the session by calling __aexit__ after the context closes. """ await self.aclose_session() @@ -783,6 +809,6 @@ def __del__(self): Usage: dendrite = Dendrite() # ... some operations ... - del dendrite # This will implicitly invoke the __del__ method. + del dendrite # This will implicitly invoke the __del__ method and close the session. """ asyncio.run(self.aclose_session()) From d8780f9fd27c104959ebed0973207e30b8dc6020 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:20:55 +0000 Subject: [PATCH 11/15] fix axon examples --- bittensor/axon.py | 50 +++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/bittensor/axon.py b/bittensor/axon.py index a8e053fa76..0395b70070 100644 --- a/bittensor/axon.py +++ b/bittensor/axon.py @@ -172,17 +172,12 @@ class is designed to be flexible and customizable, allowing users to specify cus includes internal mechanisms to manage a thread pool, supporting concurrent handling of requests with defined priority levels. - Methods in this class are equipped to deal with incoming requests from other scenarios in the + Methods in this class are equipped to deal with incoming requests from various scenarios in the network and serve as the server face for a neuron. It accepts multiple arguments, like wallet, configuration parameters, ip address, server binding port, external ip, external port and max workers. Key methods involve managing and operating the FastAPI application router, including the attachment and operation of endpoints. - The `axon` class offers flexibility to specify custom rules to forward, blacklist, prioritize and - verify incoming requests against custom functions. The class also encapsulates methods to add - command-line arguments for user-friendly interaction with the program, and supports the handling - of these arguments, to define the behavior of the axon object. - Key Features: - FastAPI router integration for endpoint creation and management. - Customizable request handling including forwarding, blacklisting, and prioritization. @@ -195,11 +190,12 @@ class is designed to be flexible and customizable, allowing users to specify cus ```python import bittensor + # Define your custom synapse class class MySyanpse( bittensor.Synapse ): input: int = 1 output: int = None - # Define a custom request forwarding function + # Define a custom request forwarding function using your synapse class def forward( synapse: MySyanpse ) -> MySyanpse: # Apply custom logic to synapse and return it synapse.output = 2 @@ -209,11 +205,13 @@ def forward( synapse: MySyanpse ) -> MySyanpse: def verify_my_synapse( synapse: MySyanpse ): # Apply custom verification logic to synapse # Optionally raise Exception + assert synapse.input == 1 + ... # Define a custom request blacklist fucntion def blacklist_my_synapse( synapse: MySyanpse ) -> bool: # Apply custom blacklist - # return False ( if non blacklisted ) or True ( if blacklisted ) + return False ( if non blacklisted ) or True ( if blacklisted ) # Define a custom request priority fucntion def prioritize_my_synape( synapse: MySyanpse ) -> float: @@ -230,7 +228,21 @@ def prioritize_my_synape( synapse: MySyanpse ) -> float: external_port=7070 ) - # Attach the endpoint with the specified verification and forwarding functions + # Attach the endpoint with the specified verification and forward functions. + my_axon.attach( + forward_fn = forward_my_synapse, + verify_fn = verify_my_synapse, + blacklist_fn = blacklist_my_synapse, + priority_fn = prioritize_my_synape + ) + + # Serve and start your axon. + my_axon.serve( + netuid = ... + subtensor = ... + ).start() + + # If you have multiple forwarding functions, you can chain attach them. my_axon.attach( forward_fn = forward_my_synapse, verify_fn = verify_my_synapse, @@ -265,7 +277,7 @@ def prioritize_my_synape( synapse: MySyanpse ) -> float: Importance and Functionality Endpoint Registration: This method dynamically registers API endpoints based on the Synapse class - used, allowing the Axon to respond to specific types of requests. + used, allowing the Axon to respond to specific types of requests and synapses. Customization of Request Handling: By attaching different functions, the Axon can customize how it handles, verifies, prioritizes, and potentially blocks incoming requests, making it adaptable @@ -452,6 +464,12 @@ def forward_custom(synapse: MyCustomSynapse) -> MyCustomSynapse: # Custom logic for processing the request return synapse + def blacklist_custom(synapse: MyCustomSynapse) -> Tuple[bool, str]: + return True, "Allowed!" + + def priority_custom(synapse: MyCustomSynapse) -> float: + return 1.0 + def verify_custom(synapse: MyCustomSynapse): # Custom logic for verifying the request pass @@ -686,14 +704,6 @@ async def verify_body_integrity(self, request: Request): 4. Reconstructing the Synapse object and recomputing the hash for verification and logging. 5. Comparing the recomputed hash with the hash provided in the request headers for verification. - Example: - Assuming this method is set as a dependency in a route: - - @app.post("/some_endpoint") - async def some_endpoint(body_dict: dict = Depends(verify_body_integrity)): - # body_dict is the parsed body of the request and is available for use in the route function. - # The function only executes if the body integrity verification is successful. - ... Note: The integrity verification is an essential step in ensuring the security of the data exchange within the Bittensor network. It helps prevent tampering and manipulation of data during transit, @@ -790,6 +800,7 @@ def start(self) -> "bittensor.axon": Example: ```python my_axon = bittensor.axon(...) + ... # setup axon, attach functions, etc. my_axon.start() # Starts the axon server ``` @@ -851,7 +862,8 @@ def serve( Example: ```python my_axon = bittensor.axon(...) - my_axon.serve(netuid=12345) # Serves the axon on subnet with netuid 12345 + subtensor = bt.subtensor(network="local") # Local by default + my_axon.serve(netuid=1, subtensor=subtensor) # Serves the axon on subnet with netuid 1 ``` Note: From 7100c8b512bd7666d1d1f47eafe7e1660ad8e376 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:25:09 +0000 Subject: [PATCH 12/15] metagraph touches --- bittensor/metagraph.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bittensor/metagraph.py b/bittensor/metagraph.py index 8b308027b5..3adca1f112 100644 --- a/bittensor/metagraph.py +++ b/bittensor/metagraph.py @@ -74,7 +74,7 @@ class metagraph(torch.nn.Module): that forms the backbone of the decentralized machine learning system. It is a dynamic representation of the network's state, capturing the interconnectedness and attributes of neurons (participants) in the Bittensor ecosystem. This class is not just a static structure but a live reflection of the - network, constantly updated and synchronized with the state of the blockchain. + network, and can should be constantly synchronized with the state of the blockchain. In Bittensor, neurons are akin to nodes in a distributed system, each contributing computational resources and participating in the network's collective intelligence. The metagraph tracks various @@ -276,7 +276,7 @@ def W(self) -> torch.FloatTensor: for setting its weights, which are then recorded on a digital ledger. These weights are reflective of the neuron's assessment or judgment of other neurons in the network. - The weight matrix W = [wij] is a key component of the network's architecture, where the ith row is set by + The weight matrix W = [w_ij] is a key component of the network's architecture, where the ith row is set by neuron i and represents its weights towards other neurons. These weights influence the ranking and incentive mechanisms within the network. Higher weights from a neuron towards another can imply greater trust or value placed on that neuron's contributions. @@ -515,6 +515,9 @@ def sync( metagraph: The metagraph instance, updated to the state of the specified block or the latest network state. Example: + # Setup subtensor (ideally local) to sync the metagraph with the latest block from the subtensor. + subtensor = bittensor.subtensor(network='local') + # Sync the metagraph with the latest block from the subtensor, using the lite version for efficiency. metagraph.sync(subtensor=subtensor) From 8c39d462f77780f8342de8de843d69d4084e5499 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:32:58 +0000 Subject: [PATCH 13/15] fix synapse ex --- bittensor/synapse.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/bittensor/synapse.py b/bittensor/synapse.py index 5557d9f749..f5f3a75b8a 100644 --- a/bittensor/synapse.py +++ b/bittensor/synapse.py @@ -308,18 +308,32 @@ class Synapse(pydantic.BaseModel): # Creating a Synapse instance with default values synapse = Synapse() - # Setting properties + # Setting properties and input synapse.timeout = 15.0 synapse.name = "MySynapse" + # Not setting fields that are not defined in your synapse class will result in an error, e.g.: + synapse.dummy_input = 1 # This will raise an error because dummy_input is not defined in the Synapse class - # Serializing and deserializing a Synapse instance - serialized_synapse = synapse.json() - deserialized_synapse = Synapse.parse_raw(serialized_synapse) + # Get a dictionary of headers and body from the synapse instance + synapse_dict = synapse.json() - # Checking the status of the dendrite + # Get a dictionary of headers from the synapse instance + headers = synapse.to_headers() + + # Reconstruct the synapse from headers using the classmethod 'from_headers' + synapse = Synapse.from_headers(headers) + + # Deserialize synapse after receiving it over the network, controlled by `deserialize` method + deserialized_synapse = synapse.deserialize() + + # Checking the status of the request if synapse.is_success: print("Request succeeded") + # Checking and setting the status of the request + print(synapse.axon.status_code) + synapse.axon.status_code = 408 # Timeout + Attributes: name (str): HTTP route name, set on axon.attach. timeout (float): Total query length, set by the dendrite terminal. @@ -700,6 +714,9 @@ def parse_headers_to_inputs(cls, headers: dict) -> dict: inputs = Synapse.parse_headers_to_inputs(received_headers) # inputs now contains a structured representation of Synapse properties based on the headers + Note: This is handled automatically when calling Synapse.from_headers(headers) and does not + need to be called directly. + Args: headers (dict): The headers dictionary to parse. From 7fde3eb7f3f6bf72c04ef41dbee81d6b246dd8d0 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:35:14 +0000 Subject: [PATCH 14/15] wallet touchup --- bittensor/wallet.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bittensor/wallet.py b/bittensor/wallet.py index d78405a01e..1a770bf230 100644 --- a/bittensor/wallet.py +++ b/bittensor/wallet.py @@ -94,11 +94,17 @@ class wallet: hotkey = my_wallet.get_hotkey() coldkey = my_wallet.get_coldkey() + # Set a new coldkey + my_wallet.new_coldkey(n_words=24) # number of seed words to use + # Update wallet hotkey my_wallet.set_hotkey(new_hotkey) # Print wallet details print(my_wallet) + + # Access coldkey property, must use password to unlock + my_wallet.coldkey """ @classmethod From f7fe31603ba0a702d64cac35e5af372cb273070f Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Wed, 29 Nov 2023 20:51:23 +0000 Subject: [PATCH 15/15] subtensor facuet fix --- bittensor/subtensor.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index e1e398133f..c658d9475e 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -129,6 +129,9 @@ class subtensor: # Speculate by accumulating bonds in other promising neurons. success = finney_subtensor.delegate(wallet=wallet, delegate_ss58=other_neuron_ss58, amount=bond_amount) + # Get the metagraph for a specific subnet using given subtensor connection + metagraph = subtensor.metagraph(netuid=netuid) + By facilitating these operations, the Subtensor class is instrumental in maintaining the decentralized intelligence and dynamic learning environment of the Bittensor network, as envisioned in its foundational principles and mechanisms described in the NeurIPS paper. @@ -684,7 +687,7 @@ def run_faucet( """ Facilitates a faucet transaction, allowing new neurons to receive an initial amount of TAO for participating in the network. This function is particularly useful for newcomers to the - Bittensor network, enabling them to start with a small stake. + Bittensor network, enabling them to start with a small stake on testnet only. Args: wallet (bittensor.wallet): The wallet for which the faucet transaction is to be run. @@ -695,6 +698,9 @@ def run_faucet( This function is part of Bittensor's onboarding process, ensuring that new neurons have the necessary resources to begin their journey in the decentralized AI network. + + Note: This is for testnet ONLY and is disabled currently. You must build your own + staging subtensor chain with the `--features pow-faucet` argument to enable this. """ return run_faucet_extrinsic( subtensor=self,