1098 lines
28 KiB
Go
1098 lines
28 KiB
Go
|
// 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))
|
||
|
}
|
||
|
}
|