Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update readme #46

Merged
merged 18 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
83 changes: 78 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

# **CoinMetrics Precog Subnet** <!-- omit in toc -->

<div align="center">

| **Testnet UID:** 256 <br> **Mainnet UID:** 55 |
| - |

</div>

<br/>

| | |
| :-: | :-: |
| **Status** | <img src="https://img.shields.io/github/v/release/coinmetrics/precog?label=Release" height="25"/> <img src="https://img.shields.io/github/actions/workflow/status/coinmetrics/precog/ci.yml?label=Build" height="25"/> <br> <a href="https://github.com/pre-commit/pre-commit" target="_blank"> <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&label=Pre-Commit" height="25"/> </a> <a href="https://github.com/psf/black" target="_blank"> <img src="https://img.shields.io/badge/code%20style-black-000000.svg?label=Code%20Style" height="25"/> </a> <br> <img src="https://img.shields.io/github/license/coinmetrics/precog?label=License" height="25"/> |
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -70,29 +102,70 @@ 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
```
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:
Expand Down
5 changes: 5 additions & 0 deletions docs/Release Notes.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 1 addition & 25 deletions precog/utils/bittensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
65 changes: 59 additions & 6 deletions precog/validators/weight_setter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
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):
Expand Down Expand Up @@ -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] = (
Expand All @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Loading