// 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)) } }