diff --git a/pool/client.go b/pool/client.go index 5e9fb049..5fa7a787 100644 --- a/pool/client.go +++ b/pool/client.go @@ -297,9 +297,9 @@ func (c *Client) handleAuthorizeRequest(req *Request, allowed bool) error { // monitor periodically checks the miner details set against expected // incoming submission tally and upgrades the miner if possible when the // submission tallies exceed the expected number by 30 percent. -func (c *Client) monitor(idx int, pair *minerIDPair, monitorCycle time.Duration, maxTries uint32) { +func (c *Client) monitor(idx int, clients []string, monitorCycle time.Duration, maxTries uint32) { var subs, tries uint32 - if len(pair.miners) <= 1 { + if len(clients) <= 1 { // Nothing to do if there are no more miner ids to upgrade to. return } @@ -311,7 +311,7 @@ func (c *Client) monitor(idx int, pair *minerIDPair, monitorCycle time.Duration, select { case <-ticker.C: - if idx == len(pair.miners)-1 { + if idx == len(clients)-1 { // No more miner upgrades possible. return } @@ -338,7 +338,7 @@ func (c *Client) monitor(idx int, pair *minerIDPair, monitorCycle time.Duration, // Update the miner's details and send a new mining.set_difficulty // message to the client. c.mtx.Lock() - miner := pair.miners[idx] + miner := clients[idx] newID := fmt.Sprintf("%v/%v", c.extraNonce1, miner) log.Infof("upgrading %s to %s", c.id, newID) info, err := c.cfg.FetchMinerDifficulty(miner) @@ -378,7 +378,7 @@ func (c *Client) handleSubscribeRequest(req *Request, allowed bool) error { return errs.PoolError(errs.LimitExceeded, err.Error()) } - mid, nid, err := ParseSubscribeRequest(req) + userAgent, nid, err := ParseSubscribeRequest(req) if err != nil { sErr := NewStratumError(Unknown, err) resp := SubscribeResponse(*req.ID, "", "", 0, sErr) @@ -386,8 +386,8 @@ func (c *Client) handleSubscribeRequest(req *Request, allowed bool) error { return err } - // Identify the miner and fetch needed mining information for it. - idPair, err := identifyMiner(mid) + // Identify the mining client and fetch needed mining information for it. + clients, err := identifyMiningClients(userAgent) if err != nil { sErr := NewStratumError(Unknown, err) resp := SubscribeResponse(*req.ID, "", "", 0, sErr) @@ -397,7 +397,7 @@ func (c *Client) handleSubscribeRequest(req *Request, allowed bool) error { c.mtx.Lock() minerIdx := 0 - miner := idPair.miners[minerIdx] + miner := clients[minerIdx] info, err := c.cfg.FetchMinerDifficulty(miner) if err != nil { c.mtx.Unlock() @@ -416,7 +416,7 @@ func (c *Client) handleSubscribeRequest(req *Request, allowed bool) error { nid = fmt.Sprintf("mn%v", c.extraNonce1) } - go c.monitor(minerIdx, idPair, c.cfg.MonitorCycle, c.cfg.MaxUpgradeTries) + go c.monitor(minerIdx, clients, c.cfg.MonitorCycle, c.cfg.MaxUpgradeTries) var resp *Response switch miner { diff --git a/pool/client_test.go b/pool/client_test.go index aa760d9b..914ab0d9 100644 --- a/pool/client_test.go +++ b/pool/client_test.go @@ -71,11 +71,7 @@ var ( } ) -func splitMinerID(id string) (string, string) { - const separator = "/" - split := strings.Split(id, separator) - return split[0], split[1] -} +const cpuUserAgent = "cpuminer/1.0.0" func setCurrentWork(work string) { currentWorkMtx.Lock() @@ -382,8 +378,7 @@ func testClientMessageHandling(t *testing.T) { } id++ - cpu, cpuVersion := splitMinerID(cpuID) - r = SubscribeRequest(&id, userAgent(cpu, cpuVersion), "") + r = SubscribeRequest(&id, cpuUserAgent, "") err = sE.Encode(r) if err != nil { t.Fatalf("[Encode] unexpected error: %v", err) @@ -919,8 +914,7 @@ func testClientTimeRolledWork(t *testing.T) { // Ensure a CPU client receives a valid non-error response when // a valid subscribe request is sent. id++ - cpu, cpuVersion := splitMinerID(cpuID) - r = SubscribeRequest(&id, userAgent(cpu, cpuVersion), "") + r = SubscribeRequest(&id, cpuUserAgent, "") err = sE.Encode(r) if err != nil { t.Fatalf("[Encode] unexpected error: %v", err) @@ -1058,12 +1052,12 @@ func testClientUpgrades(t *testing.T) { } const minerIdx = 0 - idPair := newMinerIDPair(cpuID, CPU, clientCPU2) + clients := []string{CPU, clientCPU2} // Trigger a client upgrade. atomic.StoreInt64(&client.submissions, 50) - go client.monitor(minerIdx, idPair, cfg.MonitorCycle, cfg.MaxUpgradeTries) + go client.monitor(minerIdx, clients, cfg.MonitorCycle, cfg.MaxUpgradeTries) time.Sleep(cfg.MonitorCycle + (cfg.MonitorCycle / 2)) if fetchMiner(client) != clientCPU2 { @@ -1091,7 +1085,7 @@ func testClientUpgrades(t *testing.T) { atomic.StoreInt64(&client.submissions, 2) - go client.monitor(minerIdx, idPair, cfg.MonitorCycle, cfg.MaxUpgradeTries) + go client.monitor(minerIdx, clients, cfg.MonitorCycle, cfg.MaxUpgradeTries) time.Sleep(cfg.MonitorCycle + (cfg.MonitorCycle / 2)) if fetchMiner(client) == CPU { diff --git a/pool/miner_id.go b/pool/miner_id.go index 536c79c3..faabfc95 100644 --- a/pool/miner_id.go +++ b/pool/miner_id.go @@ -6,60 +6,34 @@ package pool import ( "fmt" + "regexp" errs "github.com/decred/dcrpool/errors" ) var ( - // These miner ids represent the expected identifications returned by - // supported miners in their mining.subscribe requests. - - cpuID = "cpuminer/1.0.0" - nhID = "NiceHash/1.0.0" -) - -// minerIDPair represents miner subscription identification pairing -// between the id and the miners that identify as. -type minerIDPair struct { - id string - miners map[int]string -} - -// newMinerIDPair creates a new miner ID pair. -func newMinerIDPair(id string, miners ...string) *minerIDPair { - set := make(map[int]string, len(miners)) - for id, entry := range miners { - set[id] = entry - } - sub := &minerIDPair{ - id: id, - miners: set, + // These regular expressions are used to identify the expected mining + // clients by the user agents in their mining.subscribe requests. + cpuRE = regexp.MustCompile(`cpuminer/1\.0\.0|[1-9]\d*`) + nhRE = regexp.MustCompile(`NiceHash/1\.0\.0`) + + // miningClients maps regular expressions to the supported mining client IDs + // for all user agents that match the regular expression. + miningClients = map[*regexp.Regexp][]string{ + cpuRE: {CPU}, + nhRE: {NiceHashValidator}, } - return sub -} - -// generateMinerIDs creates the miner id pairings for all supported miners. -func generateMinerIDs() map[string]*minerIDPair { - ids := make(map[string]*minerIDPair) - cpu := newMinerIDPair(cpuID, CPU) - nicehash := newMinerIDPair(nhID, NiceHashValidator) - - ids[cpu.id] = cpu - ids[nicehash.id] = nicehash - return ids -} - -var ( - // minerIDs represents the minder id pairings for all supported miners. - minerIDs = generateMinerIDs() ) -// identifyMiner determines if the provided miner id is supported by the pool. -func identifyMiner(id string) (*minerIDPair, error) { - mID, ok := minerIDs[id] - if !ok { - msg := fmt.Sprintf("connected miner with id %s is unsupported", id) - return nil, errs.PoolError(errs.MinerUnknown, msg) +// identifyMiningClients returns the possible mining client IDs for a given user agent +// or an error when the user agent is not supported. +func identifyMiningClients(userAgent string) ([]string, error) { + for re, clients := range miningClients { + if re.MatchString(userAgent) { + return clients, nil + } } - return mID, nil + + msg := fmt.Sprintf("connected miner with id %s is unsupported", userAgent) + return nil, errs.PoolError(errs.MinerUnknown, msg) }