Skip to content

Commit

Permalink
Merge pull request #37 from drunest/feat/subnet-sync
Browse files Browse the repository at this point in the history
Feat/subnet sync
  • Loading branch information
ppolewicz authored Sep 28, 2024
2 parents dc7207c + 7484ff7 commit 2b14176
Show file tree
Hide file tree
Showing 28 changed files with 2,581 additions and 395 deletions.
3 changes: 3 additions & 0 deletions .codespellrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[codespell]
ignore-words-list = ded, bU, te
skip = node_modules, *.min.js, *.bundle.js, package-lock.json, pdm.lock
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ media/
.terraform/
.nox/
__pycache__
app/src/manager/celerybeat-schedule
/app/src/auto_validator/core/celerybeat-schedule
/app/src/auto_validator/core/frontend/node_modules
29 changes: 27 additions & 2 deletions app/src/auto_validator/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.contrib import admin
from django.db.models import Case, IntegerField, Value, When
from django.shortcuts import redirect
from django.urls import path, reverse
from rest_framework.authtoken.admin import TokenAdmin

from auto_validator.core.models import (
Expand All @@ -11,6 +13,7 @@
UploadedFile,
ValidatorInstance,
)
from auto_validator.core.utils.utils import fetch_and_compare_subnets

admin.site.site_header = "auto_validator Administration"
admin.site.site_title = "auto_validator"
Expand All @@ -21,8 +24,8 @@

@admin.register(UploadedFile)
class UploadedFileAdmin(admin.ModelAdmin):
list_display = ("file_name", "file_size", "created_at")
list_filter = ("created_at", "file_size")
list_display = ("file_name", "file_size", "hotkey", "description", "created_at")
list_filter = ("hotkey", "created_at", "file_size")
search_fields = ("file_name",)


Expand All @@ -31,10 +34,32 @@ class SubnetAdmin(admin.ModelAdmin):
list_display = (
"name",
"description",
"mainnet_id",
"testnet_id",
"owner_nick",
"registered_networks",
)
search_fields = ("name", "slots__netuid")

def create_server(self, request, queryset):
subnet = queryset.first()
return redirect("admin:select_provider", subnet_id=subnet.id)

def get_urls(self):
urls = super().get_urls()
custom_urls = [
path("sync-subnets/", self.admin_site.admin_view(self.sync_subnet), name="sync_subnets"),
]
return custom_urls + urls

def sync_subnet(self, request):
return fetch_and_compare_subnets(request)

def changelist_view(self, request, extra_context=None):
extra_context = extra_context or {}
extra_context["sync_subnets_url"] = reverse("admin:sync_subnets")
return super().changelist_view(request, extra_context=extra_context)


@admin.register(SubnetSlot)
class SubnetSlotAdmin(admin.ModelAdmin):
Expand Down
31 changes: 27 additions & 4 deletions app/src/auto_validator/core/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from rest_framework import mixins, parsers, routers, viewsets
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import AllowAny

from auto_validator.core.models import UploadedFile
from auto_validator.core.models import Hotkey, Server, UploadedFile, ValidatorInstance
from auto_validator.core.serializers import UploadedFileSerializer
from auto_validator.core.utils.utils import get_user_ip

from .authentication import HotkeyAuthentication
from .utils.bot import trigger_bot_send_message
Expand All @@ -11,16 +13,37 @@
class FilesViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = UploadedFileSerializer
parser_classes = [parsers.MultiPartParser]
queryset = UploadedFile.objects.all()

authentication_classes = [HotkeyAuthentication]
permission_classes = [AllowAny]

def get_queryset(self):
hotkey_str = self.request.headers.get("Hotkey")
try:
hotkey = Hotkey.objects.get(hotkey=hotkey_str)
except Hotkey.DoesNotExist:
return []
return UploadedFile.objects.filter(hotkey=hotkey).order_by("id")

def perform_create(self, serializer):
uploaded_file = serializer.save()
note = self.request.headers.get("Note")
hotkey_str = self.request.headers.get("Hotkey")
channel_name = self.request.headers.get("SubnetID")
realm = self.request.headers.get("Realm")
ip_address = get_user_ip(self.request)
try:
hotkey = Hotkey.objects.get(hotkey=hotkey_str)
server = Server.objects.get(ip_address=ip_address)
subnetslot = ValidatorInstance.objects.get(hotkey=hotkey, server=server).subnet_slot
except ValidatorInstance.DoesNotExist:
raise AuthenticationFailed("Invalid Hotkey")
uploaded_file = serializer.save(
meta_info={
"note": note,
"hotkey": hotkey_str,
"subnet_name": subnetslot.subnet.name,
"netuid": subnetslot.netuid,
}
)
file_url = uploaded_file.get_full_url(self.request)
trigger_bot_send_message(
channel_name=channel_name, message=(f"{note}\n" f"New validator logs:\n" f"{file_url}"), realm=realm
Expand Down
17 changes: 11 additions & 6 deletions app/src/auto_validator/core/authentication.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import time

from bittensor import Keypair
from django.conf import settings
Expand All @@ -9,17 +10,23 @@

class HotkeyAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
if settings.TESTING:
# Bypass authentication during tests
return (None, None)

hotkey_address = request.headers.get("Hotkey")
nonce = request.headers.get("Nonce")
signature = request.headers.get("Signature")
method = request.method.upper()
url = request.build_absolute_uri()

if method == "GET":
return (None, None)

if not hotkey_address or not nonce or not signature:
raise exceptions.AuthenticationFailed("Missing authentication headers.")

nonce_float = float(nonce)
current_time = time.time()
if abs(current_time - nonce_float) > int(settings.SIGNATURE_EXPIRE_DURATION):
raise exceptions.AuthenticationFailed("Invalid nonce")

if not Hotkey.objects.filter(hotkey=hotkey_address).exists():
raise exceptions.AuthenticationFailed("Unauthorized hotkey.")

Expand All @@ -33,8 +40,6 @@ def authenticate(self, request):
client_headers = {k: v for k, v in client_headers.items() if v is not None}
headers_str = json.dumps(client_headers, sort_keys=True)

method = request.method.upper()
url = request.build_absolute_uri()
data_to_sign = f"{method}{url}{headers_str}"

if "file" in request.FILES:
Expand Down
18 changes: 18 additions & 0 deletions app/src/auto_validator/core/frontend/js/sync_subnets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Diff2Html from 'diff2html';
import 'diff2html/bundles/css/diff2html.min.css';

document.addEventListener('DOMContentLoaded', () => {
const diff_str = document.getElementById('diff_str').textContent;
const githubData = document.getElementById('github_data').textContent;
if (diff_str === '') {
document.getElementById('diff').innerHTML = '<h2>No changes found</h2>';
return;
}
const decodedDiffStr = decodeURIComponent(JSON.parse('"' + diff_str.replace(/\"/g, '\\"') + '"'));
const diffHtml = Diff2Html.html(
decodedDiffStr,
{ drawFileList: false, matching: 'lines', outputFormat: 'side-by-side' }
);
document.getElementById('diff').innerHTML = diffHtml;
document.getElementById('new_data').value = JSON.stringify(githubData);
});
Loading

0 comments on commit 2b14176

Please sign in to comment.