Bittensor Integration
How Thirty Spokes operates as a Bittensor subnet: the epoch lifecycle, commit-reveal, scoring, the reign, and weight setting.
Registered as Subnet 99. Thirty Spokes is registered on Bittensor mainnet as netuid 99. The chain integration is SDK-verified against
bittensor10.x; secure enforce-mode operation still depends on the owner's measured image (see Attestation).
The epoch
One epoch = 100 blocks × 12 s ≈ 20 minutes. It is the subnet's scoring round.
Miners and validators are decoupled processes that never talk to each other — a miner runs benchmarks in its own TEE and uploads a proof to its own repo; a validator polls the chain and downloads it. So both sides must agree, from on-chain state alone, on which round it is and which tasks are scored:
epoch = current_block // 100
nonce = sha256("koth-epoch|{epoch}|{beacon}")[:16] # beacon = block hash at epoch start
seed = sha256("{nonce}|{epoch}|{benchmark}")[:8] # -> the exact task slice
Why the beacon matters. If the nonce came only from the epoch number it would be predictable arbitrarily far ahead, and a miner could pre-compute every future slice and memorise the answers. Seeding it from the block hash at epoch start — unknowable until that block is minted — is what makes static public benchmarks safe.
sequenceDiagram
participant C as Chain
participant M as Miner (own TEE)
participant R as Miner repo
participant V as Validator
M->>C: set_reveal_commitment(koth1|repo|rev|salt)
Note over C: reveal delay defeats front-running
loop Every epoch (100 blocks)
C-->>M: beacon = block hash at epoch start
C-->>V: beacon (same, derived independently)
M->>M: sample slice, run agent in-enclave, meter calls
M->>R: upload proof + trace
V->>C: read commits + owner governance record
V->>R: download bundle + proof
V->>V: verify quote, bind artifact, grade vs public gold
V->>V: grounding check, dedup, dethrone guard, reign
V->>C: set_weights()
end
At n_per_bench = 8, the full four-benchmark suite grades 32 tasks per miner (the live loader currently wires two — math + mmlu at 0.5 / 0.5 — so a live epoch grades 16). Every miner in an epoch runs the same slice, so scores are directly comparable.
Commit-reveal
A miner commits once, on-chain:
koth1|<hf_repo>|<revision>|sha256(source_hash|weights_hash|hotkey)
- The reveal delay (
blocks_until_reveal, default 360) means nobody can see your artifact hashes and front-run you. - The hotkey salt means nobody can copy your commit string and claim your artifact — the hash only verifies against your hotkey.
- The validator recomputes
source_hash/weights_hashfrom the bundle it downloads itself and checks them against the commit. The binding is enforced validator-side, never taken on the miner's word.
Per-epoch validator loop
- Read the owner's governance record from chain (approved MRTD/RTMR set, TCB policy, pool allow-list,
probe_commit). Rotation therefore takes effect at the next epoch boundary. - Read all revealed commitments; for each, download the public bundle and bind it.
- Verify the attested proof; grade the attested answers against public gold.
- Grounding check (default) — every scored answer must derive from a logged pool call, else
ungrounded; the behavioural fingerprint is the attested answer vector. No inference. (Opt-in--probe-bank: re-execute a secret held-out probe →memorization.) - Behavioural dedup → Pareto dethrone guard →
KingChain. set_weights()on Substrate.
Scoring is a lookup over attested reports, so validator cost is flat in the number of miners. A validator does no agent inference and needs no GPU.
Weights and the reign
Up to 5 slots — the king plus the last 4 ex-kings still registered — share emissions equally:
| Paid slots | Share each |
|---|---|
| 5 (king + 4 ex-kings) | 20 % |
| fewer | 1 / paid slots — bigger each |
| 1 (king only) | 100 % |
The reward for climbing is entering the chain at all — the only door in is taking the crown — so the gradient lives at the coronation, not in graduated slot weights.
- Incumbency margin. Only the king's slot is contested. A challenger must beat the king by
ε, which decays with the king's artifact age — a stale king is easier to unseat; a fresh one is protected from noise:eps(age) = 0.002 + (0.02 - 0.002) * exp(-age / 8) - Tiebreak: exact ties for the crown go to the earliest commit block.
- Burn: weight burns to uid 0 only if nobody in the chain — and no hotkey the validator can map to a uid — is still registered; a deregistered ex-king's share is re-split among the rest, not burned.
Chain surface
The integration is deliberately tiny, and every method below was verified against the installed bittensor 10.5.0 SDK (the 8.x → 10.x rename and return-shape changes are handled):
| Operation | SDK call |
|---|---|
| Register | burned_register(wallet, netuid) |
| Commit | set_reveal_commitment(wallet, netuid, data, blocks_until_reveal) |
| Read commits | get_all_revealed_commitments(netuid) → {hotkey: ((block, data), …)} |
| Metagraph (uid ↔ hotkey) | metagraph(netuid) |
| Epoch beacon | get_block_hash(epoch × epoch_blocks) |
| Set weights | set_weights(wallet, netuid, uids, weights, version_key) |
| Owner governance | published + read as a commitment under the owner hotkey |
Validator state — the reign standings and the king's dethrone-guard baseline — is persisted (--state), so a restart does not reset incumbency history or silently disable the Pareto guard for an epoch.
Resource requirements
| Role | Requirement |
|---|---|
| Validator | No GPU. CPU-only; verifies a quote and grades text. Modest VPS. |
| Miner | A confidential VM (Intel TDX). No GPU — the pool models are remote. 2–4 vCPU is enough. Cheap on Spot. |
Miners pay their own inference (their own OpenRouter key). See Confidential VM Setup.
Running the daemons
# miner — on your confidential VM
orchestra-koth-miner --netuid 99 --network finney --wallet miner --repo you/koth-miner \
--source my_router.py --weights my_weights.bin \
--pool "<owner-published allow-list>" --confine
# validator — anywhere, CPU-only
orchestra-koth-validator --netuid 99 --network finney --wallet validator \
--state ./validator_state.json
The validator refuses to start until the owner has published the approved-measurement set on-chain. --insecure bypasses that for testnet only and provides no security.
By default the validator runs the grounding check (no inference, no probe bank needed). Three refinements are landed but off by default, enabled per validator once calibrated on testnet:
--probe-bank ./bank.json— switch the memorisation backstop to the opt-in re-execution probe (audit_mode="probe"), matching the owner's on-chainprobe_commit.--commit-window <N>— F7 anti-grind: require each proof committed on-chain within N blocks of the epoch open (the miner daemon commits automatically). Off by default; calibrate N to one benchmark run's wall-clock first.--grace-blocks <N>— F2: score an epoch only N blocks after it opens, so submissions settle and decoupled validators agree. Keep N < 100 (the epoch length).
Status
| Item | Status |
|---|---|
| Epoch / nonce / beacon derivation | Implemented |
| Commit-reveal (salted, hotkey-bound) | Implemented |
| Reign, ε hysteresis, burn-to-uid-0 | Implemented |
| Grounding check (default) + opt-in probe / accumulate / F7 / F2 | Implemented |
| On-chain governance read per epoch | Implemented |
Validator restart-safety (--state) |
Implemented |
BittensorChain vs bittensor 10.x |
SDK-verified |
| Mainnet registration | Registered — Subnet 99 (netuid 99) |
See also
- The KOTH Protocol — proof format, grading, the dethrone guard
- Attestation & Trust Model — the governance record and enforce mode