feat(stratum_v2): add SV2 support#65
Conversation
Add the stratum_v2 module with a channel-based StratumV2Client that handles DNS resolution, TCP connection, Noise_NX handshake, and the SetupConnection / OpenExtendedMiningChannel sequence. The event loop drives incoming SV2 frames, outgoing commands, and shutdown via a select! loop. Add StratumV2Error with an is_fatal() predicate to distinguish recoverable from unrecoverable protocol failures. Delegates all network and cryptographic plumbing to stratum-apps (v0.4, features = ["network", "core", "config"]) rather than hand-rolling Noise framing and async DNS.
Move ExponentialBackoff and ConnectOutcome out of stratum_v1.rs into a new job_source/connection.rs module so both the V1 and V2 sources can share the same implementation without copy-paste drift.
Parse pool URLs into a typed PoolEndpoint, detecting the protocol from the scheme and extracting the Base58Check-encoded authority public key required for the Noise_NX handshake in Stratum V2. New types: PoolProtocol (StratumV1 | StratumV2), PoolEndpoint with host, port, protocol and authority key, and ParseEndpointError with typed variants for each failure mode. PoolConfig gains an endpoint() convenience method. The authority public key field is private so the invariant — StratumV2 always carries a key, StratumV1 never does — cannot be violated by direct struct construction; callers use authority_pubkey() instead. Unrecognised URL schemes (stratum://, tcp://, etc.) now return UnsupportedScheme rather than silently producing a garbage host that fails only at DNS resolution time.
Add SourceEvent::SharesAccepted(u32) so SV2 sources can report pool-acknowledged shares back to the scheduler. SV1 has no per-batch ACK, so this variant will only be emitted by SV2 sources. ForcedRateSource passes the event through unchanged; it only modifies job templates. The scheduler increments a new shares_accepted counter on receipt and includes it in periodic status log output alongside shares_submitted.
Implement StratumV2Source, bridging StratumV2Client events into the scheduler's job-source abstraction. Converts NewExtendedMiningJob/ SetNewPrevHash pairs into JobTemplates using MerkleRootKind::Computed, manages the connection lifecycle with exponential back-off, and routes pool outcomes (shutdown, pool-requested reconnect, connection loss) through ClientOutcome. Future jobs are buffered as Option<_> and activated on a matching SetNewPrevHash; a SetNewPrevHash with no matching job emits ClearJobs. Share submission is stubbed pending further wiring.
Build and forward SubmitSharesExtended on every incoming share. Track submitted sequence numbers in a VecDeque; drain on SubmitSharesSuccess using RFC 1982 serial arithmetic and remove individually on SubmitSharesError. Validate channel_id on pool ACKs before mutating pending state. Emit SharesAccepted and SharesRejected so the scheduler can track pool-acknowledged and pool-rejected share counts.
Parse MUJINA_POOL_URL with PoolEndpoint::parse() to detect the pool protocol. stratum2+tcp:// URLs construct and spawn StratumV2Source; all other schemes fall through to the existing Stratum V1 path.
If task_to_job_full fails after current_task.replace(), the ntime ticker holds a broken task that can never produce valid work. Move the conversion ahead of the replace so a failure leaves current_task unchanged and the ticker is unaffected.
Spin up a real SV2 pool via integration_tests_sv2 behind a Sniffer proxy and assert on the full message exchange at each key protocol step: SetupConnection fields, Extended Channel open, job delivery, share acceptance and rejection, stale-share filtering, ClearJobs on a missing future job, and extranonce2 zero-padding to the pool's allocation. The clean_shutdown_before_connect test requires no external binaries and runs in the normal suite. The remaining tests download Bitcoin Core and the sv2-tp binary on first run (~200 MB, cached) and are marked #[ignore].
|
My recent tests had 100% shares accepted, and that's expected as we use the same I did some investigations and I suspect we suffer from the same issues that led to bitaxeorg/ESP-Miner#420 which was also triggered by esp-miner's implementation of SV2 support. Something for a separate discussion and implementation, but I believe has to happen given that allowing for higher hashrate is something that matters more for Mujina then it does for ESP-Miner. I was wondering if a share that fails |
Summary
This pull request introduces Stratum V2 support to Mujina, it's the result of #54 with guidance from Ryan and Plebhash.
The SV2 team has great crates like channels_sv2, sv2-apps, and so on, which made possible for Mujina to reuse the same APIs, behaviors, and patterns used to build their proxies, pools, and so on which will be Mujina's counterparties. In other words, we abstract away a big part of the stratum-related code like the Noise handshake, frame processing, state machine, and so on.
We ended up with a lot less domain-specific protocol code, and more dealing with the intricacies of a firmware like being shutdown-safe, having safe defaults and checks, interfacing and providing feedback to the user.
This pull request is in draft and open for comments, suggestions, etc. I will continue to edit it as I prepare it for a final review.
Steps to try it out
Assuming you have this repo cloned and checked out this pull request's branch. Just export the variables as we normally do for SV1 servers, but using the
stratum2+tcp://scheme and suffixing it with the authority key9auqWEzQDVyd2oe1JVGFLMLHZtCo2FFqZwtKA5gd9xbuEu7PH72as described in their spec.Then you can try with regular log level or run it with debug, that would help if you see any problems as you test.