Pool Operator Guide

This guide covers setting up and running a Zcash Stratum V2 mining pool.

Architecture

                                    ┌─────────────────────────────────┐
                                    │         Zebra Node              │
                                    │    http://127.0.0.1:8232        │
                                    └──────────────┬──────────────────┘


┌─────────────────────────────────────────────────────────────────────────────────┐
│                              Pool Infrastructure                                 │
│                                                                                 │
│  ┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐          │
│  │ Template Provider│───▶│   Pool Server    │◀───│   JD Server      │          │
│  │ (fetches blocks) │    │   (port 3333)    │    │   (port 3334)    │          │
│  └──────────────────┘    └────────┬─────────┘    └────────┬─────────┘          │
│                                   │                       │                     │
│                                   ▼                       ▼                     │
│                          ┌──────────────────┐    ┌──────────────────┐          │
│                          │ Share Processor  │    │  Token Manager   │          │
│                          └────────┬─────────┘    └──────────────────┘          │
│                                   │                                             │
│                                   ▼                                             │
│                          ┌──────────────────┐                                   │
│                          │  Payout Tracker  │                                   │
│                          │     (PPS)        │                                   │
│                          └──────────────────┘                                   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

                    ┌───────────────────┼───────────────────┐
                    │                   │                   │
                    ▼                   ▼                   ▼
            ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
            │   Miners    │     │   Miners    │     │ JD Clients  │
            │  (direct)   │     │  (direct)   │     │(decentralized)│
            └─────────────┘     └─────────────┘     └─────────────┘

Quick Start

1. Prerequisites

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Clone repository
git clone https://github.com/iqlusioninc/stratum-zcash
cd stratum-zcash

# Build all components
cargo build --release

2. Setup Zebra Node

Ensure Zebra is running with RPC enabled:

# zebrad.toml
[rpc]
listen_addr = "127.0.0.1:8232"

[network]
network = "Mainnet"

3. Generate Noise Keypair

cargo run --release -p bedrock-noise --example generate_keys

# Output:
# Private key: 7a8b9c...  (KEEP SECRET)
# Public key:  a1b2c3...  (share with miners)

4. Configure Pool

Create pool.toml:

[server]
listen_addr = "0.0.0.0:3333"
jd_listen_addr = "0.0.0.0:3334"

[zebra]
url = "http://127.0.0.1:8232"

[mining]
initial_difficulty = 1.0
target_shares_per_minute = 5.0
nonce_1_len = 4

[payout]
pool_address = "t1YourPoolPayoutAddress"
pool_fee_percent = 1.0

[security]
noise_enabled = true
noise_private_key = "7a8b9c..."
max_connections = 10000

[jd]
enabled = true
full_template_enabled = true
full_template_validation = "Standard"
token_lifetime_secs = 300

5. Run Pool

cargo run --release -p zcash-pool-server -- --config pool.toml

Configuration Reference

Server Settings

OptionDefaultDescription
listen_addr0.0.0.0:3333Miner connection endpoint
jd_listen_addr0.0.0.0:3334JD Client endpoint
max_connections10000Maximum concurrent miners

Zebra Settings

OptionDefaultDescription
urlhttp://127.0.0.1:8232Zebra RPC endpoint
poll_interval_ms500Template polling interval

Mining Settings

OptionDefaultDescription
initial_difficulty1.0Starting share difficulty
min_difficulty0.001Minimum difficulty floor
max_difficulty1000000.0Maximum difficulty ceiling
target_shares_per_minute5.0Vardiff target
nonce_1_len4NONCE_1 size (bytes)
validation_threads4Equihash validation threads

Payout Settings

OptionDefaultDescription
pool_addressRequiredPool payout address
pool_fee_percent1.0Pool fee percentage
min_payout0.01Minimum payout threshold (ZEC)
payout_interval_hours24Payout frequency

Security Settings

OptionDefaultDescription
noise_enabledfalseEnable Noise encryption
noise_private_keyNonePool’s Noise private key (hex)
require_noisefalseReject unencrypted connections

JD Server Settings

OptionDefaultDescription
enabledtrueEnable Job Declaration
full_template_enabledfalseAllow Full-Template mode
full_template_validationStandardValidation level
token_lifetime_secs300Token expiration time
max_tokens_per_client10Max active tokens per client
coinbase_output_max_additional_size256Max extra coinbase bytes

