From 124d9d2e4fff0341f1afd9b64d3fa8032a11d557 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Tue, 12 Aug 2025 20:11:49 +0100 Subject: [PATCH 1/6] validator logic updates --- README.md | 47 +++++++++++++++++++---------------------- validator.py | 59 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index a0cc4d7..f3606a6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Bittensor Subnet Template This repository provides a minimal template for setting up a simple Bittensor subnet with a miner and a validator. The miner and validator communicate using a custom protocol defined in `protocol.py`. This template serves as a starting point for developers interested in building on the Bittensor network. @@ -62,8 +61,8 @@ Before you begin, ensure you have the following installed: Clone this repository to your local machine: ```bash -git clone https://github.com/yourusername/bittensor_subnet.git -cd bittensor_subnet +git clone https://github.com/opentensor/subnet-template.git +cd subnet-template ``` ### 2. Install Dependencies @@ -111,61 +110,57 @@ Register both the miner and validator on the Bittensor network. - **Register the Miner**: ```bash - btcli s register --wallet.name mywallet --wallet.hotkey miner_hotkey --subtensor.network finney + btcli s register --wallet.name mywallet --wallet.hotkey miner_hotkey --subtensor.network NETWORK ``` - **Register the Validator**: ```bash - btcli s register --wallet.name mywallet --wallet.hotkey validator_hotkey --subtensor.network finney + btcli s register --wallet.name mywallet --wallet.hotkey validator_hotkey --subtensor.network NETWORK ``` -> **Note**: Replace `finney` with the name of the network you are connecting to if different. +> **Note**: Replace `NETWORK` with the name of the network you are connecting to if different. --- ## Running the Miner and Validator -### Running the Miner +### Start the miner process -In one terminal window, navigate to the project directory and run: +To start the miner, run the following Python script in the `subnet-template` directory: -```bash -python miner.py --wallet.name mywallet --wallet.hotkey miner_hotkey --subtensor.network finney --axon.port 8901 +```sh +python miner.py --wallet.name WALLET_NAME --wallet.hotkey HOTKEY --netuid NETUID --axon.port 8901 --subtensor.network NETWORK ``` -**Arguments**: - -- `--wallet.name`: The name of the wallet. -- `--wallet.hotkey`: The hotkey name for the miner. -- `--subtensor.network`: The Bittensor network to connect to. +The script launches an Axon server on port `8901`, which the miner uses to receive incoming requests from validators. -### Running the Validator +### Start the validator process -In another terminal window, navigate to the project directory and run: +To start the validator process, run the following Python script in the `subnet-template` directory: -```bash -python validator.py --wallet.name mywallet --wallet.hotkey validator_hotkey --subtensor.network finney +```sh +python validator.py --wallet.name WALLET_NAME --wallet.hotkey HOTKEY --netuid NETUID --subtensor.network NETWORK ``` +This script begins the process of sending inputs to the miners and setting weights based on miner responses. + **Arguments**: - `--wallet.name`: The name of the wallet. - `--wallet.hotkey`: The hotkey name for the validator. +- `--netuid`: The uid of the subnet in the network. - `--subtensor.network`: The Bittensor network to connect to. --- ## Monitoring and Logging -Both the miner and validator will output logs to the console and save logs to files in the following directory structure: +Use the `--logging.info` flag to print miner and validator log messages directly to the console. This helps you monitor activity in real time. For example +```sh +python validator.py --wallet.name WALLET_NAME --wallet.hotkey HOTKEY --netuid NETUID --subtensor.network NETWORK --logging.info ``` -~/.bittensor/wallets///netuid// -``` - -- **Miner Logs**: Located in the `miner` directory. -- **Validator Logs**: Located in the `validator` directory. You can monitor these logs to observe the interactions and performance metrics. @@ -203,7 +198,7 @@ else: ### Changing Network Parameters -You can adjust network parameters like `netuid`, timeouts, and other settings via command-line arguments or by modifying the code. +You can adjust network parameters like `netuid`, timeouts, and other settings via command-line arguments or by modifying the code in the `miner.py` and `validator.py`. --- diff --git a/validator.py b/validator.py index ef3bb36..a272860 100644 --- a/validator.py +++ b/validator.py @@ -14,12 +14,12 @@ def __init__(self): self.setup_logging() self.setup_bittensor_objects() self.my_uid = self.metagraph.hotkeys.index(self.wallet.hotkey.ss58_address) - self.scores = [1.0] * len(self.metagraph.S) + self.scores = [0] * len(self.metagraph.S) self.last_update = self.subtensor.blocks_since_last_update( self.config.netuid, self.my_uid ) self.tempo = self.subtensor.tempo(self.config.netuid) - self.moving_avg_scores = [1.0] * len(self.metagraph.S) + self.moving_avg_scores = [0] * len(self.metagraph.S) self.alpha = 0.1 def get_config(self): @@ -99,7 +99,7 @@ def setup_bittensor_objects(self): # Set up initial scoring weights for validation. bt.logging.info("Building validation weights.") - self.scores = [1.0] * len(self.metagraph.S) + self.scores = [0] * len(self.metagraph.S) bt.logging.info(f"Weights: {self.scores}") def run(self): @@ -116,28 +116,33 @@ def run(self): axons=self.metagraph.axons, synapse=synapse, timeout=12 ) bt.logging.info(f"sending input {synapse.dummy_input}") - if responses: - responses = [ - response.dummy_output - for response in responses - if response is not None - ] - + # Log the results. - bt.logging.info(f"Received dummy responses: {responses}") - - # Adjust the length of moving_avg_scores to match the number of responses - if len(self.moving_avg_scores) < len(responses): - self.moving_avg_scores.extend( - [1] * (len(responses) - len(self.moving_avg_scores)) - ) - - # Adjust the scores based on responses from miners and update moving average. - for i, resp_i in enumerate(responses): - current_score = 1 if resp_i == synapse.dummy_input * 2 else 0 - self.moving_avg_scores[i] = ( - 1 - self.alpha - ) * self.moving_avg_scores[i] + self.alpha * current_score + bt.logging.info(f"Received responses: {responses}") + + # Filter successful responses for logging + successful_responses = [ + response.dummy_output + for response in responses + if response is not None + ] + bt.logging.info(f"Successful responses: {successful_responses}") + + # Score all miners based on their responses + for i, response in enumerate(responses): + if response is not None: + # Miner responded - score based on correctness + current_score = 1 if response.dummy_output == synapse.dummy_input * 2 else 0 + self.moving_avg_scores[i] = ( + (1 - self.alpha) * self.moving_avg_scores[i] + + self.alpha * current_score + ) + else: + # Miner didn't respond - set score to 0 + self.moving_avg_scores[i] = ( + (1 - self.alpha) * self.moving_avg_scores[i] + + self.alpha * 0 + ) bt.logging.info(f"Moving Average Scores: {self.moving_avg_scores}") self.last_update = self.subtensor.blocks_since_last_update( @@ -146,7 +151,11 @@ def run(self): # set weights once every tempo total = sum(self.moving_avg_scores) - weights = [score / total for score in self.moving_avg_scores] + if total > 0: + weights = [score / total for score in self.moving_avg_scores] + else: + # If no miners responded, set zero weights + weights = [0.0] * len(self.moving_avg_scores) bt.logging.info(f"[blue]Setting weights: {weights}[/blue]") # Update the incentive mechanism on the Bittensor blockchain. self.subtensor.set_weights( From efc26f070e55b57fc34c6f9b9245caacfad4fc08 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Wed, 13 Aug 2025 22:23:07 +0100 Subject: [PATCH 2/6] minor fixes --- validator.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/validator.py b/validator.py index a272860..5d1f0b6 100644 --- a/validator.py +++ b/validator.py @@ -100,7 +100,8 @@ def setup_bittensor_objects(self): # Set up initial scoring weights for validation. bt.logging.info("Building validation weights.") self.scores = [0] * len(self.metagraph.S) - bt.logging.info(f"Weights: {self.scores}") + weights_with_uids = [(int(self.metagraph.uids[i]), score) for i, score in enumerate(self.scores)] + bt.logging.info(f"Weights (uid, weight): {weights_with_uids}") def run(self): # The Main Validation Loop. @@ -120,13 +121,12 @@ def run(self): # Log the results. bt.logging.info(f"Received responses: {responses}") - # Filter successful responses for logging - successful_responses = [ - response.dummy_output - for response in responses - if response is not None - ] - bt.logging.info(f"Successful responses: {successful_responses}") + # Filter successful responses for logging with UIDs + successful_responses_with_uids = [] + for i, response in enumerate(responses): + if response is not None: + successful_responses_with_uids.append((int(self.metagraph.uids[i]), response.dummy_output)) + bt.logging.info(f"Successful responses (uid, response): {successful_responses_with_uids}") # Score all miners based on their responses for i, response in enumerate(responses): @@ -144,7 +144,9 @@ def run(self): self.alpha * 0 ) - bt.logging.info(f"Moving Average Scores: {self.moving_avg_scores}") + # Create list of (uid, score) tuples + scores_with_uids = [(int(self.metagraph.uids[i]), score) for i, score in enumerate(self.moving_avg_scores)] + bt.logging.info(f"Moving Average Scores (uid, score): {scores_with_uids}") self.last_update = self.subtensor.blocks_since_last_update( self.config.netuid, self.my_uid ) @@ -156,7 +158,9 @@ def run(self): else: # If no miners responded, set zero weights weights = [0.0] * len(self.moving_avg_scores) - bt.logging.info(f"[blue]Setting weights: {weights}[/blue]") + # Create list of (uid, weight) tuples + weights_with_uids = [(int(self.metagraph.uids[i]), weight) for i, weight in enumerate(weights)] + bt.logging.info(f"[blue]Setting weights (uid, weight): {weights_with_uids}[/blue]") # Update the incentive mechanism on the Bittensor blockchain. self.subtensor.set_weights( netuid=self.config.netuid, From 257985760f19d5a3b790f6a817d205a893424577 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Wed, 13 Aug 2025 22:38:17 +0100 Subject: [PATCH 3/6] minor fixes --- validator.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/validator.py b/validator.py index 5d1f0b6..ca5d4b0 100644 --- a/validator.py +++ b/validator.py @@ -118,8 +118,23 @@ def run(self): ) bt.logging.info(f"sending input {synapse.dummy_input}") - # Log the results. - bt.logging.info(f"Received responses: {responses}") + # Log the results with UIDs showing input and output + responses_with_uids = [] + for i, response in enumerate(responses): + uid = int(self.metagraph.uids[i]) + if response is not None and response.dummy_output is not None: + responses_with_uids.append({ + 'uid': uid, + 'input': response.dummy_input, + 'output': response.dummy_output + }) + else: + responses_with_uids.append({ + 'uid': uid, + 'input': synapse.dummy_input, + 'output': None + }) + bt.logging.info(f"Received responses: {responses_with_uids}") # Filter successful responses for logging with UIDs successful_responses_with_uids = [] From 1d9b9e3e7ffd3a236a1ddc0603cf1bbc6400c357 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Wed, 13 Aug 2025 22:47:07 +0100 Subject: [PATCH 4/6] updated readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3606a6..b93ca46 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,15 @@ Before you begin, ensure you have the following installed: ## Setup Instructions -### 1. Clone the Repository +### 1. Fork and clone the Repository -Clone this repository to your local machine: +Fork the [subnet template](https://github.com/opentensor/subnet-template) repository to create a copy of the repository under your GitHub account. + +Next, clone this repository to your local machine and change directory ask shown: ```bash -git clone https://github.com/opentensor/subnet-template.git -cd subnet-template +git clone https://github.com/YOUR_USERNAME/subnet_template.git +cd subnet_template ``` ### 2. Install Dependencies From 82c06544d6af0ff5a67318ceaa90d1ec6f77868a Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Tue, 20 Jan 2026 20:48:57 +0100 Subject: [PATCH 5/6] Resolve merge conflicts in validator.py --- validator.py | 130 +++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/validator.py b/validator.py index ca5d4b0..5c2182f 100644 --- a/validator.py +++ b/validator.py @@ -1,9 +1,11 @@ +import argparse import os -import time import random -import argparse +import time import traceback -import bittensor as bt + +from bittensor import Subtensor, Wallet, Config, Dendrite +from bittensor.utils.btlogging import logging from protocol import Dummy @@ -36,19 +38,19 @@ def get_config(self): "--netuid", type=int, default=1, help="The chain subnet uid." ) # Adds subtensor specific arguments. - bt.subtensor.add_args(parser) + Subtensor.add_args(parser) # Adds logging specific arguments. - bt.logging.add_args(parser) + logging.add_args(parser) # Adds wallet specific arguments. - bt.wallet.add_args(parser) + Wallet.add_args(parser) # Parse the config. - config = bt.config(parser) + config = Config(parser) # Set up logging directory. config.full_path = os.path.expanduser( "{}/{}/{}/netuid{}/validator".format( config.logging.logging_dir, config.wallet.name, - config.wallet.hotkey_str, + config.wallet.hotkey, config.netuid, ) ) @@ -58,35 +60,35 @@ def get_config(self): def setup_logging(self): # Set up logging. - bt.logging(config=self.config, logging_dir=self.config.full_path) - bt.logging.info( + logging(config=self.config, logging_dir=self.config.full_path) + logging.info( f"Running validator for subnet: {self.config.netuid} on network: {self.config.subtensor.network} with config:" ) - bt.logging.info(self.config) + logging.info(self.config) def setup_bittensor_objects(self): # Build Bittensor validator objects. - bt.logging.info("Setting up Bittensor objects.") + logging.info("Setting up Bittensor objects.") # Initialize wallet. - self.wallet = bt.wallet(config=self.config) - bt.logging.info(f"Wallet: {self.wallet}") + self.wallet = Wallet(config=self.config) + logging.info(f"Wallet: {self.wallet}") # Initialize subtensor. - self.subtensor = bt.subtensor(config=self.config) - bt.logging.info(f"Subtensor: {self.subtensor}") + self.subtensor = Subtensor(config=self.config) + logging.info(f"Subtensor: {self.subtensor}") # Initialize dendrite. - self.dendrite = bt.dendrite(wallet=self.wallet) - bt.logging.info(f"Dendrite: {self.dendrite}") + self.dendrite = Dendrite(wallet=self.wallet) + logging.info(f"Dendrite: {self.dendrite}") # Initialize metagraph. - self.metagraph = self.subtensor.metagraph(self.config.netuid) - bt.logging.info(f"Metagraph: {self.metagraph}") + self.metagraph = self.subtensor.metagraph(netuid=self.config.netuid) + logging.info(f"Metagraph: {self.metagraph}") # Connect the validator to the network. if self.wallet.hotkey.ss58_address not in self.metagraph.hotkeys: - bt.logging.error( + logging.error( f"Your validator: {self.wallet} is not registered to chain connection: {self.subtensor} \nRun 'btcli register' and try again." ) exit() @@ -95,17 +97,17 @@ def setup_bittensor_objects(self): self.my_subnet_uid = self.metagraph.hotkeys.index( self.wallet.hotkey.ss58_address ) - bt.logging.info(f"Running validator on uid: {self.my_subnet_uid}") + logging.info(f"Running validator on uid: {self.my_subnet_uid}") # Set up initial scoring weights for validation. - bt.logging.info("Building validation weights.") + logging.info("Building validation weights.") self.scores = [0] * len(self.metagraph.S) weights_with_uids = [(int(self.metagraph.uids[i]), score) for i, score in enumerate(self.scores)] - bt.logging.info(f"Weights (uid, weight): {weights_with_uids}") + logging.info(f"Weights (uid, weight): {weights_with_uids}") def run(self): # The Main Validation Loop. - bt.logging.info("Starting validator loop.") + logging.info("Starting validator loop.") while True: try: # time.sleep(int(self.subtensor.tempo(self.config.netuid) * 0.25)) @@ -116,7 +118,7 @@ def run(self): responses = self.dendrite.query( axons=self.metagraph.axons, synapse=synapse, timeout=12 ) - bt.logging.info(f"sending input {synapse.dummy_input}") + logging.info(f"sending input {synapse.dummy_input}") # Log the results with UIDs showing input and output responses_with_uids = [] @@ -134,34 +136,40 @@ def run(self): 'input': synapse.dummy_input, 'output': None }) - bt.logging.info(f"Received responses: {responses_with_uids}") - - # Filter successful responses for logging with UIDs - successful_responses_with_uids = [] - for i, response in enumerate(responses): - if response is not None: - successful_responses_with_uids.append((int(self.metagraph.uids[i]), response.dummy_output)) - bt.logging.info(f"Successful responses (uid, response): {successful_responses_with_uids}") - - # Score all miners based on their responses - for i, response in enumerate(responses): - if response is not None: - # Miner responded - score based on correctness + logging.info(f"Received responses: {responses_with_uids}") + + # Store original responses for scoring before filtering + original_responses = responses + if responses: + responses = [ + response.dummy_output + for response in responses + if response is not None + ] + + # Log the results. + logging.info(f"Received dummy responses: {responses}") + + # Adjust the length of moving_avg_scores to match the number of miners + if len(self.moving_avg_scores) < len(self.metagraph.S): + self.moving_avg_scores.extend( + [0] * (len(self.metagraph.S) - len(self.moving_avg_scores)) + ) + + # Adjust the scores based on responses from miners and update moving average. + # Process by original miner index to maintain UID mapping + for i, response in enumerate(original_responses): + if response is not None and response.dummy_output is not None: current_score = 1 if response.dummy_output == synapse.dummy_input * 2 else 0 - self.moving_avg_scores[i] = ( - (1 - self.alpha) * self.moving_avg_scores[i] + - self.alpha * current_score - ) else: - # Miner didn't respond - set score to 0 - self.moving_avg_scores[i] = ( - (1 - self.alpha) * self.moving_avg_scores[i] + - self.alpha * 0 - ) + current_score = 0 + self.moving_avg_scores[i] = ( + 1 - self.alpha + ) * self.moving_avg_scores[i] + self.alpha * current_score # Create list of (uid, score) tuples scores_with_uids = [(int(self.metagraph.uids[i]), score) for i, score in enumerate(self.moving_avg_scores)] - bt.logging.info(f"Moving Average Scores (uid, score): {scores_with_uids}") + logging.info(f"Moving Average Scores (uid, score): {scores_with_uids}") self.last_update = self.subtensor.blocks_since_last_update( self.config.netuid, self.my_uid ) @@ -175,26 +183,36 @@ def run(self): weights = [0.0] * len(self.moving_avg_scores) # Create list of (uid, weight) tuples weights_with_uids = [(int(self.metagraph.uids[i]), weight) for i, weight in enumerate(weights)] - bt.logging.info(f"[blue]Setting weights (uid, weight): {weights_with_uids}[/blue]") + logging.info(f"[blue]Setting weights (uid, weight): {weights_with_uids}[/blue]") # Update the incentive mechanism on the Bittensor blockchain. - self.subtensor.set_weights( - netuid=self.config.netuid, + response = self.subtensor.set_weights( wallet=self.wallet, + netuid=self.config.netuid, uids=self.metagraph.uids, weights=weights, wait_for_inclusion=True, - period=self.tempo # Good for fast blocks - otherwise make sure to set proper period or remove this argument completely + period=self.tempo, # Good for fast blocks - otherwise make sure to set proper period or remove this argument completely ) + if response.success: + logging.success( + f"Weights set successfully. Fee: {response.extrinsic_fee}" + ) + else: + logging.error( + f"Failed to set weights: {response.error} - {response.message}" + ) self.metagraph.sync() # sleep until next tempo - time.sleep((((self.subtensor.block // self.tempo) + 1) * self.tempo) + 1) + time.sleep( + (((self.subtensor.block // self.tempo) + 1) * self.tempo) + 1 + ) except RuntimeError as e: - bt.logging.error(e) + logging.error(e) traceback.print_exc() except KeyboardInterrupt: - bt.logging.success("Keyboard interrupt detected. Exiting validator.") + logging.success("Keyboard interrupt detected. Exiting validator.") exit() From efb20f2c8bc51b2c81a7a49741b07e6f44ac8823 Mon Sep 17 00:00:00 2001 From: Dera Okeke Date: Wed, 21 Jan 2026 18:08:20 +0100 Subject: [PATCH 6/6] updated readme --- .gitignore | 4 +++- README.md | 23 +++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index ba0430d..0630458 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -__pycache__/ \ No newline at end of file +__pycache__/ +venv +.venv \ No newline at end of file diff --git a/README.md b/README.md index b93ca46..b32116f 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,10 @@ Before you begin, ensure you have the following installed: - Python 3.10 or higher - [Git](https://git-scm.com/) -- [Bittensor SDK](https://github.com/opentensor/bittensor) +- [Bittensor SDK](https://github.com/opentensor/bittensor) (version 10 or higher) +- An active subnet on Bittensor testnet or local chain instance. For more information, see [create a new subnet](https://docs.learnbittensor.org/subnets/create-a-subnet#creating-a-subnet-on-testchain) + +> **Note**: To create a local blockchain instance, see [Run a Local Bittensor Blockchain Instance](https://docs.learnbittensor.org/local-build/deploy#prerequisites). ## Setup Instructions @@ -92,7 +95,6 @@ The `btcli` tool is used to manage wallets and keys. ``` 2. **Create Hotkeys**: - - **Miner Hotkey**: ```bash @@ -107,21 +109,26 @@ The `btcli` tool is used to manage wallets and keys. ### 4. Register Wallets -Register both the miner and validator on the Bittensor network. +Register both the miner and validator on the active Bittensor subnet. + +> **Note**: Ensure your miner and validator wallets are sufficiently funded before attempting subnet registration. +> +> - For local development, transfer funds from the Alice account. +> - For testnet development, you can request testnet TAO from the [Bittensor Discord](https://discord.com/channels/799672011265015819/1107738550373454028/threads/1331693251589312553). - **Register the Miner**: ```bash - btcli s register --wallet.name mywallet --wallet.hotkey miner_hotkey --subtensor.network NETWORK + btcli s register --wallet.name mywallet --wallet.hotkey miner_hotkey --subtensor.network NETWORK --netuid NETUID ``` - **Register the Validator**: ```bash - btcli s register --wallet.name mywallet --wallet.hotkey validator_hotkey --subtensor.network NETWORK + btcli s register --wallet.name mywallet --wallet.hotkey validator_hotkey --subtensor.network NETWORK --netuid NETUID ``` -> **Note**: Replace `NETWORK` with the name of the network you are connecting to if different. +> **Note**: Replace `NETWORK` with the name of the network you are connecting to if different—`local` or `test`. --- @@ -135,6 +142,8 @@ To start the miner, run the following Python script in the `subnet-template` dir python miner.py --wallet.name WALLET_NAME --wallet.hotkey HOTKEY --netuid NETUID --axon.port 8901 --subtensor.network NETWORK ``` +> **Note**: Run the `miner.py` script in a Python environment with the Bittensor SDK installed. + The script launches an Axon server on port `8901`, which the miner uses to receive incoming requests from validators. ### Start the validator process @@ -145,6 +154,8 @@ To start the validator process, run the following Python script in the `subnet-t python validator.py --wallet.name WALLET_NAME --wallet.hotkey HOTKEY --netuid NETUID --subtensor.network NETWORK ``` +> **Note**: Run the `validator.py` script in a Python environment with the Bittensor SDK installed. + This script begins the process of sending inputs to the miners and setting weights based on miner responses. **Arguments**: