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

Introduction of CR3 to SN28 #65

Merged
merged 13 commits into from
Jan 8, 2025
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ venv/
ENV/
env.bak/
venv.bak/
# branch specific environments that start with issue number
.[0-9]*-*

# Spyder project settings
.spyderproject
Expand Down Expand Up @@ -166,3 +168,5 @@ timestamp.txt
# localnet config
miner.config.local.js
validator.config.local.js

local_data/
26 changes: 14 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
################################################################################
# User Parameters #
################################################################################
coldkey = default
validator_hotkey = validator
miner_hotkey = miner
netuid = $(testnet_netuid)
network = $(testnet)
logging_level = info # options= ['info', 'debug', 'trace']
# coldkey = validator
coldkey = miner
validator_hotkey = default
miner_hotkey = default
netuid = $(localnet_netuid)
network = $(localnet)
logging_level = debug # options= ['info', 'debug', 'trace']


################################################################################
# Network Parameters #
################################################################################
finney = wss://entrypoint-finney.opentensor.ai:443
testnet = wss://test.finney.opentensor.ai:443
locanet = ws://127.0.0.1:9944
localnet = ws://127.0.0.1:9945

finney_netuid = 28
testnet_netuid = 93
testnet_netuid = 272
localnet_netuid = 1


Expand All @@ -41,16 +42,17 @@ validator:
--subtensor.chain_endpoint $(network) \
--axon.port 8091 \
--netuid $(netuid) \
--logging.level $(logging_level)
--logging.$(logging_level)

miner:
pm2 start python --name miner -- ./snp_oracle/neurons/miner.py \
pm2 start python --name miner -- ./snp_oracle/neurons/miner.py --no-autorestart \
--wallet.name $(coldkey) \
--wallet.hotkey $(miner_hotkey) \
--subtensor.chain_endpoint $(network) \
--axon.port 8092 \
--netuid $(netuid) \
--logging.level $(logging_level) \
--logging.$(logging_level) \
--vpermit_tao_limit 2 \
--hf_repo_id foundryservices/bittensor-sn28-base-lstm \
--blacklist.force_validator_permit true \
--hf_repo_id pcarlson-foundry-digital/localnet \
--model mining_models/base_lstm_new.h5
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<div align="center">

| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 93 <br> **Mainnet UID:** 28 |
| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 272 <br> **Mainnet UID:** 28 |
| - | - |

</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/miners.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<div align="center">

| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 93 <br> **Mainnet UID:** 28 |
| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 272 <br> **Mainnet UID:** 28 |
| - | - |

</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div align="center">

| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 93 <br> **Mainnet UID:** 28 |
| This repository is the official codebase<br>for Bittensor Subnet 28 (SN28) v1.0.0+,<br>which was released on February 20th 2024. | **Testnet UID:** 272 <br> **Mainnet UID:** 28 |
| - | - |

</div>
Expand Down
1,323 changes: 758 additions & 565 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ readme = "README.md"
python = ">3.9.1,<3.12"

# Bittensor Version Strict
bittensor = "7.4.0"
bittensor = "8.5.1"

# Bittensor Dependencies We Also Need
setuptools = "~70.0.0"
pydantic = "^2.3.0"
numpy = "^1.26"
numpy = ">=2.0.1,<2.1.0"

# Subnet Specific Dependencies
torch = "^2.5.1"
Expand All @@ -35,7 +35,7 @@ pandas-market-calendars = "^4.4.2"
python-dotenv = "^1.0.1"
scikit-learn = "^1.6.0"
wandb = "^0.19.1"
cryptography = ">=42.0.5,<42.1.0"
cryptography = ">=43.0.1,<43.1.0"

[tool.poetry.group.dev.dependencies]
pre-commit-hooks = "5.0.0"
Expand Down
43 changes: 29 additions & 14 deletions snp_oracle/neurons/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import typing

import bittensor as bt
import tensorflow as tf
from cryptography.fernet import Fernet

# ML imports
from dotenv import load_dotenv
from huggingface_hub import hf_hub_download
from tensorflow.keras.models import load_model

