From 9178d003527802f4aa1c85577512eaca0f16d525 Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 13 Mar 2026 23:53:11 +0000 Subject: [PATCH] httptools for header checking before body processing --- .../generative_challenge_manager.py | 5 +++- gas/protocol/epistula.py | 24 +++++++++++-------- neurons/generator/miner.py | 1 + pyproject.toml | 1 + 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/gas/evaluation/generative_challenge_manager.py b/gas/evaluation/generative_challenge_manager.py index 630e25ea..86f68bc6 100644 --- a/gas/evaluation/generative_challenge_manager.py +++ b/gas/evaluation/generative_challenge_manager.py @@ -425,8 +425,10 @@ def init_fastapi(self): self.api = FastAPI() self.router = APIRouter() + # httptools streams the body — handler is invoked on headers, so now is + # captured before the upload verifier = get_verifier( - self.wallet, self.metagraph, no_force_validator_permit=True + self.wallet, self.metagraph, no_force_validator_permit=True, ) self.router.add_api_route( @@ -444,6 +446,7 @@ def init_fastapi(self): port=self.config.neuron.callback_port, log_level="info", loop="asyncio", + http="httptools", ) self.fast_api = FastAPIThreadedServer(config=fast_config) self.fast_api.start() diff --git a/gas/protocol/epistula.py b/gas/protocol/epistula.py index 9c5ab967..24be8622 100644 --- a/gas/protocol/epistula.py +++ b/gas/protocol/epistula.py @@ -62,7 +62,8 @@ def generate_header( def verify_signature( - signature, body: bytes, timestamp, uuid, signed_for, signed_by, now + signature, body: bytes, timestamp, uuid, signed_for, signed_by, now, + allowed_delta_ms: int = 15000, ) -> Optional[Annotated[str, "Error Message"]]: if not isinstance(signature, str): return "Invalid Signature" @@ -77,12 +78,11 @@ def verify_signature( return "Invalid uuid" if not isinstance(body, bytes): return "Body is not of type bytes" - ALLOWED_DELTA_MS = 15000 keypair = Keypair(ss58_address=signed_by) - if timestamp + ALLOWED_DELTA_MS < now: + if timestamp + allowed_delta_ms < now: staleness_ms = now - timestamp staleness_seconds = staleness_ms / 1000.0 - return f"Request is too stale: {staleness_seconds:.1f}s old (limit: {ALLOWED_DELTA_MS/1000.0}s)" + return f"Request is too stale: {staleness_seconds:.1f}s old (limit: {allowed_delta_ms/1000.0}s)" message = f"{sha256(body).hexdigest()}.{uuid}.{timestamp}.{signed_for}" verified = keypair.verify(message, signature) if not verified: @@ -102,10 +102,10 @@ async def _verify_request( request: Request, wallet: bt.Wallet, metagraph: bt.Metagraph, - no_force_validator_permit: bool + no_force_validator_permit: bool, + allowed_delta_ms: int = 15000, ): now = round(time.time() * 1000) - signed_by = request.headers.get("Epistula-Signed-By") signed_for = request.headers.get("Epistula-Signed-For") client_ip = request.client.host if request.client else "unknown" @@ -115,7 +115,7 @@ async def _verify_request( raise HTTPException( status_code=400, detail="Bad Request, message is not intended for self" ) - + if signed_by not in metagraph.hotkeys: bt.logging.error(f"Signer not in metagraph: {signed_by} (IP: {client_ip})") raise HTTPException(status_code=401, detail="Signer not in metagraph") @@ -138,6 +138,7 @@ async def _verify_request( signed_for, signed_by, now, + allowed_delta_ms=allowed_delta_ms, ) if err: @@ -149,11 +150,12 @@ async def determine_epistula_version_and_verify( request: Request, wallet: bt.Wallet, metagraph: bt.Metagraph, - no_force_validator_permit: bool + no_force_validator_permit: bool, + allowed_delta_ms: int = 15000, ): version = request.headers.get("Epistula-Version") if version == EPISTULA_VERSION: - await _verify_request(request, wallet, metagraph, no_force_validator_permit) + await _verify_request(request, wallet, metagraph, no_force_validator_permit, allowed_delta_ms) return raise HTTPException(status_code=400, detail="Unknown Epistula version") @@ -161,7 +163,8 @@ async def determine_epistula_version_and_verify( def get_verifier( wallet: bt.Wallet, metagraph: bt.Metagraph, - no_force_validator_permit: bool = False + no_force_validator_permit: bool = False, + allowed_delta_ms: int = 15000, ): async def verifier(request: Request): await determine_epistula_version_and_verify( @@ -169,5 +172,6 @@ async def verifier(request: Request): wallet, metagraph, no_force_validator_permit, + allowed_delta_ms, ) return verifier diff --git a/neurons/generator/miner.py b/neurons/generator/miner.py index 381441bf..c6c60c33 100644 --- a/neurons/generator/miner.py +++ b/neurons/generator/miner.py @@ -514,6 +514,7 @@ def run(self): port=self.config.axon.port, log_level="info", loop="asyncio", + http="httptools", ) self.fast_api = FastAPIThreadedServer(config=fast_config) self.fast_api.start() diff --git a/pyproject.toml b/pyproject.toml index 77e9029d..c0b80197 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ dependencies = [ "opencv-python==4.11.0.86", "wandb==0.19.9", "uvicorn==0.27.1", + "httptools>=0.6.0", "python-multipart==0.0.22", "peft==0.17.0", "aiohttp>=3.10.2",