m2pool_core/internal/stratum/stratum.go

1098 lines
28 KiB
Go
Raw Normal View History

2025-04-10 07:27:24 +00:00
// stratum.go
package stratum
import (
"bufio"
"math/rand"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"bytes"
"net"
"pool/internal/cache"
"pool/internal/db"
"pool/internal/msg"
"pool/internal/server/coin"
"pool/internal/utility"
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
"go.uber.org/zap"
)
const STRATUM_PING_INTERVAL_CNT int = 3
const STRATUM_PING_FAILED_MAX_CNT int = STRATUM_PING_INTERVAL_CNT * 4
// Exception Macro
const MINER_ERR_UNKNOWN int = 20
const MINER_ERR_NOT_FOUND_JOB int = 21
const MINER_ERR_DUP_SHARE int = 22
const MINER_ERR_LOW_DIF_SHARE int = 23
const MINER_ERR_UNAUTH_WORKER int = 24
const MINER_ERR_NOT_SUBSCRIBED int = 25
const MINER_ERR_ILLEGAL_METHOD int = 26
const MINER_ERR_ILLEGAL_PARARMS int = 27
const MINER_ERR_IP_BANNED int = 28
const MINER_ERR_INVALID_USERNAME int = 29
const MINER_ERR_INTERNAL_ERROR int = 30
const MINER_ERR_TIME_TOO_OLD int = 31
const MINER_ERR_TIME_TOO_NEW int = 32
const MINER_ERR_ILLEGAL_VERMASK int = 33
const MINER_ERR_STALED_JOB int = 34
type Exception_reply struct {
ID float64 `json:"id"`
Result interface{} `json:"result"`
Error [3]interface{} `json:"error"`
}
type Exception_reply_str struct {
ID string `json:"id"`
Result interface{} `json:"result"`
Error [3]interface{} `json:"error"`
}
type Subscribe_reply struct {
Result [3]interface{} `json:"result"`
ID float64 `json:"id"`
Error interface{} `json:"error"`
}
type SubscribeGpu_reply struct {
Jsonrpc string `json:"jsonrpc"`
Result [3]interface{} `json:"result"`
ID float64 `json:"id"`
Error interface{} `json:"error"`
}
type Subscribe_reply_str struct {
Result [3]interface{} `json:"result"`
ID string `json:"id"`
Error interface{} `json:"error"`
}
type Notify_msg struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params [9]interface{} `json:"params"`
}
type Difficulty_msg struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params [1]float64 `json:"params"`
}
type DifficultyNexa_msg struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params [1]string `json:"params"`
}
type DifficultyNexaGpu_msg struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params [1]float64 `json:"params"`
ID interface{} `json:"id"`
}
type ExtranonceSubscribeGpu_reply struct {
ID interface{} `json:"id"`
Jsonrpc string `json:"jsonrpc"`
Result bool `json:"result"`
}
type Authorize_reply struct {
Result bool `json:"result"`
ID float64 `json:"id"`
Error interface{} `json:"error"`
}
type Authorize_reply_str struct {
Result bool `json:"result"`
ID string `json:"id"`
Error interface{} `json:"error"`
}
type AlphSubmitParams struct {
JobID string `json:"jobId"`
FromGroup int `json:"fromGroup"`
ToGroup int `json:"toGroup"`
Nonce string `json:"nonce"`
Worker string `json:"worker"` // user
WorkerName string `json:"workerName"` // miner
}
type AlphSubmitNonce struct {
Id interface{} `json:"id"`
Method string `json:"method"`
Params AlphSubmitParams `json:"params"`
}
type AlphExtranonce struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params []string `json:"params"`
}
type Submit_nonce struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params []string `json:"params"`
}
type Reconnect_msg struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params []string `json:"params"`
}
type Ping_msg struct {
ID float64 `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type Authorize_msg struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params []string `json:"params"`
}
type SubmitHashRate_msg struct {
Id int `json:"id"`
Method string `json:"method"`
Jsonrpc string `json:"jsonrpc"`
Worker string `json:"worker"`
WorkerName string `json:"workerName"`
Params []string `json:"params"`
}
func Conn_tx(conn net.Conn, body []byte) error {
_, err := conn.Write(body)
if err != nil {
conn.Close()
}
return err
}
func Conn_rx(reader *bufio.Reader) (line string) {
line, err := reader.ReadString('\n')
if err != nil {
//if err != io.EOF {
//logg.Error("[server]", zap.String("ReadString", err.Error()))
return ""
//}
}
strings.TrimSpace(line)
return line
}
// miner-->server
func Handle_subscribe(miner *coin.MinerObj, id float64, extranonce1 string) {
miner.TxLock.Lock()
var results [1][2]string
results[0][0] = "mining.notify"
results[0][1] = miner.MinerId
var result [3]interface{}
result[0] = results
//result[1] = miner.Job.Extranonce1
/*be1 := make([]byte, 4)
binary.LittleEndian.PutUint32(be1, (miner.Server.Extranonce1 + 0x81000000))
result[1] = "0000000000000000" + hex.EncodeToString(be1)
miner.Server.Extranonce1++*/
result[1] = extranonce1
//miner.Server.Logg.Debug("[server]", zap.Uint64("extra2", miner.Job.Extranonce2_size))
if miner.Job.Extranonce2_size == 0 {
result[2] = 4
} else {
result[2] = miner.Job.Extranonce2_size
}
var ack Subscribe_reply
ack.ID = id
ack.Result = result
ack.Error = nil
if extranonce1 == "" {
miner.TxLock.Unlock()
Handle_exception(miner, id, MINER_ERR_NOT_SUBSCRIBED)
return
}
body, err := json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
miner.Status = coin.MINER_STATUS_SUBSCRIBED
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
// server-->miner
func Handle_exception(miner *coin.MinerObj, Id float64, errId int) {
miner.TxLock.Lock()
var errors [3]interface{}
errors[0] = errId
switch errId {
case MINER_ERR_UNKNOWN:
errors[1] = "Other/Unknown"
break
case MINER_ERR_NOT_FOUND_JOB:
errors[1] = "Job not found"
break
case MINER_ERR_STALED_JOB:
errors[1] = "Job staled"
break
case MINER_ERR_DUP_SHARE:
errors[1] = "Duplicate share"
break
case MINER_ERR_LOW_DIF_SHARE:
errors[1] = "Low difficulty share"
break
case MINER_ERR_UNAUTH_WORKER:
errors[1] = "Unauthorized worker"
break
case MINER_ERR_NOT_SUBSCRIBED:
errors[1] = "Not subscribed"
break
case MINER_ERR_ILLEGAL_METHOD:
errors[1] = "Illegal method"
break
case MINER_ERR_ILLEGAL_PARARMS:
errors[1] = "Illegal params"
break
case MINER_ERR_IP_BANNED:
errors[1] = "Ip banned"
break
case MINER_ERR_INVALID_USERNAME:
errors[1] = "Invalid username"
break
case MINER_ERR_INTERNAL_ERROR:
errors[1] = "Internal error"
break
case MINER_ERR_TIME_TOO_OLD:
errors[1] = "Time too old"
break
case MINER_ERR_TIME_TOO_NEW:
errors[1] = "Time too new"
break
case MINER_ERR_ILLEGAL_VERMASK:
errors[1] = "Invalid version mask"
break
}
errors[2] = nil
var ack Exception_reply
ack.ID = Id
ack.Result = nil
ack.Error = errors
body, err := json.Marshal(ack)
if err != nil {
miner.Server.Logg.Debug("[server]", zap.String("fail to handle_exception", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
miner.Server.Logg.Debug("[server]", zap.String("fail to handle_exception", err.Error()))
//miner.Server.Miners.Delete(miner.MinerId)
miner.TxLock.Unlock()
return
}
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
}
func Handle_exception_str(miner *coin.MinerObj, Id string, errId int) {
miner.TxLock.Lock()
var errors [3]interface{}
errors[0] = errId
switch errId {
case MINER_ERR_UNKNOWN:
errors[1] = "Other/Unknown"
break
case MINER_ERR_NOT_FOUND_JOB:
errors[1] = "Job not found"
break
case MINER_ERR_STALED_JOB:
errors[1] = "Job staled"
break
case MINER_ERR_DUP_SHARE:
errors[1] = "Duplicate share"
break
case MINER_ERR_LOW_DIF_SHARE:
errors[1] = "Low difficulty share"
break
case MINER_ERR_UNAUTH_WORKER:
errors[1] = "Unauthorized worker"
break
case MINER_ERR_NOT_SUBSCRIBED:
errors[1] = "Not subscribed"
break
case MINER_ERR_ILLEGAL_METHOD:
errors[1] = "Illegal method"
break
case MINER_ERR_ILLEGAL_PARARMS:
errors[1] = "Illegal params"
break
case MINER_ERR_IP_BANNED:
errors[1] = "Ip banned"
break
case MINER_ERR_INVALID_USERNAME:
errors[1] = "Invalid username"
break
case MINER_ERR_INTERNAL_ERROR:
errors[1] = "Internal error"
break
case MINER_ERR_TIME_TOO_OLD:
errors[1] = "Time too old"
break
case MINER_ERR_TIME_TOO_NEW:
errors[1] = "Time too new"
break
case MINER_ERR_ILLEGAL_VERMASK:
errors[1] = "Invalid version mask"
break
}
errors[2] = nil
var ack Exception_reply_str
ack.ID = Id
ack.Result = nil
ack.Error = errors
body, err := json.Marshal(ack)
if err != nil {
miner.Server.Logg.Debug("[server]", zap.String("fail to handle_exception", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
miner.Server.Logg.Debug("[server]", zap.String("fail to handle_exception", err.Error()))
//miner.Server.Miners.Delete(miner.MinerId)
miner.TxLock.Unlock()
return
}
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
}
func InitMinerMhs(miner *coin.MinerObj, user string, minername string, minerindex string, miner_id string, status string, DbCtx *db.DbContext) {
var k string
k = user + "." + minername + "_" + minerindex
m, ok := miner.Server.MMhs.Load(k)
if ok {
var mhs coin.MhsObj = m.(coin.MhsObj)
mhs.StartSubmitTime = time.Now()
mhs.Status = status
mhs.MinerId = miner_id
miner.Server.MMhs.Store(k, m)
//miner.Server.Logg.Info("[server]", zap.String("exist mhs", k))
} else {
var mhs coin.MhsObj
mhs.MinerId = miner_id
mhs.StartSubmitTime = time.Now()
mhs.Status = status
mhs.Name = miner.Name
mhs.User = user
mhs.Miner = minername
mhs.Index = minerindex
mhs.StartDayTime = time.Now()
mhs.Algo = -1
miner.Server.MMhs.Store(k, mhs)
//miner.Server.Logg.Info("[server]", zap.String("new mhs", k))
}
}
func validateUsername(username string, min int, max int) bool {
//
if len(username) < min || len(username) > max {
return false
}
//
/*allowedChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
for _, char := range username {
if !strings.ContainsRune(allowedChars, char) {
return false
}
}*/
//
//pattern := `^[a-zA-Z0-9_]{4,15}$`
pattern := fmt.Sprintf("^[a-zA-Z0-9_]{%d,%d}$", min, max)
match, err := regexp.MatchString(pattern, username)
if err != nil {
//fmt.Println("Error matching pattern:", err)
return false
}
return match
}
func Handle_extranonce(miner *coin.MinerObj, id float64) {
miner.TxLock.Lock()
var ack ExtranonceSubscribeGpu_reply
ack.ID = id
ack.Result = true
ack.Jsonrpc = "2.0"
body, err := json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
func extractAndConvertDiff(password string) (float64, bool) {
index := strings.Index(password, "d=")
if index == -1 {
return 0, false
}
valueStr := password[index+2:]
value, err := strconv.ParseFloat(valueStr, 64)
if err != nil {
return 0, false
}
return value, true
}
func alphExtractAndConvertDiff(str string) (float64, bool) {
if str == "0" {
return 0, false
}
byteTarget := []byte(str)
value := utility.Target2Diff(byteTarget)
return value, true
}
func Handle_submitHashrate(miner *coin.MinerObj, msg string) {
miner.TxLock.Lock()
// randomNumber := rand.Intn(65536)
// hexString := fmt.Sprintf("%04x", randomNumber)
// method := "mining.set extranonce"
// params := []string{hexString}
// setExtranonce_msg := AlphSetExtranonce{Method: method, ID: nil, Params: params}
// ex_msg, err := json.Marshal(setExtranonce_msg)
// fmt.Println("extranonce:", string(ex_msg))
// if err != nil {
// fmt.Println("[server]", zap.String("Marshal", err.Error()))
// }
// Conn_tx(miner.Conn, ex_msg)
prediff, ok := alphExtractAndConvertDiff(msg)
// fmt.Println("难度初始化成功,初始化难度为:", prediff)
if ok {
if (prediff >= miner.Server.Config.Diff.DiffMin) && (prediff <= miner.Server.Config.Diff.DiffMax) {
miner.Difficulty = prediff
}
}
miner.TxLock.Unlock()
}
func AlphSetExtranonce() []byte {
randomNumber := rand.Intn(65536)
hexString := fmt.Sprintf("%04x", randomNumber)
v_json := AlphExtranonce{
ID: nil,
Method: "mining.set_extranonce",
Params: []string{hexString},
}
v_json_bytes, err := json.Marshal(v_json)
if err != nil {
fmt.Println(err)
return nil
}
fmt.Println("发送extranonce:", string(v_json_bytes))
return []byte(string(v_json_bytes) + "\n")
}
// miner-->server
func Handle_authorize(miner *coin.MinerObj, id float64, auth_msg string, DbCtx *db.DbContext) bool {
miner.TxLock.Lock()
var s Authorize_msg
var e error
if e = json.Unmarshal([]byte(auth_msg), &s); e != nil {
miner.Server.Logg.Error("[server]", zap.String("Unmarshal", e.Error()))
}
if s.Params[0] == "" {
miner.Server.Logg.Error("[server]", zap.String("Handle_authorize err", s.Params[0]))
miner.TxLock.Unlock()
return false
}
var strArr []string
if strings.Index(s.Params[0], ".") == -1 {
miner.Server.Logg.Error("[server]", zap.String("user format err", s.Params[0]))
miner.TxLock.Unlock()
return false
}
strArr = strings.Split(s.Params[0], ".") //jjyykk.4x251.dash(jjyykk:user 4x251:miner)
if strArr[0] == "" || strArr[1] == "" {
miner.Server.Logg.Error("[server]", zap.String("user", strArr[0]), zap.String("miner", strArr[1]))
miner.TxLock.Unlock()
return false
}
if (!validateUsername(strArr[0], 3, 15)) || (!validateUsername(strArr[1], 1, 15)) {
miner.TxLock.Unlock()
miner.Server.Logg.Error("[server]", zap.String("invalid user", strArr[0]))
Handle_exception(miner, id, MINER_ERR_INVALID_USERNAME)
return false
}
miner.Server.Logg.Warn("[server]", zap.String("user", strArr[0]), zap.String("miner", strArr[1]))
if miner.Server.Config.Host.Auth {
if !db.CheckUserIsPermitted(strArr[0]) {
miner.TxLock.Unlock()
miner.Server.Logg.Error("[server]", zap.String("not found user", strArr[0]))
Handle_exception(miner, id, MINER_ERR_INVALID_USERNAME)
return false
}
}
if miner.Server.CoinCtx.Coin == "alph" {
// count := 0
// for i := 0; i < 10; i++ {
// if miner.Difficulty != 0 {
// // fmt.Println(miner.User, " ", miner.Miner, "难度初始化成功!")
// break
// }
// time.Sleep(time.Second * 1)
// count++
// }
} else {
prediff, ok := extractAndConvertDiff(s.Params[1])
if ok {
if (prediff >= miner.Server.Config.Diff.DiffMin) && (prediff <= miner.Server.Config.Diff.DiffMax) {
miner.Difficulty = prediff
}
}
}
miner.User = strArr[0]
miner.Miner = strArr[1]
miner.Authorized = true
var ack Authorize_reply
ack.ID = id
ack.Result = true
ack.Error = nil
body, err := json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return false
}
var body_string = string(body) + "\n"
// fmt.Println("身份验证矿池回复:", body_string)
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
miner.Status = coin.MINER_STATUS_AUTHORIZED
miner.TxLock.Unlock()
mlogfile := "./logs/" + miner.Name + "/" + miner.User + "_" + miner.Miner + "_" + fmt.Sprint(miner.MinerIndex) + ".log"
logFile := &lumberjack.Logger{
Filename: mlogfile,
MaxSize: 1,
MaxBackups: 3,
MaxAge: 31,
Compress: true,
}
miner.LogR = logFile
zerolog.TimeFieldFormat = time.RFC3339
miner.Zlog = zerolog.New(logFile).With().Timestamp().Logger()
miner.ZlogInit = true
miner.Zlog.Info().Msg(auth_msg)
miner.Zlog.Info().Msg(body_string)
return true
}
// server-->miner
func Set_difficulty(miner *coin.MinerObj) {
miner.TxLock.Lock()
var msg Difficulty_msg
msg.ID = nil
msg.Method = "mining.set_difficulty"
msg.Params[0] = miner.Difficulty
body, err := json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
/*func Set_difficulty_nexa(miner *coin.MinerObj) {
target_new, err_to := utility.DiffToTarget(miner.Difficulty)
if err_to != nil {
miner.Server.Logg.Error("[server]", zap.String("DiffToTarget", err_to.Error()))
return
}
miner.Target = target_new
miner.TxLock.Lock()
var msg DifficultyNexa_msg
msg.ID = nil
msg.Method = "mining.set_target"
target := fmt.Sprintf("%064x\n", miner.Target.Bytes())
msg.Params[0] = target
body, err := json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}*/
func Set_difficulty_nexa(miner *coin.MinerObj) {
miner.TxLock.Lock()
var msg DifficultyNexaGpu_msg
msg.ID = nil
msg.Method = "mining.set_difficulty"
msg.Params[0] = miner.Difficulty
msg.Jsonrpc = "2.0"
body, err := json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
func removeExpiredJobs(miner *coin.MinerObj, checkExpiration bool) {
var removes []string
miner.LockForJobs.Lock()
defer miner.LockForJobs.Unlock()
for element := miner.JobList.Front(); element != nil; {
entry, isValidEntry := element.Value.(coin.JobListEntry)
next := element.Next()
if isValidEntry {
if checkExpiration {
if time.Since(entry.Ts) >= time.Duration(coin.LOCAL_JOBS_EXPIRED_TIME)*time.Second {
removes = append(removes, entry.Job_id)
miner.JobList.Remove(element)
}
} else {
removes = append(removes, entry.Job_id)
miner.JobList.Remove(element)
}
}
element = next
}
for _, jobID := range removes {
miner.Jobs.Delete(jobID)
}
}
func UpdateJobs(miner *coin.MinerObj) {
/*var removes []string
miner.LockForJobs.Lock()
defer miner.LockForJobs.Unlock()
//for e := miner.JobList.Front(); e != nil; e = e.Next() {
for e := miner.JobList.Front(); e != nil; {
entry, ok := e.Value.(coin.JobListEntry)
if ok {
//if time.Now().Sub(entry.Ts) >= time.Duration(coin.LOCAL_JOBS_EXPIRED_TIME)*time.Second {
if time.Since(entry.Ts) >= time.Duration(coin.LOCAL_JOBS_EXPIRED_TIME)*time.Second {
removes = append(removes, entry.Job_id)
next := e.Next()
miner.JobList.Remove(e)
e = next
continue
}
}
e = e.Next()
}
//miner.LockForJobs.Unlock()
for i := range removes {
miner.Jobs.Delete(removes[i])
}*/
removeExpiredJobs(miner, true)
}
func StaleAllJobs(miner *coin.MinerObj) {
/*var removes []string
miner.LockForJobs.Lock()
defer miner.LockForJobs.Unlock()
//for e := miner.JobList.Front(); e != nil; e = e.Next() {
for e := miner.JobList.Front(); e != nil; {
entry, ok := e.Value.(coin.JobListEntry)
if ok {
removes = append(removes, entry.Job_id)
next := e.Next()
miner.JobList.Remove(e)
e = next
continue
}
e = e.Next()
}
//miner.LockForJobs.Unlock()
for i := range removes {
miner.Jobs.Delete(removes[i])
}*/
removeExpiredJobs(miner, false)
}
func AddAndUpdateJob(miner *coin.MinerObj) {
entry := coin.JobListEntry{
Job_id: miner.Job.Job_id,
Ts: time.Now(),
}
miner.LockForJobs.Lock()
defer miner.LockForJobs.Unlock()
miner.JobList.PushFront(entry)
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
if e := miner.JobList.Back(); e != nil {
if oldestEntry, ok := e.Value.(coin.JobListEntry); ok {
miner.JobList.Remove(e)
miner.Jobs.Delete(oldestEntry.Job_id)
}
}
}
}
func Notify(miner *coin.MinerObj) {
miner.TxLock.Lock()
if !((miner.Status == coin.MINER_STATUS_AUTHORIZED) || (miner.Status == coin.MINER_STATUS_RUNNING)) {
miner.TxLock.Unlock()
return
}
miner.TxLock.Unlock()
if miner.DifficultyNext > -1 {
ratio := miner.DifficultyNext / miner.Difficulty
if ratio > 1.1 || ratio < 0.9 {
miner.Difficulty = miner.DifficultyNext
miner.DifficultyNext = -1
//Set_difficulty(miner)
miner.Server.CoinCtx.SetDifficulty(miner)
} else {
miner.DifficultyNext = -1
}
}
miner.TxLock.Lock()
//log.Println("[server]extra1, id", miner.Job.Extranonce1, miner.Job.Job_id, miner.MinerId)
var params [9]interface{}
var tlist []string = make([]string, 0)
idb := make([]byte, 4)
binary.BigEndian.PutUint32(idb, miner.JobId)
miner.Job.Job_id = hex.EncodeToString(idb)
params[0] = miner.Job.Job_id
if len(miner.Job.PrevblockS) > 0 {
params[1] = miner.Job.PrevblockBig
} else {
p_big := utility.Convert_big_endian(miner.Job.Prevblock.CloneBytes())
params[1] = hex.EncodeToString(p_big)
}
params[2] = miner.Job.Coinbase1
params[3] = miner.Job.Coinbase2
params[4] = tlist
miner.CurHeight = miner.Job.Height
if miner.Job.Transactions != nil {
if len(*miner.Job.Transactions) > 0 {
params[4] = miner.Job.Transactions
/*miner.Server.Logg.Error("[notify]", zap.String("coinbase1", miner.Job.Coinbase1), zap.String("coinbase2", miner.Job.Coinbase2), zap.Uint32("height", miner.Job.Height))
for i := 0; i < len(*miner.Job.Transactions); i++ {
miner.Server.Logg.Error("[notify]", zap.String("trans", (*miner.Job.Transactions)[i]))
}*/
}
}
vb := make([]byte, 4)
binary.LittleEndian.PutUint32(vb, uint32(miner.Job.Version))
params[5] = hex.EncodeToString(vb)
bb := make([]byte, 4)
binary.LittleEndian.PutUint32(bb, miner.Job.Bits)
params[6] = hex.EncodeToString(bb)
t := miner.Job.Timestamp.Unix()
if t > int64(^uint32(0)) {
tb := make([]byte, 8)
binary.LittleEndian.PutUint64(tb, uint64(t))
params[7] = hex.EncodeToString(tb)
} else {
tb := make([]byte, 4)
binary.LittleEndian.PutUint32(tb, uint32(t))
params[7] = hex.EncodeToString(tb)
}
if miner.Reconnect {
params[8] = true
miner.Reconnect = false
} else {
params[8] = miner.Job.IsClean
}
miner.Job.JobDifficulty = miner.Difficulty
//miner.Jobs[miner.Job.Job_id] = miner.Job
miner.Jobs.LoadOrStore(miner.Job.Job_id, miner.Job)
/*var entry coin.JobListEntry
entry.Job_id = miner.Job.Job_id
entry.Ts = time.Now()
miner.LockForJobs.Lock()
miner.JobList.PushFront(entry)
var removes string = ""
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
e := miner.JobList.Back()
entry := e.Value.(coin.JobListEntry)
removes = entry.Job_id
miner.JobList.Remove(e)
}
miner.LockForJobs.Unlock()
if len(removes) > 0 {
miner.Jobs.Delete(removes)
}*/
AddAndUpdateJob(miner)
UpdateJobs(miner)
//miner.LastJobId = miner.Job.Job_id
miner.JobId++
var msg Notify_msg
msg.ID = nil
msg.Method = "mining.notify"
msg.Params = params
body, err := json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
var body_string = string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
//delete(miner.Server.Miners, miner.MinerId)
//miner.Server.Miners.Delete(miner.MinerId)
}
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
type Submit_ack struct {
Result bool `json:"result"`
ID float64 `json:"id"`
Error interface{} `json:"error"`
}
type Submit_ack_str struct {
Result bool `json:"result"`
ID string `json:"id"`
Error interface{} `json:"error"`
}
// server-->miner
func parse_miner_notify(miner *coin.MinerObj, msg msg.StratumJob) int {
miner.Job.Version = msg.Version
miner.Job.Prevblock = msg.Prevblock
miner.Job.Coinbase1 = msg.Coinbase1
miner.Job.Coinbase2 = msg.Coinbase2
miner.Job.Bits = msg.Bits
miner.Job.Timestamp = msg.Timestamp
miner.Job.Target = msg.Target
miner.Job.PrevblockS = msg.PrevblockS
miner.Job.PrevblockBig = msg.PrevblockBig
miner.Job.Transactions = msg.Transactions
miner.Job.BitsS = msg.BitsS
miner.Job.Height = msg.Height
miner.Job.Extranonce2_size = msg.Extranonce2_size
miner.Job.TransData = msg.TransData
miner.Job.Payloadstart = msg.Payloadstart
miner.Job.Segwit = msg.Segwit
miner.Job.IsClean = msg.IsClean
miner.Job.Mintime = msg.Mintime
miner.ServerTargetS = msg.Target
vb := make([]byte, 4)
binary.LittleEndian.PutUint32(vb, uint32(msg.Version))
vBuffer := bytes.NewBuffer(vb)
binary.Read(vBuffer, binary.BigEndian, &(miner.Version))
//log.Printf("version %04x, %04x", miner.Version, msg.Version)
//miner.Server.Logg.Info("[server]", zap.Int32("miner.Version", miner.Version), zap.Int32("msg.Version", msg.Version))
return 1
}
func Send_reconnect_msg(miner *coin.MinerObj) bool {
var msg Reconnect_msg
msg.ID = nil
msg.Method = "client.reconnect"
msg.Params = nil
body, err := json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("failed to Send_reconnect_msg", err.Error()), zap.String("user", miner.User), zap.String("miner", miner.Miner))
return false
}
body_string := string(body) + "\n"
err = Conn_tx(miner.Conn, []byte(body_string))
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("failed to Send_reconnect_msg", err.Error()), zap.String("user", miner.User), zap.String("miner", miner.Miner))
return false
}
return true
}
func UpdateMhs(miner *coin.MinerObj, accept bool, diff float64, algo int, DbCtx *db.DbContext) {
var k string
k = miner.User + "." + miner.Miner + "_" + fmt.Sprint(miner.MinerIndex)
v, ok := miner.Server.MMhs.Load(k)
if ok {
var m coin.MhsObj = v.(coin.MhsObj)
var item coin.MhsItem
item.Tt = time.Now()
item.Diff = diff
if accept {
m.Accepts = append(m.Accepts, item)
} else {
m.Rejects = append(m.Rejects, item)
}
m.Status = miner.Status
m.MinerId = miner.MinerId
if m.Algo < 0 {
m.Algo = algo
}
miner.Server.MMhs.Store(k, m)
var mhsItem cache.CacheMhsItem
mhsItem.Tt = item.Tt.Format(time.RFC3339)
mhsItem.Diff = item.Diff
if accept {
cache.StoreMhsCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts", mhsItem)
} else {
cache.StoreMhsCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects", mhsItem)
}
//miner.Server.Logg.Info("[mhs]", zap.String("UpdateMhs", k), zap.Int("accepts", len(m.Accepts)), zap.Int("rejects", len(m.Rejects)), zap.Int("algo", m.Algo))
}
}
func UpdateMhsStatus(miner *coin.MinerObj, DbCtx *db.DbContext) {
var k string
k = miner.User + "." + miner.Miner + "_" + fmt.Sprint(miner.MinerIndex)
v, ok := miner.Server.MMhs.Load(k)
if ok {
var m coin.MhsObj = v.(coin.MhsObj)
m.Status = miner.Status
m.MinerId = miner.MinerId
miner.Server.MMhs.Store(k, m)
//miner.Server.Logg.Info("[mhs]", zap.String("UpdateMhsStatus", k), zap.String("update status", m.Status))
}
}