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

Security fixes #33

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
16 changes: 13 additions & 3 deletions neurons/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __init__(self):
wallet=self.wallet, config=self.config, external_ip=bt.net.get_external_ip()
)
bt.logging.info(f"Axon {self.axon}")

# Attach determiners which functions are called when servicing a request.
bt.logging.info("Attaching forward functions to axon.")
self.axon.attach(
Expand All @@ -140,7 +140,7 @@ def __init__(self):
f"Serving axon {self.axon} on network: {self.subtensor.chain_endpoint} with netuid: {self.config.netuid}"
)
self.axon.serve(netuid=self.config.netuid, subtensor=self.subtensor)

# Check there is not another miner running on the machine
number_of_miners = len(
[axon for axon in self.metagraph.axons if self.axon.external_ip == axon.ip]
Expand Down Expand Up @@ -172,6 +172,16 @@ def __init__(self):
def _score(self, synapse: Score) -> Score:
validator_uid = synapse.validator_uid

if synapse.owner:
bt.logging.info(f"[{validator_uid}] Miner owns the subtensor")
elif synapse.owner == False:
bt.logging.error(f"[{validator_uid}] Miner does not own the subtensor")

if synapse.verified:
bt.logging.info(f"[{validator_uid}] Miner/Subtensor verified")
elif synapse.verified == False:
bt.logging.error(f"[{validator_uid}] {synapse.reason or 'unknown'}")

if synapse.count > 1:
bt.logging.error(
f"[{validator_uid}] {synapse.count} miners are running on this machine"
Expand All @@ -184,7 +194,7 @@ def _score(self, synapse: Score) -> Score:
bt.logging.success(f"[{validator_uid}] Score {synapse.score}")

synapse.version = THIS_VERSION

return synapse

def blacklist_score(self, synapse: Score) -> typing.Tuple[bool, str]:
Expand Down
17 changes: 16 additions & 1 deletion neurons/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
from subnet.shared.weights import should_set_weights
from subnet.shared.mock import MockMetagraph, MockDendrite, MockSubtensor

from subnet.monitor.monitor_process import MonitorProcess

from subnet.validator.config import config, check_config, add_args
from subnet.validator.localisation import get_country, get_localisation
from subnet.validator.forward import forward
Expand Down Expand Up @@ -190,6 +192,13 @@ async def run(self):
# Load the state
load_state(self)

# Monitor miners
monitor_process = MonitorProcess()
monitor_process.start()

# Get the monitor object
self.monitor = monitor_process.monitor

try:
while 1:
start_epoch = time.time()
Expand Down Expand Up @@ -218,7 +227,9 @@ async def run(self):
async def run_forward():
coroutines = [
forward(self)
for _ in range(1) # IMPORTANT: do not change it. we are going to work to make it concurrent tasks asap!
for _ in range(
1
) # IMPORTANT: do not change it. we are going to work to make it concurrent tasks asap!
]
await asyncio.gather(*coroutines)

Expand Down Expand Up @@ -270,6 +281,10 @@ async def run_forward():

# After all we have to ensure subtensor connection is closed properly
finally:
if monitor_process:
monitor_process.join()
monitor_process = None

if hasattr(self, "subtensor"):
bt.logging.debug("Closing subtensor connection")
self.subtensor.close()
Expand Down
56 changes: 56 additions & 0 deletions subnet/monitor/monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import json
import requests
import threading
from typing import List

from subnet.shared import logging as sv
from subnet.monitor.monitor_constants import MONITOR_URL, LOGGING_NAME, LOGGING_DELTA

{"suspicious": [28]}


class Monitor:
def __init__(self):
super().__init__()
self._lock = threading.Lock()
self._data = {}

self.last_modified = None

def get_suspicious_uids(self) -> List[int]:
with self._lock:
suspicious = self._data.get("suspicious") or []
return list(suspicious)

def run(self):
response = requests.get(MONITOR_URL)
if response.status_code != 200:
sv.logging.warn(
f"[{LOGGING_NAME}] Could not get the monitored file {response.status_code}: {response.reason}",
silence_period=LOGGING_DELTA,
)
return

last_modified = response.headers.get("Last-Modified")
if self.last_modified == last_modified:
return

sv.logging.info(
f"[{LOGGING_NAME}] Monitored file has changed",
silence_period=LOGGING_DELTA,
)

# Store tag for future comparaison
self.last_modified = last_modified

# Load the data
data = response.json()

# Update the list
with self._lock:
self._data = data

sv.logging.success(
f"[{LOGGING_NAME}] Monitored file proceed successfully",
silence_period=LOGGING_DELTA,
)
8 changes: 8 additions & 0 deletions subnet/monitor/monitor_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Logging
LOGGING_NAME = "Monitor"
LOGGING_DELTA = 1 * 60 # Every 15 minutes

# Monitor
MONITOR_URL = (
"http://drive.google.com/uc?id=1d3NoL6THpLmLZTQxEZJNrb5uJY49l5Q4&export=download"
)
21 changes: 21 additions & 0 deletions subnet/monitor/monitor_process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import threading

from subnet.shared import logging as sv
from subnet.monitor.monitor_constants import LOGGING_NAME
from subnet.monitor.monitor import Monitor


class MonitorProcess(threading.Thread):
def __init__(self):
super().__init__()
self.stop_flag = threading.Event()
self.monitor = Monitor()

def run(self):
while not self.stop_flag.is_set():
try:
self.monitor.run()
except Exception as err:
sv.logging.error(
f"[{LOGGING_NAME}] An error during monitoring: {err}"
)
6 changes: 6 additions & 0 deletions subnet/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class Score(bt.Synapse):
distribution: float
score: float
count: typing.Optional[int] = 0
# True if the miner own the subtensor, false otherwise
owner: typing.Optional[bool] = True
# True if the miner/subtensor are up, false otherwise
verified: typing.Optional[bool] = False
# Reason of the why it is not verified
reason: typing.Optional[str] = None

# Returns
version: typing.Optional[str] = None
Expand Down
77 changes: 77 additions & 0 deletions subnet/shared/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import math
import time
import bittensor as bt


class logging(bt.logging):
def __new__(
cls,
config: "bt.config" = None,
debug: bool = None,
trace: bool = None,
record_log: bool = None,
logging_dir: str = None,
):
cls.last_time = 0

super.__new__(
config=config,
debug=debug,
trace=trace,
record_log=record_log,
logging_dir=logging_dir,
)

return cls

def can_log(cls, silence_period=None):
if not silence_period:
return True

current_time = time.time()
delta = math.floor(current_time - cls.last_time)
if delta <= silence_period:
return False

cls.last_time = current_time
return True

@classmethod
def info(cls, message: str, silence_period=None):
if not cls.__has_been_inited__:
cls()

if not cls.can_log(silence_period):
return

bt.logging.info(message)

@classmethod
def warn(cls, message: str, silence_period=None):
if not cls.__has_been_inited__:
cls()

if not cls.can_log(silence_period):
return

bt.logging.warning(message)

@classmethod
def success(cls, message: str, silence_period=None):
if not cls.__has_been_inited__:
cls()

if not cls.can_log(silence_period):
return

bt.logging.success(message)

@classmethod
def error(cls, message: str, silence_period=None):
if not cls.__has_been_inited__:
cls()

if not cls.can_log(silence_period):
return

bt.logging.error(message)
Loading