1297 lines
34 KiB
Go
1297 lines
34 KiB
Go
|
// stratum.go
|
||
|
package stratum
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
|
||
|
"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/google/uuid"
|
||
|
"github.com/rs/zerolog"
|
||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||
|
|
||
|
"sync/atomic"
|
||
|
|
||
|
//"container/list"
|
||
|
|
||
|
"sync"
|
||
|
|
||
|
"go.uber.org/zap"
|
||
|
|
||
|
"log"
|
||
|
)
|
||
|
|
||
|
const STRATUM_PING_INTERVAL_CNT int = 3
|
||
|
|
||
|
// const STRATUM_PING_FAILED_MAX_CNT int = STRATUM_PING_INTERVAL_CNT * 4
|
||
|
const STRATUM_PING_FAILED_MAX_CNT int = 70
|
||
|
|
||
|
// 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 Sha3xAuthorize_reply struct {
|
||
|
Jsonrpc string `json:"jsonrpc"`
|
||
|
Result Sha3xAuthorize_result_msg `json:"result"`
|
||
|
ID float64 `json:"id"`
|
||
|
}
|
||
|
|
||
|
type Authorize_reply_str struct {
|
||
|
Result bool `json:"result"`
|
||
|
ID string `json:"id"`
|
||
|
Error interface{} `json:"error"`
|
||
|
}
|
||
|
|
||
|
type Submit_nonce struct {
|
||
|
ID interface{} `json:"id"`
|
||
|
Method string `json:"method"`
|
||
|
Params []string `json:"params"`
|
||
|
}
|
||
|
|
||
|
type Sha3xSubmit_params struct {
|
||
|
Id string `json:"id"`
|
||
|
Job_id string `json:"job_id"`
|
||
|
Nonce string `json:"nonce"`
|
||
|
Result string `json:"result"`
|
||
|
}
|
||
|
|
||
|
type Sha3xSubmit_nonce struct {
|
||
|
ID interface{} `json:"id"`
|
||
|
Method string `json:"method"`
|
||
|
Params Sha3xSubmit_params `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 Sha3xAuthorize_params_msg struct {
|
||
|
Login string `json:"login"`
|
||
|
Pass string `json:"pass"`
|
||
|
Agent string `json:"agent"`
|
||
|
}
|
||
|
|
||
|
type Sha3xAuthorize_msg struct {
|
||
|
ID interface{} `json:"id"`
|
||
|
Method string `json:"method"`
|
||
|
Params Sha3xAuthorize_params_msg `json:"params"`
|
||
|
}
|
||
|
|
||
|
type Sha3xAuthorize_job_msg struct {
|
||
|
Algo string `json:"algo"`
|
||
|
Blob string `json:"blob"`
|
||
|
Height uint32 `json:"height"`
|
||
|
Job_id string `json:"job_id"`
|
||
|
Target string `json:"target"`
|
||
|
Xn string `json:"xn"`
|
||
|
}
|
||
|
|
||
|
type Sha3xAuthorize_result_msg struct {
|
||
|
Id string `json:"id"`
|
||
|
Job Sha3xAuthorize_job_msg `json:"job"`
|
||
|
Status string `json:"status"`
|
||
|
}
|
||
|
|
||
|
type MoneroAuthorize_reply struct {
|
||
|
ID float64 `json:"id"`
|
||
|
Jsonrpc string `json:"jsonrpc"`
|
||
|
Error interface{} `json:"error"`
|
||
|
Result MoneroJob `json:"result"`
|
||
|
}
|
||
|
|
||
|
type MoneroJob struct {
|
||
|
Id string `json:"id"`
|
||
|
Job struct {
|
||
|
Id string `json:"id"`
|
||
|
JobId string `json:"job_id"`
|
||
|
Blob string `json:"blob"`
|
||
|
Target string `json:"target"`
|
||
|
SeedHash string `json:"seed_hash"`
|
||
|
NextSeedHash string `json:"next_seed_hash"`
|
||
|
Algo string `json:"algo"`
|
||
|
Height uint64 `json:"height"`
|
||
|
} `json:"job"`
|
||
|
Status string `json:"status"`
|
||
|
}
|
||
|
|
||
|
type KeepAlived_resp struct {
|
||
|
ID int `json:"id"`
|
||
|
Jsonrpc string `json:"jsonrpc"`
|
||
|
Result struct {
|
||
|
Status string `json:"status"`
|
||
|
} `json:"result"`
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
//mhs.LockForMhs.Lock()
|
||
|
//mhs.Accepts = nil
|
||
|
//mhs.Rejects = nil
|
||
|
//mhs.LockForMhs.Unlock()
|
||
|
|
||
|
miner.Server.MMhs.Store(k, mhs)
|
||
|
//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
|
||
|
mhs.Release = false
|
||
|
|
||
|
var mhs_lock sync.Mutex
|
||
|
mhs.LockForMhs = mhs_lock
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
var s_sha3x Sha3xAuthorize_msg
|
||
|
|
||
|
if miner.Name == "nexa" {
|
||
|
if e = json.Unmarshal([]byte(auth_msg), &s); e != nil {
|
||
|
miner.Server.Logg.Error("[server]", zap.String("Unmarshal", e.Error()))
|
||
|
}
|
||
|
} else if miner.Name == "sha3x" || miner.Name == "monero" {
|
||
|
if e = json.Unmarshal([]byte(auth_msg), &s_sha3x); e != nil {
|
||
|
miner.Server.Logg.Error("[server]", zap.String("Unmarshal", e.Error()))
|
||
|
}
|
||
|
s.Params = append(s.Params, s_sha3x.Params.Login)
|
||
|
s.Params = append(s.Params, s_sha3x.Params.Pass)
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
|
||
|
if len(s.Params) < 2 {
|
||
|
miner.Server.Logg.Error("[server]", zap.String("Handle_authorize err", s.Params[0]))
|
||
|
miner.TxLock.Unlock()
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
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.Server.MinerType) {
|
||
|
_, ok := miner.Server.CacheUsers.Load(strArr[0])
|
||
|
if ok {
|
||
|
} else {
|
||
|
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
|
||
|
}
|
||
|
} else {
|
||
|
_, ok := miner.Server.CacheUsers.Load(strArr[0])
|
||
|
if ok {
|
||
|
} else {
|
||
|
if atomic.LoadInt32(&(miner.Server.CacheUsersCnt)) < 1000 {
|
||
|
miner.Server.CacheUsers.Store(strArr[0], strArr[0])
|
||
|
atomic.AddInt32(&(miner.Server.CacheUsersCnt), 1)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
miner.Session = uuid.New().String()
|
||
|
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 body_string = ""
|
||
|
|
||
|
if miner.Name == "nexa" {
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
} else if miner.Name == "sha3x" {
|
||
|
var sha3x_ack Sha3xAuthorize_reply
|
||
|
sha3x_ack.Jsonrpc = "2.0"
|
||
|
|
||
|
idb := make([]byte, 4)
|
||
|
binary.BigEndian.PutUint32(idb, miner.JobId)
|
||
|
miner.Job.Job_id = hex.EncodeToString(idb)
|
||
|
|
||
|
sha3x_ack.ID = id
|
||
|
sha3x_ack.Result.Status = "OK"
|
||
|
sha3x_ack.Result.Id = miner.Job.Job_id
|
||
|
sha3x_ack.Result.Job.Algo = "sha3x"
|
||
|
sha3x_ack.Result.Job.Blob = miner.Sha3xJob.Header
|
||
|
sha3x_ack.Result.Job.Height = miner.Sha3xJob.Height
|
||
|
sha3x_ack.Result.Job.Job_id = miner.Job.Job_id
|
||
|
//target_str, _ := ReverseHexStringByByte(miner.Sha3xJob.Target)
|
||
|
//sha3x_ack.Result.Job.Target = target_str[48:]
|
||
|
target_new, _ := utility.DiffToTarget(miner.Difficulty)
|
||
|
target_str := fmt.Sprintf("%064x", target_new.Bytes())
|
||
|
target_strr, strerr := ReverseHexStringByByte(target_str)
|
||
|
if strerr != nil {
|
||
|
println("ReverseHexStringByByte", strerr.Error())
|
||
|
}
|
||
|
//println("target=", target_str, "r=", target_strr)
|
||
|
sha3x_ack.Result.Job.Target = target_strr[48:]
|
||
|
sha3x_ack.Result.Job.Xn = miner.Sha3xJob.Extranonce1[:4]
|
||
|
|
||
|
body, err := json.Marshal(sha3x_ack)
|
||
|
|
||
|
if err != nil {
|
||
|
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
|
||
|
miner.TxLock.Unlock()
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
} else if miner.Name == "monero" {
|
||
|
var monero_ack MoneroAuthorize_reply
|
||
|
monero_ack.Jsonrpc = "2.0"
|
||
|
monero_ack.ID = id
|
||
|
monero_ack.Result.Status = "OK"
|
||
|
monero_ack.Result.Id = miner.Session
|
||
|
monero_ack.Result.Job.Id = miner.Session
|
||
|
monero_ack.Result.Job.JobId = miner.MoneroJob.JobId
|
||
|
monero_ack.Result.Job.Algo = "rx/0"
|
||
|
target_new, _ := utility.MoneroDiffToTarget(miner.Difficulty)
|
||
|
target_str := fmt.Sprintf("%064x", target_new.Bytes())
|
||
|
target_strr, strerr := ReverseHexStringByByte(target_str)
|
||
|
if strerr != nil {
|
||
|
println("ReverseHexStringByByte", strerr.Error())
|
||
|
}
|
||
|
monero_ack.Result.Job.Target = target_strr[48:]
|
||
|
monero_ack.Result.Job.Blob = miner.MoneroJob.BlockhashingBlob
|
||
|
monero_ack.Result.Job.Height = miner.MoneroJob.Height
|
||
|
monero_ack.Result.Job.SeedHash = miner.MoneroJob.SeedHash
|
||
|
monero_ack.Result.Job.NextSeedHash = ""
|
||
|
body, err := json.Marshal(monero_ack)
|
||
|
if err != nil {
|
||
|
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
|
||
|
miner.TxLock.Unlock()
|
||
|
return false
|
||
|
}
|
||
|
miner.Jobs.LoadOrStore(miner.MoneroJob.JobId, miner.MoneroJob)
|
||
|
body_string = string(body) + "\n"
|
||
|
// fmt.Println(body_string)
|
||
|
err = Conn_tx(miner.Conn, []byte(body_string))
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
|
||
|
//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) {
|
||
|
miner.LockForJobs.Lock()
|
||
|
defer miner.LockForJobs.Unlock()
|
||
|
if checkExpiration {
|
||
|
var removes []string
|
||
|
|
||
|
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)
|
||
|
element.Value = nil
|
||
|
}
|
||
|
//} else {
|
||
|
//removes = append(removes, entry.Job_id)
|
||
|
//miner.JobList.Remove(element)
|
||
|
//}
|
||
|
}
|
||
|
element = next
|
||
|
}
|
||
|
|
||
|
for _, jobID := range removes {
|
||
|
//miner.Jobs.Store(jobID, nil)
|
||
|
miner.Jobs.Delete(jobID)
|
||
|
}
|
||
|
} else {
|
||
|
//miner.JobList.Init()
|
||
|
for elem := miner.JobList.Front(); elem != nil; {
|
||
|
next := elem.Next()
|
||
|
miner.JobList.Remove(elem)
|
||
|
elem.Value = nil
|
||
|
elem = next
|
||
|
}
|
||
|
//miner.Jobs = sync.Map{}
|
||
|
var keysJobs []interface{}
|
||
|
miner.Jobs.Range(func(k, v interface{}) bool {
|
||
|
keysJobs = append(keysJobs, k)
|
||
|
return true
|
||
|
})
|
||
|
for _, key := range keysJobs {
|
||
|
//miner.Jobs.Store(key, nil)
|
||
|
miner.Jobs.Delete(key)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var total_jobs int32 = 0
|
||
|
miner.Jobs.Range(func(k, v interface{}) bool {
|
||
|
total_jobs += 1
|
||
|
return true
|
||
|
})
|
||
|
total_entries := 0
|
||
|
for e := miner.JobList.Front(); e != nil; e = e.Next() {
|
||
|
total_entries++
|
||
|
}
|
||
|
// log.Println("jobs: ", miner.User, miner.Miner, total_jobs, total_entries)
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
|
||
|
miner.LockForJobs.Lock()
|
||
|
defer miner.LockForJobs.Unlock()
|
||
|
|
||
|
currentLen := miner.JobList.Len()
|
||
|
maxSize := int(coin.LOCAL_JOBS_TOTAL_SIZE)
|
||
|
needRemove := currentLen + 1 - maxSize
|
||
|
|
||
|
for cnt := 0; cnt < needRemove; cnt++ {
|
||
|
//if miner.JobList.Len()+1 > 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)
|
||
|
e.Value = nil
|
||
|
//miner.Jobs.Store(oldestEntry.Job_id, nil)
|
||
|
miner.Jobs.Delete(oldestEntry.Job_id)
|
||
|
} else {
|
||
|
miner.JobList.Remove(e)
|
||
|
e.Value = nil
|
||
|
}
|
||
|
}
|
||
|
//}
|
||
|
}
|
||
|
|
||
|
entry := coin.JobListEntry{
|
||
|
Job_id: miner.Job.Job_id,
|
||
|
Ts: time.Now(),
|
||
|
}
|
||
|
miner.JobList.PushFront(entry)
|
||
|
|
||
|
}
|
||
|
|
||
|
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 {
|
||
|
if v != nil {
|
||
|
var m *coin.MhsObj = v.(*coin.MhsObj)
|
||
|
if m != nil {
|
||
|
var item coin.MhsItem
|
||
|
item.Tt = time.Now()
|
||
|
item.Diff = diff
|
||
|
m.LockForMhs.Lock()
|
||
|
if accept {
|
||
|
m.Accepts = append(m.Accepts, item)
|
||
|
} else {
|
||
|
m.Rejects = append(m.Rejects, item)
|
||
|
}
|
||
|
m.LockForMhs.Unlock()
|
||
|
m.Status = miner.Status
|
||
|
m.MinerId = miner.MinerId
|
||
|
if m.Algo < 0 {
|
||
|
m.Algo = algo
|
||
|
}
|
||
|
log.Println("miner:", k, len(m.Accepts), len(m.Rejects))
|
||
|
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 {
|
||
|
if v != nil {
|
||
|
var m *coin.MhsObj = v.(*coin.MhsObj)
|
||
|
if m != nil {
|
||
|
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))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func SetMhsRelease(miner *coin.MinerObj) {
|
||
|
var k string
|
||
|
|
||
|
k = miner.User + "." + miner.Miner + "_" + fmt.Sprint(miner.MinerIndex)
|
||
|
|
||
|
v, ok := miner.Server.MMhs.Load(k)
|
||
|
if ok {
|
||
|
if v != nil {
|
||
|
var m *coin.MhsObj = v.(*coin.MhsObj)
|
||
|
if m != nil {
|
||
|
//if m.Status == coin.MINER_STATUS_DISCONNECTED {
|
||
|
//m.Release = true
|
||
|
m.LockForMhs.Lock()
|
||
|
m.Accepts = nil
|
||
|
m.Rejects = nil
|
||
|
m.LockForMhs.Unlock()
|
||
|
miner.Server.MMhs.Store(k, m)
|
||
|
//}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//miner.Server.MMhs.Store(k, nil)
|
||
|
miner.Server.MMhs.Delete(k)
|
||
|
}
|
||
|
|
||
|
func ReverseHexStringByByte(hexStr string) (string, error) {
|
||
|
|
||
|
bytes, err := hex.DecodeString(hexStr)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 {
|
||
|
bytes[i], bytes[j] = bytes[j], bytes[i]
|
||
|
}
|
||
|
|
||
|
return hex.EncodeToString(bytes), nil
|
||
|
}
|