You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
3.5 KiB
143 lines
3.5 KiB
10 years ago
|
package stratum
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"regexp"
|
||
|
"strings"
|
||
8 years ago
|
"sync/atomic"
|
||
10 years ago
|
|
||
7 years ago
|
"github.com/sammy007/monero-stratum/util"
|
||
10 years ago
|
)
|
||
|
|
||
|
var noncePattern *regexp.Regexp
|
||
|
|
||
8 years ago
|
const defaultWorkerId = "0"
|
||
|
|
||
10 years ago
|
func init() {
|
||
|
noncePattern, _ = regexp.Compile("^[0-9a-f]{8}$")
|
||
|
}
|
||
|
|
||
8 years ago
|
func (s *StratumServer) handleLoginRPC(cs *Session, params *LoginParams) (*JobReply, *ErrorReply) {
|
||
8 years ago
|
address, id := extractWorkerId(params.Login)
|
||
|
if !s.config.BypassAddressValidation && !util.ValidateAddress(address, s.config.Address) {
|
||
|
log.Printf("Invalid address %s used for login by %s", address, cs.ip)
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Invalid address used for login"}
|
||
10 years ago
|
}
|
||
|
|
||
8 years ago
|
t := s.currentBlockTemplate()
|
||
|
if t == nil {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Job not ready"}
|
||
8 years ago
|
}
|
||
|
|
||
|
miner, ok := s.miners.Get(id)
|
||
|
if !ok {
|
||
8 years ago
|
miner = NewMiner(id, cs.ip)
|
||
|
s.registerMiner(miner)
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
log.Printf("Miner connected %s@%s", id, cs.ip)
|
||
|
|
||
8 years ago
|
s.registerSession(cs)
|
||
10 years ago
|
miner.heartbeat()
|
||
|
|
||
8 years ago
|
return &JobReply{Id: id, Job: cs.getJob(t), Status: "OK"}, nil
|
||
10 years ago
|
}
|
||
|
|
||
8 years ago
|
func (s *StratumServer) handleGetJobRPC(cs *Session, params *GetJobParams) (*JobReplyData, *ErrorReply) {
|
||
10 years ago
|
miner, ok := s.miners.Get(params.Id)
|
||
|
if !ok {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Unauthenticated"}
|
||
10 years ago
|
}
|
||
8 years ago
|
t := s.currentBlockTemplate()
|
||
|
if t == nil {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Job not ready"}
|
||
8 years ago
|
}
|
||
10 years ago
|
miner.heartbeat()
|
||
8 years ago
|
return cs.getJob(t), nil
|
||
10 years ago
|
}
|
||
|
|
||
8 years ago
|
func (s *StratumServer) handleSubmitRPC(cs *Session, params *SubmitParams) (*SubmitReply, *ErrorReply) {
|
||
10 years ago
|
miner, ok := s.miners.Get(params.Id)
|
||
|
if !ok {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Unauthenticated"}
|
||
10 years ago
|
}
|
||
|
miner.heartbeat()
|
||
|
|
||
8 years ago
|
job := cs.findJob(params.JobId)
|
||
10 years ago
|
if job == nil {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Invalid job id"}
|
||
10 years ago
|
}
|
||
|
|
||
|
if !noncePattern.MatchString(params.Nonce) {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Malformed nonce"}
|
||
10 years ago
|
}
|
||
|
nonce := strings.ToLower(params.Nonce)
|
||
|
exist := job.submit(nonce)
|
||
|
if exist {
|
||
8 years ago
|
atomic.AddUint64(&miner.invalidShares, 1)
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Duplicate share"}
|
||
10 years ago
|
}
|
||
|
|
||
|
t := s.currentBlockTemplate()
|
||
8 years ago
|
if job.height != t.height {
|
||
8 years ago
|
log.Printf("Stale share for height %d from %s@%s", job.height, miner.id, cs.ip)
|
||
8 years ago
|
atomic.AddUint64(&miner.staleShares, 1)
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Block expired"}
|
||
10 years ago
|
}
|
||
|
|
||
8 years ago
|
validShare := miner.processShare(s, cs, job, t, nonce, params.Result)
|
||
10 years ago
|
if !validShare {
|
||
8 years ago
|
return nil, &ErrorReply{Code: -1, Message: "Low difficulty share"}
|
||
10 years ago
|
}
|
||
8 years ago
|
return &SubmitReply{Status: "OK"}, nil
|
||
10 years ago
|
}
|
||
|
|
||
8 years ago
|
func (s *StratumServer) handleUnknownRPC(req *JSONRpcReq) *ErrorReply {
|
||
10 years ago
|
log.Printf("Unknown RPC method: %v", req)
|
||
8 years ago
|
return &ErrorReply{Code: -1, Message: "Invalid method"}
|
||
10 years ago
|
}
|
||
|
|
||
|
func (s *StratumServer) broadcastNewJobs() {
|
||
8 years ago
|
t := s.currentBlockTemplate()
|
||
|
if t == nil {
|
||
|
return
|
||
|
}
|
||
8 years ago
|
s.sessionsMu.RLock()
|
||
|
defer s.sessionsMu.RUnlock()
|
||
|
count := len(s.sessions)
|
||
|
log.Printf("Broadcasting new jobs to %d miners", count)
|
||
8 years ago
|
bcast := make(chan int, 1024*16)
|
||
|
n := 0
|
||
10 years ago
|
|
||
8 years ago
|
for m := range s.sessions {
|
||
8 years ago
|
n++
|
||
|
bcast <- n
|
||
8 years ago
|
go func(cs *Session) {
|
||
|
reply := cs.getJob(t)
|
||
|
err := cs.pushMessage("job", &reply)
|
||
8 years ago
|
<-bcast
|
||
10 years ago
|
if err != nil {
|
||
8 years ago
|
log.Printf("Job transmit error to %s: %v", cs.ip, err)
|
||
|
s.removeSession(cs)
|
||
8 years ago
|
} else {
|
||
8 years ago
|
s.setDeadline(cs.conn)
|
||
10 years ago
|
}
|
||
8 years ago
|
}(m)
|
||
10 years ago
|
}
|
||
|
}
|
||
|
|
||
|
func (s *StratumServer) refreshBlockTemplate(bcast bool) {
|
||
|
newBlock := s.fetchBlockTemplate()
|
||
|
if newBlock && bcast {
|
||
|
s.broadcastNewJobs()
|
||
|
}
|
||
|
}
|
||
8 years ago
|
|
||
8 years ago
|
func extractWorkerId(loginWorkerPair string) (string, string) {
|
||
8 years ago
|
parts := strings.SplitN(loginWorkerPair, ".", 2)
|
||
|
if len(parts) > 1 {
|
||
8 years ago
|
return parts[0], parts[1]
|
||
8 years ago
|
}
|
||
8 years ago
|
return loginWorkerPair, defaultWorkerId
|
||
8 years ago
|
}
|