diff --git a/miner_login_test.go b/miner_login_test.go index c51211d..6a92311 100644 --- a/miner_login_test.go +++ b/miner_login_test.go @@ -17,6 +17,9 @@ func TestMiner_HandleLogin_Good(t *testing.T) { miner.algoEnabled = true miner.extNH = true miner.fixedByte = 0x2a + miner.onLogin = func(m *Miner) { + m.SetMapperID(1) + } miner.currentJob = Job{ Blob: strings.Repeat("0", 160), JobID: "job-1", @@ -106,3 +109,63 @@ func TestProxy_New_Watch_Good(t *testing.T) { t.Fatalf("expected config watcher when watch is enabled and source path is known") } } + +func TestMiner_HandleLogin_Ugly(t *testing.T) { + for i := 0; i < 256; i++ { + miner := &Miner{} + miner.SetID(int64(i + 1)) + miner.SetMapperID(int64(i + 1)) + } + + serverConn, clientConn := net.Pipe() + defer serverConn.Close() + defer clientConn.Close() + + miner := NewMiner(serverConn, 3333, nil) + miner.extNH = true + miner.onLogin = func(*Miner) {} + + params, err := json.Marshal(loginParams{ + Login: "wallet", + Pass: "x", + }) + if err != nil { + t.Fatalf("marshal login params: %v", err) + } + + done := make(chan []byte, 1) + go func() { + line, readErr := bufio.NewReader(clientConn).ReadBytes('\n') + if readErr != nil { + done <- nil + return + } + done <- line + }() + + miner.handleLogin(stratumRequest{ID: 2, Method: "login", Params: params}) + + line := <-done + if line == nil { + t.Fatal("expected login rejection response") + } + + var payload struct { + Error struct { + Message string `json:"message"` + } `json:"error"` + Result map[string]any `json:"result"` + } + if err := json.Unmarshal(line, &payload); err != nil { + t.Fatalf("unmarshal login response: %v", err) + } + if payload.Error.Message != "Proxy is full, try again later" { + t.Fatalf("expected full-table error, got %q", payload.Error.Message) + } + if payload.Result != nil { + t.Fatalf("expected no login success payload, got %#v", payload.Result) + } + if miner.MapperID() != -1 { + t.Fatalf("expected rejected miner to remain unassigned, got mapper %d", miner.MapperID()) + } +} diff --git a/state_impl.go b/state_impl.go index e7a22ee..b20af1e 100644 --- a/state_impl.go +++ b/state_impl.go @@ -373,12 +373,19 @@ func (p *Proxy) acceptMiner(conn net.Conn, localPort uint16) { miner.globalDiff = p.config.CustomDiff miner.extNH = strings.EqualFold(p.config.Mode, "nicehash") miner.onLogin = func(m *Miner) { - if p.events != nil { - p.events.Dispatch(Event{Type: EventLogin, Miner: m}) - } if p.splitter != nil { p.splitter.OnLogin(&LoginEvent{Miner: m}) } + if m.extNH { + if m.MapperID() < 0 { + return + } + } else if m.RouteID() < 0 { + return + } + if p.events != nil { + p.events.Dispatch(Event{Type: EventLogin, Miner: m}) + } } miner.onSubmit = func(m *Miner, event *SubmitEvent) { if p.splitter != nil { @@ -915,6 +922,22 @@ func (m *Miner) handleLogin(req stratumRequest) { if m.onLogin != nil { m.onLogin(m) } + if m.state == MinerStateClosing { + return + } + if m.extNH { + if m.MapperID() < 0 { + m.state = MinerStateWaitLogin + m.rpcID = "" + m.ReplyWithError(requestID(req.ID), "Proxy is full, try again later") + return + } + } else if m.RouteID() < 0 { + m.state = MinerStateWaitLogin + m.rpcID = "" + m.ReplyWithError(requestID(req.ID), "Proxy is unavailable, try again later") + return + } m.replyLoginSuccess(requestID(req.ID)) }