High-Availability RelayMiner implementation for Pocket Network - a production-grade, horizontally scalable relay mining service with full multi-transport support.
The HA RelayMiner is a distributed relay mining system that enables horizontal scaling and automatic failover through Redis-based shared state. It separates concerns into two components:
- Relayer: Stateless multi-transport proxy (JSON-RPC, WebSocket, gRPC, Streaming) that validates and forwards relay requests (scales horizontally)
- Miner: Stateful claim/proof submission service with leader election (active-standby failover)
✅ JSON-RPC (HTTP) - Traditional HTTP POST with JSON-RPC payload ✅ WebSocket - Persistent bidirectional connections for real-time applications ✅ gRPC - High-performance binary protocol with streaming support ✅ REST/Streaming - Server-Sent Events (SSE) for streaming responses
All transport modes support:
- Ring signature validation for application authentication
- Supplier signature verification on responses
- Relay metering and rate limiting
- Session-based routing to backends
+----------------+
| Load Balancer |
+----------------+
|
+-------------+-------------+
| | |
+-----------+ +-----------+ +-----------+
| Relayer#1 | | Relayer#2 | | Relayer#3 |
+-----------+ +-----------+ +-----------+
| | |
+-------------+-------------+
|
+-------------+
| Redis (HA) |
+-------------+
|
+-------------+-------------+
| |
+-----------+ +-----------+
| Miner | | Miner |
| (Leader) | | (Standby) |
+-----------+ +-----------+
All session state is stored in Redis for cross-instance sharing:
- WAL (Write-Ahead Log): Redis Streams for durability and recovery
- SMST (Sparse Merkle Sum Tree): Redis Hashes for merkle tree node storage
- Session Metadata: Redis Hashes for session state snapshots
Key Benefits:
- No local disk I/O bottlenecks
- Instant failover (standby can take over immediately)
- Horizontal scaling without state synchronization
- O(1) operations for Get/Set/Delete
- Target: 1000+ RPS per relayer replica
- SMST Operations: ~30µs per operation (Redis Hashes)
- Relay Processing: Sub-millisecond validation and signing
- Failover Time: <5 seconds (leader election + state recovery)
- Go: 1.24.3+
- Redis: 6.2+ (Standalone, Sentinel, or Cluster mode)
- Network: Access to Pocket Network Shannon RPC/gRPC endpoints
- Poktroll: v0.1.31+ (dependency)
# Clone the repository
git clone https://github.com/pokt-network/pocket-relay-miner.git
cd pocket-relay-miner
# Build the binary
make build
# Or build optimized release version
make build-release- Development build:
./pocket-relay-miner - Release build:
bin/pocket-relay-miner
See localnet/ha/relayer-config-1.yaml for example configuration.
Key settings:
listen_addr: HTTP server bind address (e.g.,0.0.0.0:3000)redis.url: Redis connection string (e.g.,redis://localhost:6379)pocket_node.query_node_grpc_url: Pocket node gRPC endpointpocket_node.query_node_rpc_url: Pocket node RPC endpoint (WebSocket)services: Backend service configurations per service ID and RPC type
See localnet/ha/miner-config-1.yaml for example configuration.
Key settings:
redis.url: Redis connection string (must match relayer)redis.consumer_group: Consumer group name (e.g.,ha-miners)redis.consumer_name: Unique consumer identifier per replicapocket_node.query_node_grpc_url: Pocket node gRPC endpointpocket_node.tx_node_rpc_url: Pocket node RPC endpoint for submitting transactionskeys.keys_file: Path to supplier keys YAML fileknown_applications: Application addresses to pre-warm in cache
pocket-relay-miner relayer --config /path/to/relayer-config.yamlCommand flags:
--config: Path to relayer config file (required)--redis-url: Override Redis URL from config
pocket-relay-miner miner --config /path/to/miner-config.yamlCommand flags:
--config: Path to miner config file (required)--redis-url: Override Redis URL from config--consumer-group: Override consumer group name--consumer-name: Override consumer name (defaults to hostname)
Only one miner instance will be active (leader) at any time. The leader:
- Refreshes shared caches (params, services, applications)
- Processes relays from Redis Streams
- Builds SMST trees and submits claims/proofs
Standby instances automatically take over if the leader fails.
Test all supported transport protocols with signature verification:
# Test JSON-RPC (HTTP) relay
pocket-relay-miner relay jsonrpc \
--app-priv-key <hex> \
--service <serviceID> \
--node <grpc_endpoint> \
--chain-id <chainID> \
--relayer-url <relayer_url> \
--supplier <supplier_address>
# Test WebSocket relay
pocket-relay-miner relay websocket \
--app-priv-key <hex> \
--service <serviceID> \
--node <grpc_endpoint> \
--chain-id <chainID> \
--relayer-url <relayer_url> \
--supplier <supplier_address>
# Test gRPC relay
pocket-relay-miner relay grpc \
--app-priv-key <hex> \
--service <serviceID> \
--node <grpc_endpoint> \
--chain-id <chainID> \
--relayer-url <relayer_url> \
--supplier <supplier_address>
# Test streaming relay (SSE)
pocket-relay-miner relay stream \
--app-priv-key <hex> \
--service <serviceID> \
--node <grpc_endpoint> \
--chain-id <chainID> \
--relayer-url <relayer_url> \
--supplier <supplier_address>
# Load testing mode (concurrent requests)
pocket-relay-miner relay jsonrpc \
--load-test \
--count 1000 \
--concurrency 50 \
<...other flags>Features:
- Signature verification for all relay responses
- Detailed timing breakdowns (build, network, verify)
- Load testing with configurable concurrency
- Custom JSON-RPC payload support
- Session-based relay construction
Debug and inspect Redis data structures used by the RelayMiner:
# Check leader election status
pocket-relay-miner redis-debug leader --redis redis://localhost:6379
# Inspect session metadata for a supplier
pocket-relay-miner redis-debug sessions --supplier pokt1abc... --state active
# View SMST tree for a session
pocket-relay-miner redis-debug smst --session session_123
# Monitor Redis Streams
pocket-relay-miner redis-debug streams --supplier pokt1abc...
# Inspect cache entries
pocket-relay-miner redis-debug cache --type application --list
# List all keys matching pattern
pocket-relay-miner redis-debug keys --pattern "ha:smst:*" --stats
# Monitor pub/sub events
pocket-relay-miner redis-debug pubsub --channel "ha:events:cache:application:invalidate"
# Flush data with safety confirmations (DANGEROUS)
pocket-relay-miner redis-debug flush --pattern "ha:smst:old_session_*"Available debug commands:
sessions: Inspect session metadata and statesmst: View SMST tree data for sessionsstreams: Monitor Redis Streams (WAL)cache: Inspect/invalidate cache entriesleader: Check leader election statusdedup: Inspect deduplication setssupplier: View supplier registrymeter: Inspect relay metering datapubsub: Monitor pub/sub channels in real-timekeys: List keys by pattern with statsflush: Delete keys with safety confirmations
All debug commands support --redis flag to specify Redis URL (default: redis://localhost:6379).
# Run all tests
make test
# Run tests with coverage
make test-coverage
# Run HA-specific tests
go test -tags test ./miner/... -v# Format code
make fmt
# Run linters
make lint
# Tidy dependencies
make tidy- Memory per relay: ~500 bytes (SMST node + metadata)
- Memory per session: ~500 bytes × relays_per_session
- Example: 1000 relays × 100 active sessions = ~50 MB
- Recommendation: Size Redis memory at 2x expected usage
Redis:
- Use Redis Sentinel (3+ nodes) for automatic failover
- Or Redis Cluster (6+ nodes) for sharding
- Configure connection retry with exponential backoff
- Monitor replication lag
Relayer:
- Deploy 3+ instances behind a load balancer
- Use health checks at
/healthendpoint - Configure resource limits (CPU/memory)
Miner:
- Deploy 2+ instances (leader + standbys)
- Ensure network connectivity to Redis and Pocket node
- Monitor leader election state
Metrics exposed at :9092/metrics:
ha_relay_requests_total: Total relay requests processedha_relay_validation_errors_total: Relay validation failuresha_session_trees_active: Active SMST trees in memoryha_cache_hits_total/ha_cache_misses_total: Cache performanceha_leader_election_state: Leader election status (1=leader, 0=standby)
Redis health checks:
# Check memory usage
redis-cli INFO memory
# Monitor latency
redis-cli --latency
# Check connected clients
redis-cli CLIENT LIST1. Redis Out of Memory
- Symptom:
OOM command not allowederrors - Solution: Increase
maxmemoryor enable eviction policy - Prevention: Monitor memory usage, alert at 80% threshold
- Debug:
redis-debug keys --pattern "ha:*" --statsto see what's consuming memory
2. High Relay Latency
- Symptom: Slow relay processing, timeouts
- Solution: Check Redis network latency, verify no disk swapping
- Check:
redis-cli --latencyshould show <2ms - Debug:
redis-debug streams --supplier <addr>to check pending message backlog
3. Leader Election Failures
- Symptom: No active miner leader
- Solution: Check Redis connectivity, verify lock TTL settings
- Debug:
redis-debug leaderto check current leader status
4. SMST Tree Corruption
- Symptom: Proof generation failures
- Solution: WAL replay from last checkpoint
- Debug:
redis-debug smst --session <id>to inspect tree node count - Check: Redis keys
wal:session_{sessionID}exist
5. Stale Cache Data
- Symptom: Validation failures, outdated session/params
- Solution: Invalidate cache entries to force refresh
- Debug:
redis-debug cache --type <type> --key <key> --invalidate
6. Orphaned Session Data
- Symptom: High memory usage from old sessions
- Solution: Clean up completed/expired sessions
- Debug:
redis-debug sessions --supplier <addr> --state settled - Cleanup:
redis-debug flush --pattern "ha:miner:sessions:*:old_session_id"
# 1. Check overall system state
redis-debug leader # Verify active leader
redis-debug keys --pattern "ha:*" # See all HA keys
# 2. Investigate session issues
redis-debug sessions --supplier <addr> --state active
redis-debug smst --session <session_id>
# 3. Monitor real-time events
redis-debug pubsub --channel "ha:events:cache:session:invalidate"
# 4. Clean up after investigation
redis-debug flush --pattern "ha:test:*" # Delete test dataRelayer:
- Validate relay requests (ring signatures, session validity)
- Sign relay responses with supplier keys
- Publish validated relays to Redis Streams
- Rate limit based on application stake (relay meter)
Miner:
- Consume relays from Redis Streams (per supplier)
- Build SMST trees in Redis (shared across instances)
- Submit claims at session grace period end
- Generate and submit proofs before proof window closes
Cache Orchestrator:
- Refresh shared caches on block updates (leader only)
- Coordinate L1 (local) / L2 (Redis) / L3 (network) cache layers
- Pub/sub invalidation across all relayer/miner instances
- Pre-warm caches with configured known entities
- Client sends relay request → Relayer
- Relayer validates request → Publishes to Redis Stream
- Miner (leader) consumes from stream → Updates SMST in Redis
- WAL entry written to Redis Stream for durability
- At session end → Miner generates claim → Submits to blockchain
- During proof window → Miner generates proof from SMST → Submits to blockchain
- poktroll: Core Pocket Network protocol (v0.1.31+)
- Redis: go-redis/v9 for Redis operations
- Cosmos SDK: Blockchain client libraries (v0.53.0)
- CometBFT: Consensus engine fork (pokt-network/cometbft)
See LICENSE file in the repository.
This is production-grade software. All contributions must:
- Include comprehensive tests
- Pass all linters and static analysis
- Include performance benchmarks for critical paths
- Be reviewed for security vulnerabilities
- Maintain backward compatibility or provide migration path
For issues and questions:
- GitHub Issues: https://github.com/pokt-network/pocket-relay-miner/issues
- Discord: https://discord.gg/pokt (for community support)