Job Declaration Support

Coinbase-Only Mode

Default mode where miners customize their coinbase but pool provides transactions.

Configuration:

[jd]
enabled = true
full_template_enabled = false

Validation:

  • Pool verifies coinbase includes pool payout
  • Pool provides transaction set

Full-Template Mode

Advanced mode where miners select their own transactions.

Configuration:

[jd]
enabled = true
full_template_enabled = true
full_template_validation = "Standard"  # or "Minimal" or "Strict"
min_pool_payout = 10000  # zatoshis

Validation Levels:

LevelChecksPerformance
MinimalPool payout onlyFastest
StandardPayout + known txids, request missingBalanced
StrictFull transaction validationSlowest

Monitoring

Prometheus Metrics

The pool exposes metrics on port 9090:

# prometheus.yml
scrape_configs:
  - job_name: 'zcash-pool'
    static_configs:
      - targets: ['localhost:9090']

Key Metrics:

# Connections
pool_connections_active
pool_connections_total
pool_connections_noise_total

# Mining
pool_shares_accepted_total
pool_shares_rejected_total{reason="stale|duplicate|invalid|low_diff"}
pool_blocks_found_total
pool_hashrate_estimated

# JD Server
jd_tokens_allocated_total
jd_jobs_declared_total
jd_full_template_jobs_total

# Performance
pool_share_validation_duration_seconds
pool_template_age_seconds

Logging

Configure structured logging:

[logging]
level = "info"  # debug, info, warn, error
format = "json"  # or "pretty"
file = "/var/log/zcash-pool/pool.log"

Alerts

Recommended alerts:

# Alertmanager rules
groups:
  - name: zcash-pool
    rules:
      - alert: HighStaleShareRate
        expr: rate(pool_shares_rejected_total{reason="stale"}[5m]) / rate(pool_shares_accepted_total[5m]) > 0.1
        for: 5m

      - alert: NoBlocksFound
        expr: increase(pool_blocks_found_total[24h]) == 0
        for: 1h

      - alert: ZebraDisconnected
        expr: pool_zebra_connected == 0
        for: 1m

      - alert: HighTemplateAge
        expr: pool_template_age_seconds > 30
        for: 5m

High Availability

Load Balancing

Run multiple pool instances behind a load balancer:

                    ┌─────────────────────┐
                    │    HAProxy/Nginx    │
                    │    (TCP mode)       │
                    └──────────┬──────────┘

           ┌───────────────────┼───────────────────┐
           │                   │                   │
           ▼                   ▼                   ▼
    ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
    │ Pool Node 1 │     │ Pool Node 2 │     │ Pool Node 3 │
    └─────────────┘     └─────────────┘     └─────────────┘
           │                   │                   │
           └───────────────────┼───────────────────┘

                    ┌──────────▼──────────┐
                    │   Shared Redis      │
                    │  (share dedup)      │
                    └─────────────────────┘

HAProxy config:

frontend stratum
    bind *:3333
    mode tcp
    default_backend pool_nodes

backend pool_nodes
    mode tcp
    balance leastconn
    option tcp-check
    server pool1 10.0.0.1:3333 check
    server pool2 10.0.0.2:3333 check
    server pool3 10.0.0.3:3333 check

Share Deduplication

For multiple pool instances, use Redis for share deduplication:

[dedup]
enabled = true
redis_url = "redis://10.0.0.100:6379"
window_seconds = 60

Zebra Redundancy

Run multiple Zebra nodes:

[zebra]
urls = [
    "http://10.0.0.10:8232",
    "http://10.0.0.11:8232",
    "http://10.0.0.12:8232"
]
failover_timeout_ms = 5000

Security Hardening

Network Security

# Firewall rules
sudo ufw allow 3333/tcp   # Stratum
sudo ufw allow 3334/tcp   # JD Server
sudo ufw deny 8232/tcp    # Block external Zebra access

# Rate limiting (iptables)
sudo iptables -A INPUT -p tcp --dport 3333 -m connlimit --connlimit-above 100 -j DROP

TLS Termination

For additional security, terminate TLS at the load balancer:

