From 3d2a3371d795282bb5519d8d84d83aca3d88bc4a Mon Sep 17 00:00:00 2001 From: abs2023 Date: Tue, 10 Feb 2026 15:18:04 -0500 Subject: [PATCH] fix: handle mining.extranonce.subscribe and prevent nil pointer crash Fixes crash loop caused by new miners sending mining.extranonce.subscribe messages during handshake phase. Changes: 1. Made logWithContext() defensive against nil destination connections - Prevents panic when logging before connection fully established - Returns "not-connected" instead of crashing on nil pointer dereference 2. Added explicit handling for MiningExtranonceSubscribe messages - Properly forwards extranonce subscription requests to pool - Logs at debug level for visibility - Prevents falling through to unknown message handler Root Cause: New miners added to seller node send mining.extranonce.subscribe during initial handshake. This legitimate Stratum extension message wasn't explicitly handled, causing it to fall through to default case. The logging in default case attempted to access p.dest.conn.conn.LocalAddr() which was nil during early handshake, triggering panic and container restart every 5-6 minutes. Impact: - Resolves continuous crash loop since 2026-02-10 13:16 ET - Enables modern miners with extranonce support to connect properly - Future-proofs logging against similar edge cases Co-authored-by: Cursor --- .../hashrate/proxy/handler_first_connect.go | 5 +++++ internal/resources/hashrate/proxy/proxy.go | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/resources/hashrate/proxy/handler_first_connect.go b/internal/resources/hashrate/proxy/handler_first_connect.go index a2b3a05..02d0533 100644 --- a/internal/resources/hashrate/proxy/handler_first_connect.go +++ b/internal/resources/hashrate/proxy/handler_first_connect.go @@ -69,6 +69,11 @@ func (p *HandlerFirstConnect) handleSource(ctx context.Context, msg i.MiningMess case *m.MiningAuthorize: return nil, p.onMiningAuthorize(ctx, msgTyped) + case *m.MiningExtranonceSubscribe: + // Pass through extranonce subscription to pool + p.proxy.logDebugf("forwarding mining.extranonce.subscribe from source") + return nil, p.proxy.dest.Write(ctx, msgTyped) + case *m.MiningSubmit: return nil, fmt.Errorf("unexpected handshake message from source: %s", string(msg.Serialize())) diff --git a/internal/resources/hashrate/proxy/proxy.go b/internal/resources/hashrate/proxy/proxy.go index 2564b0c..0301e47 100644 --- a/internal/resources/hashrate/proxy/proxy.go +++ b/internal/resources/hashrate/proxy/proxy.go @@ -450,5 +450,21 @@ func (p *Proxy) logErrorf(template string, args ...interface{}) { p.logWithContext(p.log.Errorw, template, args...) } func (p *Proxy) logWithContext(logFn func(t string, a ...interface{}), t string, a ...interface{}) { - logFn(fmt.Sprintf(t, a...), "DstAddr", p.dest.ID(), "DstPort", lib.ParsePort(p.dest.conn.conn.LocalAddr().String())) + logFields := []interface{}{} + + // Add destination address if available + if p.dest != nil { + logFields = append(logFields, "DstAddr", p.dest.ID()) + + // Add port if connection is established + if p.dest.conn != nil && p.dest.conn.conn != nil { + logFields = append(logFields, "DstPort", lib.ParsePort(p.dest.conn.conn.LocalAddr().String())) + } else { + logFields = append(logFields, "DstPort", "not-connected") + } + } else { + logFields = append(logFields, "DstAddr", "not-initialized", "DstPort", "not-connected") + } + + logFn(fmt.Sprintf(t, a...), logFields...) }