Phase 1: substrate client + signer foundation for quip-miner

Summary

First MR of the v0.1 → v0.2 series that refactors quip-protocol (Python) into quip-miner (Substrate-integrated mining stack). This MR introduces the substrate-interface plumbing, an abstract Signer interface, and the SCALE decoders needed for the controller in later phases. No existing code paths are deleted — legacy Node/NetworkNode continues to work unchanged.

See PYTHON_MINER_INTEGRATION_PLAN.md (in v0.2 working tree, untracked) for the full multi-phase plan.

What's in this MR

  • shared/signer.py — abstract Signer base class plus Sr25519Signer (wraps substrate-interface's Keypair). Phase 7 will add HybridSigner for sr25519 + ML-DSA-44 once the ru/feature/hybrid-sig-consensus Rust branch lands.
  • shared/substrate_client.py — async wrapper around SubstrateInterface. Exposes:
    • get_head / get_finalized_head / get_block_number
    • get_mining_snapshot — calls state_call('QuantumPowApi_mining_snapshot', ...) at a pinned block hash and SCALE-decodes the result
    • query_miner, query_difficulty, query_balance
    • submit_extrinsic
    • subscribe_new_heads (no test yet; Phase 4 controller is first consumer)
  • shared/substrate_types.py — dataclasses mirroring the on-chain pallet types (SubstrateMiningContext, SubstrateDifficulty, MinerInfo, ExtrinsicReceipt) plus the canonical h_values = (-1.0, 0.0, 1.0) constant.
  • shared/substrate_submitter.py — encodes MiningResult + SubstrateMiningContext → QuantumProof (with float→milli conversion) and submits via QuantumPow.submit_proof.
  • shared/quantum_proof_of_work.py — adds derive_nonce(bytes, bytes, int, bytes) -> int, matching quantum_validation::derive_nonce byte-for-byte. Legacy ising_nonce_from_block retained as a one-line wrapper until Phase 5.
  • pyproject.toml — adds substrate-interface>=1.7.4 and scalecodec>=1.2; bumps version to 0.2.0-dev.

Verification

  • Cross-language parity: tests/test_derive_nonce_parity.py loads crates/quantum-validation/tests/fixtures/python_parity.json from the sibling quip-protocol-rs checkout and runs all 4 fixture cases. Set QUIP_RUST_FIXTURE_DIR to override the path. Test is skipped (not failed) if the fixture can't be found.
  • Live-chain integration: tests/test_substrate_client.py exercises 6 read paths against ws://localhost:9944. Auto-skipped if the chain isn't reachable. Verified against the running docker compose chain at block #1380+.
  • SCALE decoder unit tests: tests/test_mining_snapshot_decode.py covers the None case, a small populated snapshot, and a Z(9,2)-sized graph (1368 nodes / 1367 edges) that exercises the multi-byte compact-length path.
  • Full suite: 805 passed, 19 skipped, 0 failed (15 new + 790 pre-existing legacy tests still green).

Things worth a careful look

  1. Sr25519Signer.keypair is exposed so the submitter can pass it to substrate-interface's create_signed_extrinsic. Slightly leaky abstraction, but cleaner than reimplementing the extrinsic-signing wrapper at this stage — Phase 7 will tighten when HybridSigner lands.
  2. SubstrateClient.subscribe_new_heads is untested. First consumer is the Phase 4 controller, which will exercise it end-to-end.
  3. SubstrateClient.submit_extrinsic raises NotImplementedError for signature_kind != "Sr25519" so Phase 7's hybrid path is explicit.
  4. _coerce_receipt is defensive about substrate-interface's receipt API since attribute names have shifted across versions.
  5. _decode_mining_snapshot hand-rolls the SCALE decode rather than using scalecodec's RuntimeConfiguration route. The dependency is lighter and the schema is fixed, but worth flagging if you'd rather we go through the registry.

Out of scope (later phases)

  • register_miner extrinsic submission + faucet bot (Phase 2)
  • BaseMiner.mine_work_item extraction (Phase 3)
  • New-head-driven mining controller (Phase 4)
  • MinerCore extraction from Node, telemetry REST rewrite, P2P deletion sweep (Phase 5)
  • End-to-end verification against docker chain (Phase 6)
  • Hybrid sr25519 + ML-DSA-44 signing (Phase 7, blocked on Rust merge)
  • QuantumComputeMempool integration (Phase 8)

Merge request reports

Loading