# HAProxy with TLS
frontend stratum_tls
    bind *:3334 ssl crt /etc/ssl/pool.pem
    mode tcp
    default_backend pool_nodes

Connection Limits

[security]
max_connections = 10000
max_connections_per_ip = 100
connection_timeout_secs = 30
idle_timeout_secs = 300

DDoS Protection

[security]
# Require valid handshake within timeout
handshake_timeout_secs = 10

# Rate limit share submissions
max_shares_per_second = 100

# Ban repeated invalid submissions
ban_threshold = 50  # invalid shares
ban_duration_secs = 3600

Payout System

PPS (Pay Per Share)

Default payout scheme:

Miner Payment = (Miner Shares / Total Shares) x Block Reward x (1 - Pool Fee)
[payout]
scheme = "pps"
pool_fee_percent = 1.0

PPLNS (Pay Per Last N Shares)

Alternative scheme based on recent shares:

[payout]
scheme = "pplns"
pplns_window = 100000  # shares
pool_fee_percent = 0.5

Database Integration

For production, integrate with a database:

[database]
url = "postgres://user:pass@localhost/pool"

Schema:

CREATE TABLE shares (
    id BIGSERIAL PRIMARY KEY,
    miner_id VARCHAR(255) NOT NULL,
    difficulty DOUBLE PRECISION NOT NULL,
    timestamp TIMESTAMP DEFAULT NOW(),
    job_id INTEGER NOT NULL,
    is_block BOOLEAN DEFAULT FALSE
);

CREATE TABLE payouts (
    id BIGSERIAL PRIMARY KEY,
    miner_id VARCHAR(255) NOT NULL,
    amount BIGINT NOT NULL,  -- zatoshis
    txid VARCHAR(64),
    timestamp TIMESTAMP DEFAULT NOW()
);

Operational Procedures

Starting the Pool

# Verify Zebra is synced
curl -s http://127.0.0.1:8232 -d '{"jsonrpc":"2.0","method":"getblockchaininfo","params":[],"id":1}' | jq .result.blocks

# Start pool
systemctl start zcash-pool

# Monitor logs
journalctl -u zcash-pool -f

Graceful Shutdown

# Signal graceful shutdown
kill -SIGTERM $(pidof zcash-pool-server)

# Pool will:
# 1. Stop accepting new connections
# 2. Finish processing pending shares
# 3. Save state
# 4. Exit

Updating the Pool

# Build new version
git pull
cargo build --release

# Deploy with rolling restart
systemctl stop zcash-pool
cp target/release/zcash-pool-server /usr/local/bin/
systemctl start zcash-pool

Emergency Procedures

Zebra disconnected:

# Check Zebra status
systemctl status zebrad

# Restart if needed
systemctl restart zebrad

# Pool will auto-reconnect

High stale rate:

# Check template freshness
curl localhost:9090/metrics | grep template_age

# Reduce poll interval if needed
# Edit pool.toml: poll_interval_ms = 250
systemctl restart zcash-pool

Compliance Considerations

Logging Requirements

Maintain logs for regulatory compliance:

[logging]
retention_days = 90
include_miner_ips = true
include_share_details = true

Payout Records

Keep detailed payout records:

-- All payouts with miner identity
SELECT
    miner_id,
    SUM(amount) as total_paid,
    COUNT(*) as payout_count
FROM payouts
WHERE timestamp > NOW() - INTERVAL '1 year'
GROUP BY miner_id;

Troubleshooting

“Failed to connect to Zebra”

# Check Zebra is running
systemctl status zebrad

# Check RPC is accessible
curl http://127.0.0.1:8232

# Check firewall
sudo iptables -L | grep 8232

“High share rejection rate”

  1. Check for outdated mining software
  2. Verify network latency
  3. Review vardiff settings
  4. Check for stale template issues

“Memory usage growing”

# Check for connection leaks
ss -s | grep ESTABLISHED

# Review pool metrics
curl localhost:9090/metrics | grep connections

“Blocks not submitting”

# Check Zebra connectivity
curl http://127.0.0.1:8232 -d '{"jsonrpc":"2.0","method":"getblockcount","params":[],"id":1}'

# Check recent block submissions
journalctl -u zcash-pool | grep "block found"