diff --git a/docs/validator.md b/docs/validator.md index 69d2093a..0de921ad 100644 --- a/docs/validator.md +++ b/docs/validator.md @@ -30,4 +30,5 @@ If you want to run validation APIs locally, check out [Setup validator endpoint] 3. (Optional) **Enable Auto Update Validator** ``` pm2 start auto_update.sh --name "auto-update" -``` \ No newline at end of file +``` + diff --git a/image_generation_subnet/protocol.py b/image_generation_subnet/protocol.py index 9f40f8e2..31ec64aa 100644 --- a/image_generation_subnet/protocol.py +++ b/image_generation_subnet/protocol.py @@ -1,3 +1,5 @@ +import time +import json import bittensor as bt import pydantic from generation_models.utils import base64_to_pil_image @@ -99,7 +101,7 @@ def deserialize_response(self): "response_dict": self.response_dict, } - def store_response(self, storage_url: str, uid, validator_uid): + def store_response(self, storage_url: str, uid, validator_uid, keypair: bt.Keypair): if self.model_name == "GoJourney": storage_url = storage_url + "/upload-go-journey-item" data = { @@ -128,6 +130,14 @@ def store_response(self, storage_url: str, uid, validator_uid): "pipeline_params": self.pipeline_params, } } + serialized_data = json.dumps(data, sort_keys=True, separators=(',', ':')) + nonce = str(time.time_ns()) + # Calculate validator 's signature + message = f"{serialized_data}{keypair.ss58_address}{nonce}" + signature = f"0x{keypair.sign(message).hex()}" + # Add validator 's signature + data["nonce"] = nonce + data["signature"] = signature try: response = requests.post(storage_url, json=data) response.raise_for_status() @@ -301,7 +311,7 @@ def deserialize_response(self): "model_name": self.model_name, } - def store_response(self, storage_url: str, uid, validator_uid): + def store_response(self, storage_url: str, uid, validator_uid, keypair: bt.Keypair): storage_url = storage_url + "/upload-multimodal-item" minimized_prompt_output: dict = copy.deepcopy(self.prompt_output) minimized_prompt_output['choices'][0].pop("logprobs") @@ -317,6 +327,14 @@ def store_response(self, storage_url: str, uid, validator_uid): "pipeline_params": self.pipeline_params, } } + serialized_data = json.dumps(data, sort_keys=True, separators=(',', ':')) + nonce = str(time.time_ns()) + # Calculate validator 's signature + message = f"{serialized_data}{keypair.ss58_address}{nonce}" + signature = f"0x{keypair.sign(message).hex()}" + # Add validator 's signature + data["nonce"] = nonce + data["signature"] = signature try: response = requests.post(storage_url, json=data) response.raise_for_status() diff --git a/image_generation_subnet/validator/miner_manager.py b/image_generation_subnet/validator/miner_manager.py index 176ed3f1..c3b84623 100644 --- a/image_generation_subnet/validator/miner_manager.py +++ b/image_generation_subnet/validator/miner_manager.py @@ -1,3 +1,5 @@ +import json +import time import bittensor as bt from image_generation_subnet.protocol import ImageGenerating, Information import torch @@ -150,21 +152,31 @@ def get_model_specific_weights(self, model_name, normalize=True): return model_specific_weights def store_miner_info(self): + catalogue = {} + for k, v in self.validator.nicheimage_catalogue.items(): + catalogue[k] = { + "model_incentive_weight": v.get("model_incentive_weight", 0), + "supporting_pipelines": v.get("supporting_pipelines", []), + } + data = { + "uid": self.validator.uid, + "info": self.all_uids_info, + "version": ig_subnet.__version__, + "catalogue": catalogue, + } + serialized_data = json.dumps(data, sort_keys=True, separators=(',', ':')) + nonce = str(time.time_ns()) + # Calculate validator 's signature + keypair = self.validator.wallet.hotkey + message = f"{serialized_data}{keypair.ss58_address}{nonce}" + signature = f"0x{keypair.sign(message).hex()}" + # Add validator 's signature + data["nonce"] = nonce + data["signature"] = signature try: - catalogue = {} - for k, v in self.validator.nicheimage_catalogue.items(): - catalogue[k] = { - "model_incentive_weight": v.get("model_incentive_weight", 0), - "supporting_pipelines": v.get("supporting_pipelines", []), - } requests.post( self.validator.config.storage_url + "/store_miner_info", - json={ - "uid": self.validator.uid, - "info": self.all_uids_info, - "version": ig_subnet.__version__, - "catalogue": catalogue, - }, + json=data ) self.reset_metadata() except Exception as e: diff --git a/neurons/validator/validator.py b/neurons/validator/validator.py index 26927a8d..4015c35a 100644 --- a/neurons/validator/validator.py +++ b/neurons/validator/validator.py @@ -576,7 +576,7 @@ def async_query_and_reward( ) store_thread = threading.Thread( target=self.store_miner_output, - args=(self.config.storage_url, responses, uids, self.uid), + args=(self.config.storage_url, responses, uids), daemon=True, ) store_thread.start() @@ -683,16 +683,16 @@ def prepare_challenge(self, uids_should_rewards, model_name, pipeline_type): return synapses, batched_uids_should_rewards def store_miner_output( - self, storage_url, responses: list[bt.Synapse], uids, validator_uid + self, storage_url, responses: list[bt.Synapse], uids ): if not self.config.share_response: return - - for uid, response in enumerate(responses): + + for uid, response in zip(uids, responses): if not response.is_success: continue try: - response.store_response(storage_url, uid, validator_uid) + response.store_response(storage_url, uid, self.uid, self.wallet.hotkey) break except Exception as e: bt.logging.error(f"Error in storing response: {e}") diff --git a/neurons/validator/validator_proxy.py b/neurons/validator/validator_proxy.py index fa14b627..11682658 100644 --- a/neurons/validator/validator_proxy.py +++ b/neurons/validator/validator_proxy.py @@ -1,3 +1,4 @@ +import time from fastapi import FastAPI, HTTPException, Depends from concurrent.futures import ThreadPoolExecutor import uvicorn @@ -16,12 +17,8 @@ from starlette.concurrency import run_in_threadpool import threading - class ValidatorProxy: - def __init__( - self, - validator, - ): + def __init__(self, validator: "neurons.validator.validator.Validator"): self.validator = validator self.get_credentials() self.miner_request_counter = {} @@ -41,16 +38,26 @@ def __init__( self.start_server() def get_credentials(self): + postfix = ( + f":{self.validator.config.proxy.port}/validator_proxy" + if self.validator.config.proxy.port + else "" + ) + ss58_address = self.validator.wallet.hotkey.ss58_address + uid = self.validator.uid + nonce = str(time.time_ns()) + # Calculate validator 's signature + message = f"{postfix}{ss58_address}{nonce}" + signature = f"0x{self.validator.wallet.hotkey.sign(message).hex()}" + with httpx.Client(timeout=httpx.Timeout(30)) as client: response = client.post( f"{self.validator.config.proxy.proxy_client_url}/get_credentials", json={ - "postfix": ( - f":{self.validator.config.proxy.port}/validator_proxy" - if self.validator.config.proxy.port - else "" - ), - "uid": self.validator.uid, + "postfix": postfix, + "uid": uid, + "signature": signature, + "nonce": nonce }, ) response.raise_for_status()