fix: isolate proof submits onto a dedicated validator connection
Problem (gh-18 / QUI-829)
A colocated single-validator miner mined valid solutions but landed none — every submit_proof failed, proofs_won stuck, all health signals green.
The submit broadcast (pool_client.submit_signed_extrinsic) shared the ValidatorPool active handle with the get_mining_snapshot poll that runs on every new head. The single-URL pool fast-reconnects on any transient socket blip (rc43, 65a4fb4), so a snapshot-driven swap shut the active handle down while a submit was in flight, cancelling it. A submit spans the ~90s watch-timeout while the connection swaps every 1–2 min, so every submit crossed a swap and died; replay re-entered the same window → 0 wins.
Fix
- Route
submit_signed_extrinsicthrough a dedicated write handle (ValidatorPool.send_write) that read-path (snapshot) swaps never touch. - Submits are rare, so the write handle is created lazily and its idle/stale socket is health-reconnected on demand; writes serialize on a write lock. A connection failure during the submit still raises
ValidatorSwappedso the caller re-signs with a fresh nonce. - Add
pool.last_successful_submission, surfaced on/api/v1/status, so a node that mines but never lands a proof is diagnosable instead of silently reportingis_mining=true.
Tests
- 4 new tests in
test_validator_pool.py, includingtest_read_swap_does_not_teardown_inflight_writewhich reproduces the QUI-829 race (concurrent read swap while a submit is in flight) and asserts the submit survives. - 30 pool + 203 submit/controller/telemetry tests pass; ruff clean.
Closes gh-18 / QUI-829.