keva-stratum/go-pool/stratum/handlers.go

142 lines
3.4 KiB
Go
Raw Normal View History

2015-07-05 09:49:07 +00:00
package stratum
import (
"log"
"regexp"
"strings"
2016-12-06 14:25:07 +00:00
"sync/atomic"
2015-07-05 09:49:07 +00:00
"../util"
)
var noncePattern *regexp.Regexp
2016-12-06 19:55:16 +00:00
const defaultWorkerId = "0"
2015-07-05 09:49:07 +00:00
func init() {
noncePattern, _ = regexp.Compile("^[0-9a-f]{8}$")
}
2016-12-07 07:07:31 +00:00
func (s *StratumServer) handleLoginRPC(cs *Session, params *LoginParams) (*JobReply, *ErrorReply) {
2016-08-07 02:05:45 +00:00
if !s.config.BypassAddressValidation && !util.ValidateAddress(params.Login, s.config.Address) {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Invalid address used for login"}
2015-07-05 09:49:07 +00:00
}
2016-12-06 18:03:54 +00:00
t := s.currentBlockTemplate()
if t == nil {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Job not ready"}
2016-12-06 19:55:16 +00:00
}
id := extractWorkerId(params.Login)
miner, ok := s.miners.Get(id)
if !ok {
2016-12-06 20:29:59 +00:00
miner = NewMiner(id, cs.ip)
s.registerMiner(miner)
2016-12-06 18:03:54 +00:00
}
2016-12-06 19:55:16 +00:00
log.Printf("Miner connected %s@%s", id, cs.ip)
2016-12-06 20:29:59 +00:00
s.registerSession(cs)
2015-07-05 09:49:07 +00:00
miner.heartbeat()
2016-12-06 20:29:59 +00:00
return &JobReply{Id: id, Job: cs.getJob(t), Status: "OK"}, nil
2015-07-05 09:49:07 +00:00
}
2016-12-07 07:07:31 +00:00
func (s *StratumServer) handleGetJobRPC(cs *Session, params *GetJobParams) (*JobReplyData, *ErrorReply) {
2015-07-05 09:49:07 +00:00
miner, ok := s.miners.Get(params.Id)
if !ok {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Unauthenticated"}
2015-07-05 09:49:07 +00:00
}
2016-12-06 18:03:54 +00:00
t := s.currentBlockTemplate()
if t == nil {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Job not ready"}
2016-12-06 18:03:54 +00:00
}
2015-07-05 09:49:07 +00:00
miner.heartbeat()
2016-12-06 21:29:01 +00:00
return cs.getJob(t), nil
2015-07-05 09:49:07 +00:00
}
2016-12-07 07:07:31 +00:00
func (s *StratumServer) handleSubmitRPC(cs *Session, params *SubmitParams) (*SubmitReply, *ErrorReply) {
2015-07-05 09:49:07 +00:00
miner, ok := s.miners.Get(params.Id)
if !ok {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Unauthenticated"}
2015-07-05 09:49:07 +00:00
}
miner.heartbeat()
2016-12-06 20:29:59 +00:00
job := cs.findJob(params.JobId)
2015-07-05 09:49:07 +00:00
if job == nil {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Invalid job id"}
2015-07-05 09:49:07 +00:00
}
if !noncePattern.MatchString(params.Nonce) {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Malformed nonce"}
2015-07-05 09:49:07 +00:00
}
nonce := strings.ToLower(params.Nonce)
exist := job.submit(nonce)
if exist {
2016-12-06 14:25:07 +00:00
atomic.AddUint64(&miner.invalidShares, 1)
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Duplicate share"}
2015-07-05 09:49:07 +00:00
}
t := s.currentBlockTemplate()
2016-12-07 07:00:18 +00:00
if job.height != t.height {
2016-12-07 06:55:29 +00:00
log.Printf("Stale share for height %d from %s@%s", job.height, miner.id, miner.ip)
2016-12-06 14:25:07 +00:00
atomic.AddUint64(&miner.staleShares, 1)
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Block expired"}
2015-07-05 09:49:07 +00:00
}
2016-12-07 07:07:31 +00:00
validShare := miner.processShare(s, cs.endpoint, job, t, nonce, params.Result)
2015-07-05 09:49:07 +00:00
if !validShare {
2016-12-06 21:29:01 +00:00
return nil, &ErrorReply{Code: -1, Message: "Low difficulty share"}
2015-07-05 09:49:07 +00:00
}
2016-12-06 21:29:01 +00:00
return &SubmitReply{Status: "OK"}, nil
2015-07-05 09:49:07 +00:00
}
2016-12-07 07:07:31 +00:00
func (s *StratumServer) handleUnknownRPC(req *JSONRpcReq) *ErrorReply {
2015-07-05 09:49:07 +00:00
log.Printf("Unknown RPC method: %v", req)
2016-12-06 21:29:01 +00:00
return &ErrorReply{Code: -1, Message: "Invalid method"}
2015-07-05 09:49:07 +00:00
}
func (s *StratumServer) broadcastNewJobs() {
2016-12-06 18:03:54 +00:00
t := s.currentBlockTemplate()
if t == nil {
return
}
2016-12-06 20:29:59 +00:00
s.sessionsMu.RLock()
defer s.sessionsMu.RUnlock()
count := len(s.sessions)
log.Printf("Broadcasting new jobs to %d miners", count)
2016-08-07 02:07:27 +00:00
bcast := make(chan int, 1024*16)
2015-07-05 09:49:07 +00:00
n := 0
2016-12-06 20:29:59 +00:00
for m := range s.sessions {
2015-07-05 09:49:07 +00:00
n++
bcast <- n
2016-12-06 20:29:59 +00:00
go func(cs *Session) {
reply := cs.getJob(t)
err := cs.pushMessage("job", &reply)
2015-07-05 09:49:07 +00:00
<-bcast
if err != nil {
2016-12-06 20:29:59 +00:00
log.Printf("Job transmit error to %s: %v", cs.ip, err)
s.removeSession(cs)
2016-08-07 03:11:10 +00:00
} else {
2016-12-06 20:29:59 +00:00
s.setDeadline(cs.conn)
2015-07-05 09:49:07 +00:00
}
2016-12-06 20:29:59 +00:00
}(m)
2015-07-05 09:49:07 +00:00
}
}
func (s *StratumServer) refreshBlockTemplate(bcast bool) {
newBlock := s.fetchBlockTemplate()
if newBlock && bcast {
s.broadcastNewJobs()
}
}
2016-12-06 19:55:16 +00:00
func extractWorkerId(loginWorkerPair string) string {
parts := strings.SplitN(loginWorkerPair, ".", 2)
if len(parts) > 1 {
return parts[1]
}
return defaultWorkerId
}