# quip-miner

> **Experimental software.** Use at your own risk. No production warranties.

A Python mining stack for the [quip-protocol-rs](https://gitlab.com/quip.network/quip-protocol-rs) Substrate chain. Drives CPU SA, GPU (CUDA / Metal / Modal), and QPU (D-Wave) miners against the chain's `QuantumPow` pallet — fetch the mining snapshot at each new chain head, search for valid Ising solutions, submit `QuantumPow.submit_proof` extrinsics, repeat.

This is the `v0.2` line of the repository (formerly `quip-protocol`). In `v0.1` this codebase shipped its own consensus, P2P (QUIC), block store, REST API, and SPHINCS+ block signer. `v0.2` removes all of that — the chain is the source of truth, miners attach to it.

## Architecture

```
                              chain (substrate)
                                     │
                              ws://localhost:9944
                                     │
                  ┌──────────────────┴──────────────────┐
                  │       SubstrateClient (read)         │
                  │   - get_mining_snapshot              │
                  │   - subscribe_new_heads              │
                  │   - submit_extrinsic                 │
                  └──────────────────┬──────────────────┘
                                     │
                  ┌──────────────────┴──────────────────┐
                  │  SubstrateMinerController            │
                  │   - on new head: cancel + fetch +    │
                  │     dispatch                         │
                  │   - on result: encode + submit       │
                  │   - classify receipts                │
                  └──────┬────────────────────┬──────────┘
                         │                    │
                  ┌──────┴──────┐      ┌──────┴──────┐
                  │ MinerCore   │      │ TelemetryApi│
                  │ - handles[] │      │ /api/v1/*   │
                  │ - stats     │      └─────────────┘
                  │ - descriptor│
                  └──────┬──────┘
                         │
                  MinerHandle (per worker process)
                         │
                  BaseMiner.mine_work_item
                  (CPU SA / GPU CUDA|Metal|Modal / QPU)
```

Component responsibilities (`shared/`):

| module | role |
|--------|------|
| `signer.py` | Abstract `Signer` + `Sr25519Signer`. Phase 7 adds `HybridSigner` (sr25519 + ML-DSA-44). |
| `keystore.py` | sr25519 keystore (`0o600` JSON; plaintext seed for dev). |
| `substrate_client.py` | `py-substrate-interface` async wrapper; `state_call` for the mining snapshot. |
| `substrate_types.py` | `SubstrateMiningContext`, `SubstrateDifficulty`, `MinerInfo`, `ExtrinsicReceipt`. |
| `substrate_submitter.py` | `MiningResult` → `QuantumProof` SCALE encoding + submission. |
| `substrate_miner_controller.py` | Head subscription, snapshot fetch, dispatch, receipt classification. |
| `miner_core.py` | Owns persistent `MinerHandle` workers, hardware descriptor cache, aggregate stats. |
| `miner_bootstrap.py` | Idempotent fund + register pipeline. |
| `telemetry_api.py` | HTTP REST surface (`/api/v1/status`, `/system`, `/stats`, `/block/*`). |
| `base_miner.py` | Protocol-neutral `mine_work_item(context, stop_event)` loop. |
| `miner_worker.py` | 2-process worker scaffolding (parent ↔ child mp.Queue + stop_event). |
| `quantum_proof_of_work.py` | `derive_nonce`, `generate_ising_model_from_nonce`, `evaluate_sampleset`. |

Standalone scripts at repo root:

| file | role |
|------|------|
| `quip_cli.py` | `quip-miner` CLI dispatch (keygen / bootstrap / cpu / gpu / qpu). |

## Installation

```bash
python3 -m venv .quip
source .quip/bin/activate
pip install -U pip setuptools wheel
pip install -e .
```

Dependencies pulled in by `pyproject.toml`:
- `substrate-interface>=1.7.4`, `scalecodec>=1.2` — chain RPC + SCALE
- `dwave-ocean-sdk>=9.0.0,<10`, `numpy>=1.24.0` — Ising sampling
- `aiohttp>=3.9.0` — telemetry server + faucet
- `click>=8.1.7` — CLI
- `blake3>=1.0.5` — nonce derivation

D-Wave QPU access requires `DWAVE_API_KEY` in `.env` (loaded via `python-dotenv`).

## Quick start

In one terminal, bring up the chain:

```bash
cd ../quip-protocol-rs
docker compose up -d
docker compose logs -f node1   # confirm blocks being produced
```

The dev faucet now lives in its own repository (`gitlab.com/quip.network/faucet`);
local-network setup (node + faucet + chain seeding) is handled by the testing
repo at `nodes.quip.network`. Point the miner at a running faucet with
`--faucet-url`; it self-funds and self-registers on first run.

Bootstrap a miner account (generates a keystore, funds it via the faucet, sudo-seeds `Difficulty` + `DefaultTopology` on a fresh chain, then submits `register_miner`):

```bash
quip-miner bootstrap \
    --node-url ws://localhost:9944 \
    --faucet-url http://127.0.0.1:8087 \
    --seed-chain
```

Run the miner:

```bash
quip-miner cpu \
    --node-url ws://localhost:9944 \
    --num-cpus 4 \
    --topology zephyr:9,2 \
    --rest-port 8086
```

In a third terminal, watch chain events for `QuantumPow.ProofAccepted` (via [polkadot.js](https://polkadot.js.org/apps/#/explorer) pointed at `ws://localhost:9944`), or hit the local telemetry API:

```bash
curl http://localhost:8086/api/v1/status   | jq
curl http://localhost:8086/api/v1/stats    | jq
curl http://localhost:8086/api/v1/system   | jq
```

## CLI reference

### `quip-miner keygen`

Generate a fresh sr25519 signing key. Writes a `0o600` JSON keystore with the seed in plaintext (passphrase-encrypted keystores ship in Phase 7).

```
quip-miner keygen --out ~/.quip-miner/signing.json
```

### `quip-miner bootstrap`

Idempotent setup: generate keystore (if missing) → request funds from the faucet → submit `register_miner`. With `--seed-chain`, also sudo-submits `set_difficulty` + `register_topology` if missing.

```
quip-miner bootstrap \
    --node-url ws://localhost:9944 \
    --signer-key ~/.quip-miner/signing.json \
    --faucet-url http://127.0.0.1:8087 \
    --seed-chain \
    --seed-topology 9,2
```

Re-runs are no-ops that just verify state.

### `quip-miner cpu | gpu | qpu`

Run the mining controller. All three subcommands share these flags:

- `--node-url ws://...` (required) — substrate WS endpoint
- `--signer-key ~/.quip-miner/signing.json` — keystore path
- `--topology zephyr:M,T` — sampler topology (defaults to `zephyr:9,2`)
- `--rest-port 8086` — HTTP telemetry port (`-1` disables)

The `cpu` subcommand adds `--num-cpus N`; `gpu` adds `--gpu-backend {local,metal,modal}`; `qpu` adds `--qpu-type` and `--daily-budget`.

Topology binding is enforced at startup: the CLI hashes the configured topology with the same `blake2_256(SCALE((sorted_nodes, canonical_edges)))` recipe the chain uses, and refuses to start if the hash doesn't match the chain's registered topology.

## Telemetry REST API

```
GET  /health
GET  /api/v1/status                        chain head + miner identity + is_mining
GET  /api/v1/system                        hardware descriptor (cached)
GET  /api/v1/stats                         aggregate MinerCore + controller stats
GET  /api/v1/block/latest                  substrate-fetched chain head
GET  /api/v1/block/{n}                     substrate-fetched block by number
GET  /api/v1/block/{n}/header              header subset
POST /api/v1/solve                         disabled in v0.2 (was direct DWave sample)
```

Response envelope: `{"success": bool, "data": ..., "error": ..., "timestamp": int}`.

The legacy `/api/v1/peers`, `/api/v1/join`, `/api/v1/gossip`, `/api/v1/heartbeat`, and `POST /api/v1/block` paths are removed — they were P2P / consensus surfaces with no equivalent in substrate mode. The legacy `/telemetry/*` SSE stream and per-peer aggregator also moved out; consumers should switch to substrate-side events and Prometheus (`http://localhost:9615/metrics`).

## Topology

Mining works against any `BoundedVec`-bounded graph registered on chain (`QuantumPow.RegisteredTopologies`). The CLI's `--topology zephyr:M,T` constructs a [dwave-networkx](https://docs.ocean.dwavesys.com/projects/dwave-networkx/) Zephyr graph; the chain's `pallets/quantum-pow/src/topology.rs::hash_topology` canonicalizes and `blake2_256`-hashes the result.

`bootstrap --seed-chain --seed-topology 9,2` registers Zephyr Z(9,2) (1368 nodes / 7692 edges — the legacy default that matches the chain's difficulty calibration of `max_energy_milli=-2_500_000`). Smaller graphs (Z(2,2), Z(3,2)) work too but need their own difficulty calibration since their ground-state energy range is much narrower.

## Running tests

```bash
python -m pytest tests/ -v
```

Integration tests against the docker chain auto-skip if `ws://localhost:9944` isn't reachable. The end-to-end controller test (`test_controller_submits_proof_end_to_end`) bootstraps inline and asserts at least one `QuantumPow.ProofAccepted` event lands within 120 seconds.

The cross-language nonce-parity test (`test_derive_nonce_parity.py`) reads `crates/quantum-validation/tests/fixtures/python_parity.json` from a sibling `quip-protocol-rs` checkout. Set `QUIP_RUST_FIXTURE_DIR` if your checkout is elsewhere.

## What changed from v0.1

Removed entirely:

- The local blockchain stack: `shared/block.py`, `shared/node.py`, `shared/network_node.py`, `shared/block_store.py`, `shared/block_synchronizer.py`, `shared/block_requirements.py`, `genesis_block_public.json`.
- The P2P stack: QUIC client/server, SWIM failure detector, peer scorer / ban list, gossip telemetry aggregator, sync wire codecs.
- The legacy signing path: SPHINCS+ block signer + certificate manager.
- CLI: `quip-network-node` and `quip-network-simulator` are gone. Use `quip-miner` instead.

Kept (with rewired backends):

- `/api/v1/status`, `/system`, `/stats`, `/block/*` (now substrate-backed)
- The 2-process worker model (`MinerHandle` ↔ child mp.Queue + stop_event)
- The Ising sampling code (`BaseMiner.mine_work_item`, `quantum_proof_of_work.*`, CPU/GPU/QPU subclasses)
- The hardware descriptor / aggregate stats (now exposed via `MinerCore`)

Migration:

- A `v0.1` miner that ran a single mining node now runs a chain node (quip-protocol-rs docker compose) + `quip-miner cpu|gpu|qpu` against it.
- Mining rewards now accrue on chain to the registered sr25519 account, not as v0.1 block-proposer credit.

## License

AGPL-3.0-or-later. See LICENSE.
