diff --git a/Makefile b/Makefile index f36e0e3..802eb95 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ ifeq ($(NETWORK),localnet) else ifeq ($(NETWORK),testnet) netuid = 256 else ifeq ($(NETWORK),finney) - #netuid = 64 + netuid = 55 $(error Finney network not supported yet) endif diff --git a/README.md b/README.md index f379af8..c2c384c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ # **CoinMetrics Precog Subnet** +
+ +| **Testnet UID:** 256
**Mainnet UID:** 55 | +| - | + +
+ +
+ | | | | :-: | :-: | | **Status** |

| @@ -35,6 +44,29 @@ The incentive mechanism was specifically designed to reward precise statements o | 2 vCPUs | 2 vCPUs | --- + +## Prerequisites + +Before beginning, ensure you have: + +1. **Python Installation** + - Python version 3.9, 3.10, or 3.11 installed + - We recommend using `pyenv` for Python version management + +2. **Bittensor Knowledge** + - Understanding of the Bittensor ecosystem and wallet management + - Familiarity with creating and managing Bittensor wallets + - Review the [Bittensor Wallet Documentation](https://docs.bittensor.com/getting-started/wallets) + - For general understanding, see the [Bittensor Documentation](https://docs.bittensor.com/) + +3. **Poetry** + - Basic understanding of Poetry for dependency management + - See the [Poetry Documentation](https://python-poetry.org/docs/) for installation and usage + +4. **System Requirements** + - NodeJS and NPM (for PM2 installation) + - Basic understanding of command-line interfaces + ## Installation First, install PM2: @@ -70,10 +102,8 @@ poetry install --- ## Configuration -### Makefile - ### .env Files -Copy the example `.env` files and edit all desired values: +Copy the example `.env` files and edit all desired values. If you are running a validator, you will only need to copy the .env.validator file. If you are running a miner, you will only need to copy the .env.miner file: #### .env.validator ``` @@ -81,18 +111,61 @@ cp .env.validator.example .env.validator ``` Edit `.env.validator` with your desired values. +``` +# Network Configuration +# Options: localnet, testnet, finney +NETWORK=testnet + +# Wallet Configuration +COLDKEY=your_validator_coldkey +VALIDATOR_HOTKEY=your_validator_hotkey + +# Node Configuration +VALIDATOR_NAME=validator +VALIDATOR_PORT=8091 + +# Logging +# Options: info, debug, trace +LOGGING_LEVEL=debug +``` + #### .env.miner ``` cp .env.miner.example .env.miner ``` Edit `.env.miner` with your desired values. -### Wandb -Wandb integration is planned for mainnet launch and does not currently work. +```# Network Configuration +# Options: localnet, testnet, finney +NETWORK=testnet + +# Wallet Configuration +COLDKEY=your_miner_coldkey +MINER_HOTKEY=your_miner_hotkey + +# Node Configuration +MINER_NAME=miner +# This port must be open to accept incoming TCP connections. +MINER_PORT=8092 + +# Miner Settings +TIMEOUT=16 +VPERMIT_TAO_LIMIT=2 + +#Adjust this function if you would like to specify a custom forward function +FORWARD_FUNCTION=base_miner + +# Logging +# Options: info, debug, trace +LOGGING_LEVEL=debug +``` --- ## Deployment +### Registering a Hotkey +Once you have configured your .env files as per the instructions above, you can register a miner with `make register ENV_FILE=.env.miner` or register a validator with `make register ENV_FILE=.env.validator`. + ### Running a Miner Base miner: 1. Run the command: diff --git a/docs/Release Notes.md b/docs/Release Notes.md index ee8f021..43408dd 100644 --- a/docs/Release Notes.md +++ b/docs/Release Notes.md @@ -1,6 +1,11 @@ Release Notes ============= +1.0.0 +----- +Released on January 14th 2025 +- Release to mainnet + 0.3.0 ----- Released on January 7th 2025 diff --git a/precog/utils/bittensor.py b/precog/utils/bittensor.py index ce48803..b283c0f 100644 --- a/precog/utils/bittensor.py +++ b/precog/utils/bittensor.py @@ -31,32 +31,8 @@ def setup_bittensor_objects(self): self.metagraph = self.subtensor.metagraph(self.config.netuid) self.wallet = bt.wallet(config=self.config) self.dendrite = bt.dendrite(wallet=self.wallet) + self.axon = bt.axon(wallet=self.wallet, config=self.config, port=self.config.axon.port) - # Initialize axon config - axon_config = bt.axon.config() - axon_config.max_workers = self.config.axon.max_workers - axon_config.port = self.config.axon.port - axon_config.ip = self.config.axon.ip - axon_config.external_ip = self.config.axon.external_ip - axon_config.external_port = self.config.axon.external_port - self.config.axon = axon_config - - # Debug prints - bt.logging.debug(f"Axon config - port: {self.config.axon.port}") - bt.logging.debug(f"Axon config - ip: {self.config.axon.ip}") - bt.logging.debug(f"Axon config - external_ip: {self.config.axon.external_ip}") - bt.logging.debug(f"Axon config - external_port: {self.config.axon.external_port}") - bt.logging.debug(f"Axon config - max_workers: {self.config.axon.max_workers}") - - self.axon = bt.axon( - wallet=self.wallet, - config=self.config, - port=self.config.axon.port, - ip=self.config.axon.ip, - external_ip=self.config.axon.external_ip, - external_port=self.config.axon.external_port, - max_workers=self.config.axon.max_workers, - ) # Connect the validator to the network. if self.wallet.hotkey.ss58_address not in self.metagraph.hotkeys: bt.logging.error( diff --git a/precog/validators/weight_setter.py b/precog/validators/weight_setter.py index 8780f04..c8cc71c 100755 --- a/precog/validators/weight_setter.py +++ b/precog/validators/weight_setter.py @@ -89,26 +89,61 @@ async def get_available_uids(self): return miner_uids async def resync_metagraph(self): - """Resyncs the metagraph and updates the hotkeys and moving averages based on the new metagraph.""" + """Resyncs the metagraph and updates the hotkeys, available UIDs, and MinerHistory. + Ensures all data structures remain in sync.""" + # Resync subtensor and metagraph self.subtensor = bt.subtensor(config=self.config, network=self.config.subtensor.chain_endpoint) bt.logging.info("Syncing Metagraph...") self.metagraph.sync(subtensor=self.subtensor) bt.logging.info("Metagraph updated, re-syncing hotkeys, dendrite pool and moving averages") - # Zero out all hotkeys that have been replaced. + + # Get current state for logging + old_uids = set(self.available_uids) + old_history = set(self.MinerHistory.keys()) + bt.logging.debug(f"Before sync - Available UIDs: {old_uids}") + bt.logging.debug(f"Before sync - MinerHistory keys: {old_history}") + + # Update available UIDs self.available_uids = asyncio.run(self.get_available_uids()) + new_uids = set(self.available_uids) + + # Process hotkey changes for uid, hotkey in enumerate(self.metagraph.hotkeys): - new_miner = uid not in self.hotkeys - # replaced_miner will throw a key error if the uid is not in the hotkeys dict + new_miner = uid in new_uids and uid not in old_uids if not new_miner: replaced_miner = self.hotkeys[uid] != hotkey else: replaced_miner = False + if new_miner or replaced_miner: bt.logging.info(f"Replacing hotkey on {uid} with {self.metagraph.hotkeys[uid]}") - self.hotkeys = {uid: value for uid, value in enumerate(self.metagraph.hotkeys)} + self.moving_average_scores[uid] = 0 + if uid in new_uids: # Only create history for available UIDs + self.MinerHistory[uid] = MinerHistory(uid, timezone=self.timezone) + + # Update hotkeys dictionary + self.hotkeys = {uid: value for uid, value in enumerate(self.metagraph.hotkeys)} + + # Ensure all available UIDs have MinerHistory entries + for uid in self.available_uids: + if uid not in self.MinerHistory: + bt.logging.info(f"Creating new MinerHistory for available UID {uid}") self.MinerHistory[uid] = MinerHistory(uid, timezone=self.timezone) self.moving_average_scores[uid] = 0 - self.scores = list(self.moving_average_scores.values()) + + # Clean up old MinerHistory entries + for uid in list(self.MinerHistory.keys()): + if uid not in new_uids: + bt.logging.info(f"Removing MinerHistory for inactive UID {uid}") + del self.MinerHistory[uid] + + # Update scores list + self.scores = list(self.moving_average_scores.values()) + + bt.logging.debug(f"After sync - Available UIDs: {new_uids}") + bt.logging.debug(f"After sync - MinerHistory keys: {set(self.MinerHistory.keys())}") + + # Save updated state self.save_state() def query_miners(self): @@ -182,7 +217,13 @@ async def scheduled_prediction_request(self): if is_query_time(self.prediction_interval, self.timestamp) or query_lag >= 60 * self.prediction_interval: responses, self.timestamp = self.query_miners() try: + bt.logging.debug(f"Processing responses for UIDs: {self.available_uids}") + bt.logging.debug(f"Number of responses: {len(responses)}") + for uid, response in zip(self.available_uids, responses): + bt.logging.debug(f"Response from UID {uid}: {response}") + rewards = calc_rewards(self, responses=responses) + # Adjust the scores based on responses from miners and update moving average. for i, value in zip(self.available_uids, rewards): self.moving_average_scores[i] = ( @@ -192,7 +233,19 @@ async def scheduled_prediction_request(self): if not self.config.wandb.off: log_wandb(responses, rewards, self.available_uids) except Exception as e: - bt.logging.error(f"Failed to calculate rewards with error: {e}") + import traceback + + bt.logging.error(f"Failed to calculate rewards with error: {str(e)}") + bt.logging.error(f"Error type: {type(e)}") + bt.logging.error("Full traceback:") + bt.logging.error(traceback.format_exc()) + bt.logging.error(f"Available UIDs: {self.available_uids}") + bt.logging.error(f"Response count: {len(responses)}") + bt.logging.error(f"MinerHistory keys: {list(self.MinerHistory.keys())}") + bt.logging.error(f"Full MinerHistory: {self.MinerHistory}") + for uid, response in zip(self.available_uids, responses): + bt.logging.error(f"UID {uid} response status: {getattr(response, 'status_code', 'unknown')}") + bt.logging.error(f"UID {uid} response type: {type(response)}") else: print_info(self) diff --git a/pyproject.toml b/pyproject.toml index 4d70659..79819cd 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "precog" -version = "0.3.0" +version = "1.0.0" description = "Bitcoin Price Prediction Subnet" authors = ["Coin Metrics", "Yuma Group"] readme = "README.md" diff --git a/tests/test_package.py b/tests/test_package.py index d94327e..89b0eb0 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -16,4 +16,4 @@ def setUp(self): def test_package_version(self): # Check that version is as expected # Must update to increment package version successfully - self.assertEqual(__version__, "0.3.0") + self.assertEqual(__version__, "1.0.0")