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— abstractSignerbase class plusSr25519Signer(wrapssubstrate-interface'sKeypair). Phase 7 will addHybridSignerfor sr25519 + ML-DSA-44 once theru/feature/hybrid-sig-consensusRust branch lands.shared/substrate_client.py— async wrapper aroundSubstrateInterface. Exposes:get_head/get_finalized_head/get_block_numberget_mining_snapshot— callsstate_call('QuantumPowApi_mining_snapshot', ...)at a pinned block hash and SCALE-decodes the resultquery_miner,query_difficulty,query_balancesubmit_extrinsicsubscribe_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 canonicalh_values = (-1.0, 0.0, 1.0)constant.shared/substrate_submitter.py— encodesMiningResult + SubstrateMiningContext → QuantumProof(with float→milli conversion) and submits viaQuantumPow.submit_proof.shared/quantum_proof_of_work.py— addsderive_nonce(bytes, bytes, int, bytes) -> int, matchingquantum_validation::derive_noncebyte-for-byte. Legacyising_nonce_from_blockretained as a one-line wrapper until Phase 5.pyproject.toml— addssubstrate-interface>=1.7.4andscalecodec>=1.2; bumps version to0.2.0-dev.
Verification
- Cross-language parity:
tests/test_derive_nonce_parity.pyloadscrates/quantum-validation/tests/fixtures/python_parity.jsonfrom the siblingquip-protocol-rscheckout and runs all 4 fixture cases. SetQUIP_RUST_FIXTURE_DIRto override the path. Test is skipped (not failed) if the fixture can't be found. - Live-chain integration:
tests/test_substrate_client.pyexercises 6 read paths againstws://localhost:9944. Auto-skipped if the chain isn't reachable. Verified against the runningdocker composechain at block #1380+. - SCALE decoder unit tests:
tests/test_mining_snapshot_decode.pycovers theNonecase, 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
Sr25519Signer.keypairis exposed so the submitter can pass it tosubstrate-interface'screate_signed_extrinsic. Slightly leaky abstraction, but cleaner than reimplementing the extrinsic-signing wrapper at this stage — Phase 7 will tighten whenHybridSignerlands.SubstrateClient.subscribe_new_headsis untested. First consumer is the Phase 4 controller, which will exercise it end-to-end.SubstrateClient.submit_extrinsicraisesNotImplementedErrorforsignature_kind != "Sr25519"so Phase 7's hybrid path is explicit._coerce_receiptis defensive aboutsubstrate-interface's receipt API since attribute names have shifted across versions._decode_mining_snapshothand-rolls the SCALE decode rather than usingscalecodec'sRuntimeConfigurationroute. 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_minerextrinsic submission + faucet bot (Phase 2)BaseMiner.mine_work_itemextraction (Phase 3)- New-head-driven mining controller (Phase 4)
MinerCoreextraction fromNode, 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)
QuantumComputeMempoolintegration (Phase 8)