A production-grade Stratum V2 Job Declarator Client in Rust that enables individual miners to:
- Select transactions from their own Bitcoin node
- Declare custom mining jobs to a pool
- Negotiate via the Stratum V2 protocol with full Noise encryption
Three independent actors communicate via tokio::broadcast channels:
βββββββββββββββββββ
β Node Actor β Polls Bitcoin Core RPC
β (Bitcoin RPC) β Creates block templates
ββββββββββ¬βββββββββ Sends to pool
β
βΌ
ββββββββββ
β Msgs β βββββββββββ
ββββββ¬ββββ β
β β
ββββββΌβββββββββ β
β Pool Actor ββββββββ Manages SV2 protocol
β (SV2 Proto) β Noise NX handshake
βββββββββββββββ Encrypted channel
β
βΌ
ββββββββββ
β Msgs β
ββββββ¬ββββ
β
ββββββΌβββββββββ
β UI Actor β Terminal dashboard
β (ratatui) β Real-time stats
βββββββββββββββ Event log
Stratum_V2/
βββ Cargo.toml # Dependencies & metadata
βββ config.toml # Runtime configuration
βββ .gitignore # Git exclusions
βββ start.sh # Quick start script
β
βββ ARCHITECTURE.md # System design overview
βββ NOISE_HANDSHAKE.md # Noise NX deep dive
βββ PRODUCTION_PATTERNS.md # Rust best practices
β
βββ src/
βββ main.rs # Entry point & orchestration
β
βββ common/
β βββ mod.rs # Module exports
β βββ error.rs # JdcError types
β βββ types.rs # AppMessage, AppStats
β
βββ node/
β βββ mod.rs # NodeActor (Bitcoin RPC)
β
βββ pool/
β βββ mod.rs # PoolActor (SV2 protocol)
β
βββ ui/
βββ mod.rs # UiActor (Terminal UI)
1. Node Actor (src/node/mod.rs)
Responsibilities:
- Connect to Bitcoin Core RPC
- Poll
getblocktemplateevery N seconds - Extract transactions and fees
- Broadcast
NewBlockTemplateevents - Send
SendJobDeclarationto pool
Error Handling:
JdcError::BitcoinRpcfor connection failures- Automatic reconnection logic
- No unwrap() calls
2. Pool Actor (src/pool/mod.rs)
Responsibilities:
- Establish TCP connection to pool
- Perform Noise NX handshake
- Maintain encrypted SV2 channel
- Send
DeclareMiningJobmessages - Process pool responses
State Machine:
Disconnected β Connected β InitiatorSent β Complete
Noise Handshake Flow:
- Generate ephemeral keypair
- Send
-> e(ephemeral pubkey) - Receive
<- e, ee, s, es - Derive transport keys
- Enter encrypted mode
3. UI Actor (src/ui/mod.rs)
Responsibilities:
- Render terminal dashboard with
ratatui - Display connection status
- Show real-time statistics
- Maintain event log
- Handle user input (quit on 'q')
Displayed Stats:
- Node/Pool connection status
- Current block height
- Templates created
- Jobs declared/accepted/rejected
- Total fees collected
- Acceptance rate
4. Message System (src/common/types.rs)
AppMessage enum:
pub enum AppMessage {
// Node events
NodeConnected,
NewBlockTemplate { height, tx_count, total_fees },
// Pool events
HandshakeComplete,
JobAccepted { template_id, token },
JobRejected { template_id, reason },
// Control
Shutdown,
}Why broadcast channel:
- One-to-many fanout
- Multiple actors subscribe to same events
- UI gets all events for display
- Pool gets job declarations from node
5. Error Handling (src/common/error.rs)
Zero-unwrap philosophy:
#[derive(Error, Debug)]
pub enum JdcError {
#[error("Bitcoin RPC error: {0}")]
BitcoinRpc(#[from] bitcoincore_rpc::Error),
#[error("Noise handshake error: {0}")]
NoiseHandshake(String),
// ... 10 total variants
}Every operation returns Result<T, JdcError> - no panics in production.
See NOISE_HANDSHAKE.md for full details.
NX Pattern: No static key for initiator, responder has static key
Initiator (JDC) Responder (Pool)
β β
ββββββββ e βββββββββββββββ (ephemeral key)
β β
βββββ e, ee, s, es βββββββ (encrypted static key)
β β
[Derive transport keys] [Derive transport keys]
β β
ββββ Encrypted Channel βββ
After handshake:
- All messages encrypted with ChaCha20-Poly1305
- Forward secrecy via ephemeral keys
- Pool authenticated via static key
- No MITM possible without static key
- Rust toolchain (1.75+)
- Running Bitcoin Core node
- Stratum V2 pool address
Edit config.toml:
[bitcoin_node]
rpc_url = "http://127.0.0.1:8332"
rpc_user = "your_user"
rpc_password = "your_password"
[pool]
address = "pool.example.com:34254"
[jdc]
coinbase_outputs = [
{ value = 0, script_pubkey = "76a914...88ac" }
]# Quick start
./start.sh
# Or manually
cargo build --release
cargo run --release- 'q' or ESC - Quit application
- Automatically updates every 100ms
- Complete actor architecture
- Message passing system
- Bitcoin Core RPC integration
- Block template polling
- TCP connection to pool
- Full Noise NX handshake
- Encrypted channel establishment
- Terminal UI dashboard
- Real-time statistics
- Event logging
- Configuration system
- Error handling (zero unwrap)
- Graceful shutdown
- Full SV2 message encoding (
DeclareMiningJob) - Transaction short ID calculation
- Merkle proof generation
- Mining job token management
-
AllocateMiningJobTokenhandling -
IdentifyTransactionsresponse - Transaction fee optimization
- Reconnection with state recovery
The handshake is fully functional - the encrypted channel is established correctly. What remains is encoding the actual SV2 protocol messages.
-
Implement
DeclareMiningJobencoding:use job_declaration_sv2::DeclareMiningJob; let job = DeclareMiningJob { request_id: template_id, mining_job_token: token, version: template.version, // ... full structure }; let encoded = job.serialize()?;
-
Add transaction short ID calculation:
fn calculate_short_id(tx: &Transaction, k0: u64, k1: u64) -> u64 { // SipHash-2-4 with pool's keys }
-
Build Merkle proofs:
fn build_merkle_proof(txs: &[Txid], index: usize) -> Vec<[u8; 32]> { // Merkle branch for coinbase commitment }
-
No Shared Mutable State
- Each actor owns its data
- Communication via messages only
- Compiler proves thread safety
-
Zero-Unwrap Policy
- Every
Resulthandled with? - Typed errors via
thiserror - No panics in production code
- Every
-
Trait-Based Abstractions
- Prefer traits over inheritance
- Easy to mock for testing
- Zero-cost abstractions
-
RAII Resource Management
- Terminal cleanup in Drop
- No resource leaks
- Graceful error recovery
-
Async-First Design
tokiofor all I/O- Non-blocking throughout
- Efficient resource usage
- ARCHITECTURE.md - System design, actor pattern, message flow
- NOISE_HANDSHAKE.md - Complete Noise NX explanation
- PRODUCTION_PATTERNS.md - Rust best practices used
Core SV2:
noise_sv2- Noise Protocol Frameworkframing_sv2- SV2 frame encodingcodec_sv2- Message serializationbinary_sv2- Binary primitives
Bitcoin:
bitcoincore-rpc- Bitcoin Core RPC client
Async:
tokio- Async runtimetokio-util- Codec utilities
UI:
ratatui- Terminal UI frameworkcrossterm- Terminal control
Utils:
thiserror- Error derive macrostracing- Structured loggingconfig- Configuration management
- Robust Error Handling - All failure modes explicitly handled
- Structured Logging - Debug production issues easily
- Configuration Management - TOML files + environment variables
- Graceful Shutdown - Clean resource cleanup
- Type Safety - Compiler-verified correctness
- Zero Data Races - Proven by borrow checker
- Memory Safety - No unsafe code needed
This codebase demonstrates:
- Actor pattern in Rust
- Async/await with Tokio
- Error handling best practices
- Cryptographic protocols (Noise)
- Terminal UI development
- Zero-copy optimizations
- Production Rust patterns
To complete the implementation:
- Study the Stratum V2 Job Declaration spec
- Implement message encoding using
codec_sv2 - Test against a real SV2 pool
- Add transaction selection strategies
- Implement job tracking and metrics
Status: Foundation complete, ready for protocol message implementation.
Quality: Production-grade architecture with proper error handling, logging, and documentation.