import snp_oracle.predictionnet as predictionnet
from snp_oracle.base_miner.get_data import prep_data, scale_data
Expand All @@ -29,8 +29,13 @@ class Miner(BaseMinerNeuron):
"""

def __init__(self, config=None):
bt.logging.info("Initializing Miner...")
bt.logging.info(f"Initial config: {config}")

super(Miner, self).__init__(config=config)
print(config)

bt.logging.info(f"Config after super init: {self.config}")
bt.logging.info(f"Config model path: {self.config.model if self.config else 'No config'}")
# TODO(developer): Anything specific to your use case you can do here
self.model_loc = self.config.model
if self.config.neuron.device == "cpu":
Expand Down Expand Up @@ -184,7 +189,7 @@ async def forward(self, synapse: predictionnet.protocol.Challenge) -> prediction
)
bt.logging.info(f"Model downloaded from huggingface at {model_path}")

model = load_model(model_path)
model = tf.keras.models.load_model(model_path)
data = prep_data()

# Generate encryption key for this request
Expand Down Expand Up @@ -237,17 +242,27 @@ def print_info(self):
metagraph = self.metagraph
self.uid = self.metagraph.hotkeys.index(self.wallet.hotkey.ss58_address)

log = (
"Miner | "
f"Step:{self.step} | "
f"UID:{self.uid} | "
f"Block:{self.block} | "
f"Stake:{metagraph.S[self.uid]:.4f} | "
f"Trust:{metagraph.T[self.uid]:.4f} | "
f"Incentive:{metagraph.I[self.uid]:.4f} | "
f"Emission:{metagraph.E[self.uid]:.4f}"
)
bt.logging.info(log)
# Get all values in one go to avoid multiple concurrent requests
try:
current_block = self.block # Single websocket call
stake = float(metagraph.S[self.uid])
trust = float(metagraph.T[self.uid])
incentive = float(metagraph.I[self.uid])
emission = float(metagraph.E[self.uid])

log = (
"Miner | "
f"Step:{self.step} | "
f"UID:{self.uid} | "
f"Block:{current_block} | "
f"Stake:{stake:.4f} | "
f"Trust:{trust:.4f} | "
f"Incentive:{incentive:.4f} | "
f"Emission:{emission:.4f}"
)
bt.logging.info(log)
except Exception as e:
bt.logging.error(f"Error getting miner info: {e}")


# This is the main function, which runs the miner.
Expand Down
32 changes: 21 additions & 11 deletions snp_oracle/neurons/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,27 @@ def print_info(self):
metagraph = self.metagraph
self.uid = self.metagraph.hotkeys.index(self.wallet.hotkey.ss58_address)

log = (
"Validator | "
f"Step:{self.step} | "
f"UID:{self.uid} | "
f"Block:{self.block} | "
f"Stake:{metagraph.S[self.uid]} | "
f"VTrust:{metagraph.Tv[self.uid]} | "
f"Dividend:{metagraph.D[self.uid]} | "
f"Emission:{metagraph.E[self.uid]}"
)
bt.logging.info(log)
# Get all values in one go to avoid multiple concurrent requests
try:
current_block = self.block # Single websocket call
stake = float(metagraph.S[self.uid])
vtrust = float(metagraph.Tv[self.uid])
dividend = float(metagraph.D[self.uid])
emission = float(metagraph.E[self.uid])

log = (
"Validator | "
f"Step:{self.step} | "
f"UID:{self.uid} | "
f"Block:{current_block} | "
f"Stake:{stake:.4f} | "
f"VTrust:{vtrust:.4f} | "
f"Dividend:{dividend:.4f} | "
f"Emission:{emission:.4f}"
)
bt.logging.info(log)
except Exception as e:
bt.logging.error(f"Error getting validator info: {e}")


# The main function parses the configuration and runs the validator.
Expand Down
140 changes: 77 additions & 63 deletions snp_oracle/predictionnet/base/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import traceback

import bittensor as bt
from bittensor.constants import V_7_2_0
from substrateinterface import Keypair

from snp_oracle.predictionnet.base.neuron import BaseNeuron
from snp_oracle.predictionnet.protocol import Challenge

# from substrateinterface import Keypair


# from snp_oracle.predictionnet.protocol import Challenge


class BaseMinerNeuron(BaseNeuron):
Expand Down Expand Up @@ -168,63 +170,75 @@ def resync_metagraph(self):
self.metagraph.sync(subtensor=self.subtensor)
self.metagraph.last_update[self.uid] = self.block

def _to_seconds(self, nano: int) -> int:
return nano / 1_000_000_000

async def verify(self, synapse: Challenge) -> None:
# needs to replace the base miner verify logic
bt.logging.debug(f"checking nonce: {synapse.dendrite}")
# Build the keypair from the dendrite_hotkey
if synapse.dendrite is not None:
keypair = Keypair(ss58_address=synapse.dendrite.hotkey)

# Build the signature messages.
message = f"{synapse.dendrite.nonce}.{synapse.dendrite.hotkey}.{self.wallet.hotkey.ss58_address}.{synapse.dendrite.uuid}.{synapse.computed_body_hash}"

# Build the unique endpoint key.
endpoint_key = f"{synapse.dendrite.hotkey}:{synapse.dendrite.uuid}"

# Requests must have nonces to be safe from replays
if synapse.dendrite.nonce is None:
raise Exception("Missing Nonce")
if synapse.dendrite.version is not None and synapse.dendrite.version >= V_7_2_0:
bt.logging.debug("Using custom synapse verification logic")
# If we don't have a nonce stored, ensure that the nonce falls within
# a reasonable delta.
cur_time = time.time_ns()

allowed_delta = self.config.timeout * 1_000_000_000 # nanoseconds

latest_allowed_nonce = synapse.dendrite.nonce + allowed_delta

bt.logging.debug(f"synapse.dendrite.nonce: {synapse.dendrite.nonce}")
bt.logging.debug(f"latest_allowed_nonce: {latest_allowed_nonce}")
bt.logging.debug(f"cur time: {cur_time}")
bt.logging.debug(f"delta: {self._to_seconds(cur_time - synapse.dendrite.nonce)}")

if self.nonces.get(endpoint_key) is None and synapse.dendrite.nonce > latest_allowed_nonce:
raise Exception(
f"Nonce is too old. Allowed delta in seconds: {self._to_seconds(allowed_delta)}, got delta: {self._to_seconds(cur_time - synapse.dendrite.nonce)}"
)
if self.nonces.get(endpoint_key) is not None and synapse.dendrite.nonce <= self.nonces[endpoint_key]:
raise Exception(
f"Nonce is too small, already have a newer nonce in the nonce store, got: {synapse.dendrite.nonce}, already have: {self.nonces[endpoint_key]}"
)
else:
bt.logging.warning(f"Using synapse verification logic for version < 7.2.0: {synapse.dendrite.version}")
if (
endpoint_key in self.nonces.keys()
and self.nonces[endpoint_key] is not None
and synapse.dendrite.nonce <= self.nonces[endpoint_key]
):
raise Exception(
f"Nonce is too small, already have a newer nonce in the nonce store, got: {synapse.dendrite.nonce}, already have: {self.nonces[endpoint_key]}"
)

if not keypair.verify(message, synapse.dendrite.signature):
raise Exception(
f"Signature mismatch with {message} and {synapse.dendrite.signature}, from hotkey {synapse.dendrite.hotkey}"
)

# Success
self.nonces[endpoint_key] = synapse.dendrite.nonce # type: ignore
# def _to_seconds(self, nano: int) -> int:
# return nano / 1_000_000_000

# async def verify(self, synapse: Challenge) -> None:
# """
# Verifies the authenticity and validity of incoming requests.

