Perturb is a Bittensor subnet where validators create adversarial-image challenges and miners return perturbed images under bounded distortion constraints.
This repository provides:
- validator node implementation (
neurons/validator.py) - baseline miner implementation (
neurons/miner.py) - validator-side local LLM semantic verification service (
tools/llm_endpoint_service.py) - one-command launchers for validator, miner, and llm endpoint
- Pull challenge images from Pexels Search API (
PERTURB_IMAGE_ENDPOINT) - Run fixed classifier (
EfficientNetV2-M) on pulled image - Verify semantic consistency of model output vs prompt label through local
llm_endpoint - Build and broadcast
AttackChallengesynapse to selected miners - Verify miner responses and compute rewards
- Maintain rolling histories and set on-chain weights periodically
- Receive
AttackChallengeover Axon - Run baseline PGD-style attack
- Return only
perturbed_image_b64 - Let validator handle all authoritative verification and scoring
- Validator samples a prompt from
perturbnet/constants.py(PROMPTS) - Validator fetches image from Pexels using
query=<prompt>and random page/photo selection - If API pull fails, validator falls back to
assets/dog_1.jpgand sets prompt todog - Validator runs
EfficientNetV2-Mand gets exact model label string - Validator calls local
llm_endpoint(POST /verify-label) to confirm semantic match between model label and prompt - On success, validator creates challenge where
true_labelis the exact EfficientNet label - Validator sends challenge to sampled miners and scores returned perturbations
- Minimum: 4 vCPU, 16 GB RAM, 50 GB SSD, stable 20+ Mbps network
- Recommended: 8 vCPU, 32 GB RAM, NVIDIA GPU with 8+ GB VRAM, 100+ GB SSD
- Minimum: 8 vCPU, 32 GB RAM, NVIDIA GPU with 12+ GB VRAM, 100 GB SSD
- Recommended: 16 vCPU, 64 GB RAM, NVIDIA GPU with 24+ GB VRAM, 200 GB SSD
- Minimum: 2 vCPU, 4 GB RAM (assuming model already served by Ollama)
- Recommended: run on same private network/host as validator for low latency
- Python 3.10+
- Node.js 18+ (includes
npm) for PM2 installation pipand virtualenv support (python -m venv)- OS build tools needed by Python wheels
- For GPU usage: correct NVIDIA driver + CUDA stack compatible with installed PyTorch
Run role-specific setup once before starting nodes:
git clone https://github.com/0xsigurd/Perturb
cd PerturbFor miner setup:
bash ./scripts/setup_common.sh minerFor validator setup:
bash ./scripts/setup_common.sh validatorsetup_common.sh behavior by role:
miner: creates.venv, installs Python/Bittensor dependencies onlyvalidator: also installs PM2, Ollama, startsperturb-ollama, and pullsPERTURB_LLM_ENDPOINT_MODEL
If npm: command not found, install Node.js first, then rerun:
macOS (Homebrew):
brew install node
node --version
npm --version
bash ./scripts/setup_common.sh validatorUbuntu/Debian:
sudo apt-get update
sudo apt-get install -y nodejs npm
node --version
npm --version
bash ./scripts/setup_common.sh validatorThis section is specifically for validator operators.
Create endpoint config:
cp scripts/llm_endpoint.env.example scripts/llm_endpoint.envEdit scripts/llm_endpoint.env:
LLM_ENDPOINT_HOST(default127.0.0.1)LLM_ENDPOINT_PORT(default8081)OLLAMA_URL(defaulthttp://127.0.0.1:11434)PERTURB_LLM_ENDPOINT_MODEL(defaultqwen2.5:1.5b-instruct)
Start llm_endpoint:
bash ./scripts/run_llm_endpoint.shHealth check:
curl "http://127.0.0.1:8081/health"Create validator env:
cp scripts/validator.env.example scripts/validator.envEdit required fields in scripts/validator.env:
WALLET_NAMEWALLET_HOTKEYNETUIDNETWORK
Important validator-specific fields:
PERTURB_IMAGE_ENDPOINTPERTURB_PEXELS_API_KEY(required)PERTURB_PEXELS_PER_PAGEPERTURB_PEXELS_PAGE_SPANPERTURB_PEXELS_IMAGE_VARIANT(medium,large,original, etc.)PERTURB_LLM_ENDPOINT_URL(must point to your running llm endpoint, e.g.http://127.0.0.1:8081/verify-label)PERTURB_LLM_ENDPOINT_MODELPERTURB_K_MINERSPERTURB_HISTORY_SIZEPERTURB_MIN_PROCESSED_COUNTPERTURB_MIN_LINF_DELTAPERTURB_MAX_LINF_DELTAPERTURB_WANDB_ENABLED(trueto enable validator metrics logging to Weights & Biases)PERTURB_WANDB_PROJECT,PERTURB_WANDB_ENTITY,PERTURB_WANDB_RUN_NAME,PERTURB_WANDB_MODEPERTURB_WANDB_LOG_CONSOLE(trueto forward validator console logs to W&B as well)LOG_LEVEL(DEBUGdefault, setINFO/WARNING/ERRORif you want quieter logs)
bash ./scripts/run_validator.shExpected log behavior:
- challenge generation messages
- miner selection messages
- per-miner score logs
- periodic
set_weightsattempts
- Verification is LLM-only by design; if llm_endpoint is down, challenge verification fails.
- Keep fallback image
assets/dog_1.jpgpresent for external image API outage handling.
This section is specifically for miner operators.
Create miner env:
cp scripts/miner.env.example scripts/miner.envEdit required fields in scripts/miner.env:
WALLET_NAMEWALLET_HOTKEYNETUIDNETWORK
Optional:
PYTHON_BINLOG_LEVEL(DEBUGdefault, setINFO/WARNING/ERRORif you want quieter logs)MINER_EXTRA_ARGS
bash ./scripts/run_miner.shExpected log behavior:
Serving miner axon...Miner started. Waiting for validator queries.
- Baseline miner is intentionally simple; competitive miners should optimize attack logic.
- Miner does not run llm_endpoint; semantic verification is validator-side only.
- Pexels endpoint:
GET https://api.pexels.com/v1/search - Required header:
Authorization: <PEXELS_API_KEY>(raw key, no Bearer prefix) - Validator sends params:
query,page,per_page - Validator reads
photos[].src.<variant>and downloads the selected image bytes - No custom
image_base64API response is required; validator converts downloaded image bytes to base64 internally.
- Endpoint:
POST /verify-label - Request JSON:
{
"prediction": "<efficientnet_label>",
"target_label": "<prompt_label>",
"llm_model": "<optional model hint>"
}- Response JSON must contain a boolean verdict key, typically:
{
"is_match": true,
"reason": "short explanation",
"method": "ollama"
}Operations endpoints:
GET /healthGET /metrics
Key fields sent to miners:
task_idmodel_name(fixedEfficientNetV2-M)prompt(broad label)clean_image_b64true_label(exact EfficientNet class label)epsilon,norm_type,min_delta,timeout_seconds
Miner response field:
perturbed_image_b64
Per-response score (if verification passes):
- Hard gates:
min_linf_delta <= norm <= min(epsilon, max_linf_delta)ssim(clean, adv) >= min_ssimpsnr_db(clean, adv) >= min_psnr_db- predicted label must differ from the original label
linf_ratio = clamp((norm - min_linf_delta) / (min(epsilon, max_linf_delta) - min_linf_delta), 0, 1)rmse_ratio = clamp(rmse / min(epsilon, max_linf_delta), 0, 1)linf_score = (1 - linf_ratio)^2rmse_score = (1 - rmse_ratio)^2perturbation_score = weighted_avg(linf_score, rmse_score)usingPERTURB_LINF_COMPONENT_WEIGHTandPERTURB_RMSE_COMPONENT_WEIGHTspeed_score = 1 - min(response_time / timeout, 1)final = PERTURB_PERTURBATION_WEIGHT * perturbation_score + PERTURB_SPEED_WEIGHT * speed_score
Any verification or constraint failure gets 0.0.
Weight setting:
- Only miners with
processed_count > 100are weight-eligible - Emission schedule: top-5 only with fixed shares
62%, 24%, 9%, 4%, 1%(ranks 6+ receive 0) - Final weights combine normalized rolling average and normalized rank bonus, then normalize to sum 1
Run after llm_endpoint is up:
python scripts/integration_smoke_test.pyThe smoke test validates:
- llm_endpoint health and semantic sanity checks
- image fetch from configured image endpoint
- local EfficientNetV2-M inference path
- challenge semantic verification through llm endpoint
- Validator fails verification loop: check
PERTURB_LLM_ENDPOINT_URLand llm_endpoint health. - Frequent image API failures: verify
PERTURB_IMAGE_ENDPOINTandPERTURB_PEXELS_API_KEY; fallback should loadassets/dog_1.jpg. - No miner scoring activity: ensure miner hotkeys are registered and publicly reachable.
- Dependency install issues: install CUDA/CPU-specific PyTorch build compatible with your host.
- Slow verifier responses: reduce model size or place llm_endpoint closer to validator process.
Use docs/READINESS_CHECKLIST.md before long-run validation or deployment.
neurons/validator.py: validator loop, challenge build, verification, scoring, set_weightsneurons/miner.py: baseline miner logic and Axon servingperturbnet/protocol.py:AttackChallengesynapse schemaperturbnet/model.py: EfficientNet model load and label prediction helpersperturbnet/image_io.py: base64 image encode/decode helperstools/llm_endpoint_service.py: validator-side semantic verification servicescripts/run_llm_endpoint.sh: start/restart llm endpoint with PM2 (auto-ensures Ollama + model)scripts/run_validator.sh: start/restart validator with PM2scripts/run_miner.sh: start/restart miner with PM2scripts/setup_common.sh: role-aware bootstrap (miner= Python deps only,validator= adds PM2/Ollama/model)scripts/integration_smoke_test.py: local integration test