# Args:
# synapse: The Challenge object containing request details and credentials

# Raises:
# Exception: If verification fails due to missing nonce, invalid timestamps,
# duplicate nonces, or signature mismatch
# """
# bt.logging.debug(f"checking nonce: {synapse.dendrite}")

# # Skip verification if no dendrite info
# if synapse.dendrite is None:
# return

# # Build keypair and verify signature
# keypair = Keypair(ss58_address=synapse.dendrite.hotkey)

# # Check for missing nonce
# if synapse.dendrite.nonce is None:
# raise Exception("Missing Nonce")

# # Build unique endpoint identifier
# endpoint_key = f"{synapse.dendrite.hotkey}:{synapse.dendrite.uuid}"

# # Check timestamp validity
# cur_time = time.time_ns()
# allowed_delta = self.config.timeout * 1_000_000_000 # nanoseconds
# latest_allowed_nonce = synapse.dendrite.nonce + allowed_delta

# # Log timing details for debugging
# bt.logging.debug(f"synapse.dendrite.nonce: {synapse.dendrite.nonce}")
# bt.logging.debug(f"latest_allowed_nonce: {latest_allowed_nonce}")
# bt.logging.debug(f"cur time: {cur_time}")
# bt.logging.debug(f"delta: {self._to_seconds(cur_time - synapse.dendrite.nonce)}")

# # Verify nonce timing
# if self.nonces.get(endpoint_key) is None and synapse.dendrite.nonce > latest_allowed_nonce:
# raise Exception(
# f"Nonce is too old. Allowed delta in seconds: {self._to_seconds(allowed_delta)}, "
# f"got delta: {self._to_seconds(cur_time - synapse.dendrite.nonce)}"
# )

# # Verify nonce order
# if self.nonces.get(endpoint_key) is not None and synapse.dendrite.nonce <= self.nonces[endpoint_key]:
# raise Exception(
# f"Nonce is too small, already have a newer nonce in the nonce store, "
# f"got: {synapse.dendrite.nonce}, already have: {self.nonces[endpoint_key]}"
# )

# # Build and verify signature message
# message = (
# f"{synapse.dendrite.nonce}."
# f"{synapse.dendrite.hotkey}."
# f"{self.wallet.hotkey.ss58_address}."
# f"{synapse.dendrite.uuid}."
# f"{synapse.computed_body_hash}"
# )

# if not keypair.verify(message, synapse.dendrite.signature):
# raise Exception(
# f"Signature mismatch with {message} and {synapse.dendrite.signature}, "
# f"from hotkey {synapse.dendrite.hotkey}"
# )

# # Store nonce after successful verification
# self.nonces[endpoint_key] = synapse.dendrite.nonce # type: ignore
Loading