1627 lines
53 KiB
Go
1627 lines
53 KiB
Go
|
// server.go
|
|||
|
package server
|
|||
|
|
|||
|
/*
|
|||
|
#cgo LDFLAGS: -lzmq
|
|||
|
#include <zmq.h>
|
|||
|
|
|||
|
void set_max_msg_size(void *ctx, int size) {
|
|||
|
zmq_ctx_set(ctx, ZMQ_MAXMSGSIZE, size);
|
|||
|
}
|
|||
|
*/
|
|||
|
import "C"
|
|||
|
import (
|
|||
|
"bufio"
|
|||
|
"container/list"
|
|||
|
"context"
|
|||
|
|
|||
|
//"encoding/binary"
|
|||
|
//"encoding/hex"
|
|||
|
"encoding/json"
|
|||
|
"fmt"
|
|||
|
"io"
|
|||
|
"io/ioutil"
|
|||
|
"log"
|
|||
|
|
|||
|
//"math/big"
|
|||
|
"net"
|
|||
|
"os"
|
|||
|
"os/signal"
|
|||
|
"pool/internal/db"
|
|||
|
"pool/internal/server/coin"
|
|||
|
"pool/internal/server/dbif"
|
|||
|
|
|||
|
"math/rand"
|
|||
|
"pool/internal/cache"
|
|||
|
"pool/internal/server/alph"
|
|||
|
"pool/internal/server/enx"
|
|||
|
"pool/internal/server/nexa"
|
|||
|
"pool/internal/stratum"
|
|||
|
"pool/internal/utility"
|
|||
|
"runtime"
|
|||
|
"strings"
|
|||
|
"sync"
|
|||
|
"sync/atomic"
|
|||
|
"syscall"
|
|||
|
"time"
|
|||
|
|
|||
|
"github.com/rs/zerolog"
|
|||
|
|
|||
|
"github.com/redis/go-redis/v9"
|
|||
|
|
|||
|
"go.uber.org/zap"
|
|||
|
"gopkg.in/natefinch/lumberjack.v2"
|
|||
|
)
|
|||
|
|
|||
|
var logg *zap.Logger
|
|||
|
var logr *lumberjack.Logger
|
|||
|
var ServerCtx coin.ServerContext
|
|||
|
|
|||
|
func InitConfig() *coin.ServerConfig {
|
|||
|
var config coin.ServerConfig
|
|||
|
data, err := ioutil.ReadFile("server.conf")
|
|||
|
if err != nil {
|
|||
|
panic(err.Error())
|
|||
|
}
|
|||
|
if err = json.Unmarshal(data, &config); err != nil {
|
|||
|
panic(err.Error())
|
|||
|
}
|
|||
|
return &config
|
|||
|
}
|
|||
|
|
|||
|
/*func ServerLivingHandler(server *coin.ServerContext) {
|
|||
|
timer := time.NewTimer(time.Duration(600) * time.Second)
|
|||
|
defer timer.Stop()
|
|||
|
for {
|
|||
|
select {
|
|||
|
case living := <-server.AlivingChan:
|
|||
|
if !living {
|
|||
|
timer.Stop()
|
|||
|
//log.Println("ServerLivingHandler exited, living: false")
|
|||
|
server.Logg.Error("[server]", zap.String("ServerLivingHandler exited", "living: false"))
|
|||
|
return
|
|||
|
} else {
|
|||
|
if !timer.Stop() {
|
|||
|
<-timer.C
|
|||
|
}
|
|||
|
timer.Reset(time.Duration(600) * time.Second)
|
|||
|
}
|
|||
|
server.Logg.Error("[server]", zap.String("ServerLivingHandler exited", "living: true"))
|
|||
|
case <-timer.C:
|
|||
|
//log.Println("ServerLivingHandler exited, timer expired")
|
|||
|
server.Logg.Error("[server]", zap.String("ServerLivingHandler exited", "timer expired"))
|
|||
|
server.LiveingExpired = true
|
|||
|
server.DbCtx.AppExit <- true
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
func ServerLivingHandler(server *coin.ServerContext) {
|
|||
|
var to_cnt int = 0
|
|||
|
for {
|
|||
|
flagAliving := atomic.LoadInt32(&(server.FlagAliving))
|
|||
|
flagExit := atomic.LoadInt32(&(server.FlagAlivingExit))
|
|||
|
|
|||
|
if flagExit == 1 {
|
|||
|
server.Logg.Error("[server]", zap.String("ServerLivingHandler exited", "exit"))
|
|||
|
break
|
|||
|
}
|
|||
|
|
|||
|
if flagAliving == 0 {
|
|||
|
if to_cnt > server.CoinCtx.GetBlockInterval() {
|
|||
|
server.Logg.Error("[server]", zap.String("ServerLivingHandler exited", "timer expired"))
|
|||
|
server.DbCtx.AppExit <- true
|
|||
|
break
|
|||
|
}
|
|||
|
to_cnt++
|
|||
|
} else {
|
|||
|
to_cnt = 0
|
|||
|
atomic.StoreInt32(&(server.FlagAliving), 0)
|
|||
|
}
|
|||
|
|
|||
|
time.Sleep(time.Second)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var coinobjs = []coin.CoinObj{
|
|||
|
{
|
|||
|
Coin: "nexa",
|
|||
|
Init: nexa.Init,
|
|||
|
Start: nexa.Start,
|
|||
|
Stop: nexa.Stop,
|
|||
|
InitMiner: nexa.InitMiner,
|
|||
|
HandleMinerSubscribe: nexa.HandleMinerSubscribe,
|
|||
|
HandleMinerAuth: nexa.HandleMinerAuth,
|
|||
|
HandleMinerSubmit: nexa.HandleMinerSubmit,
|
|||
|
SetDifficulty: nexa.SetDifficulty,
|
|||
|
Notify: nexa.Notify,
|
|||
|
HandleJobMsg: nexa.HandleJobMsg,
|
|||
|
IsMhsLow: nexa.IsMhsLow,
|
|||
|
GetBlockInterval: nexa.GetBlockInterval,
|
|||
|
},
|
|||
|
{
|
|||
|
Coin: "alph",
|
|||
|
Init: alph.Init,
|
|||
|
Start: alph.Start,
|
|||
|
Stop: alph.Stop,
|
|||
|
InitMiner: alph.InitMiner,
|
|||
|
HandleMinerSubscribe: alph.HandleMinerSubscribe,
|
|||
|
HandleMinerAuth: alph.HandleMinerAuth,
|
|||
|
HandleMinerSubmit: alph.HandleMinerSubmit,
|
|||
|
SetDifficulty: alph.SetDifficulty,
|
|||
|
Notify: alph.Notify,
|
|||
|
HandleJobMsg: alph.HandleJobMsg,
|
|||
|
IsMhsLow: alph.IsMhsLow,
|
|||
|
GetBlockInterval: alph.GetBlockInterval,
|
|||
|
},
|
|||
|
{
|
|||
|
Coin: "enx",
|
|||
|
Init: enx.Init,
|
|||
|
Start: enx.Start,
|
|||
|
Stop: enx.Stop,
|
|||
|
InitMiner: enx.InitMiner,
|
|||
|
HandleMinerSubscribe: enx.HandleMinerSubscribe,
|
|||
|
HandleMinerAuth: enx.HandleMinerAuth,
|
|||
|
HandleMinerSubmit: enx.HandleMinerSubmit,
|
|||
|
SetDifficulty: enx.SetDifficulty,
|
|||
|
Notify: enx.Notify,
|
|||
|
HandleJobMsg: enx.HandleJobMsg,
|
|||
|
IsMhsLow: enx.IsMhsLow,
|
|||
|
GetBlockInterval: enx.GetBlockInterval,
|
|||
|
},
|
|||
|
}
|
|||
|
|
|||
|
func register_signal(dbctx *db.DbContext) {
|
|||
|
signal_ch := make(chan os.Signal, 1)
|
|||
|
signal.Notify(signal_ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
|||
|
go signal_handle(signal_ch, dbctx)
|
|||
|
}
|
|||
|
|
|||
|
func signal_handle(signal_ch chan os.Signal, DbCtx *db.DbContext) {
|
|||
|
for s := range signal_ch {
|
|||
|
switch s {
|
|||
|
case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
|
|||
|
DbCtx.AppExit <- true
|
|||
|
//log.Println("stop signal!")
|
|||
|
if logg != nil {
|
|||
|
logg.Error("[server]", zap.String("signal", "stop"))
|
|||
|
}
|
|||
|
default:
|
|||
|
//fmt.Println("other signal", s)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func register_user_signal(server *coin.ServerContext) {
|
|||
|
signal_ch := make(chan os.Signal, 1)
|
|||
|
signal.Notify(signal_ch, syscall.SIGUSR1)
|
|||
|
go user_signal_handle(signal_ch, server)
|
|||
|
}
|
|||
|
|
|||
|
func user_signal_handle(signal_ch chan os.Signal, ctx *coin.ServerContext) {
|
|||
|
for s := range signal_ch {
|
|||
|
switch s {
|
|||
|
case syscall.SIGUSR1:
|
|||
|
//log.Println("user signal 1!")
|
|||
|
if logg != nil {
|
|||
|
logg.Error("[server]", zap.String("signal", "user 1"))
|
|||
|
}
|
|||
|
default:
|
|||
|
//fmt.Println("other signal", s)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func do_keepalive_ping(server *coin.ServerContext) {
|
|||
|
timer := time.NewTimer(time.Duration(5) * time.Second)
|
|||
|
defer timer.Stop()
|
|||
|
for {
|
|||
|
if server.ExitFlag {
|
|||
|
break
|
|||
|
}
|
|||
|
select {
|
|||
|
//case <-time.After(time.Duration(5) * time.Second):
|
|||
|
case <-timer.C:
|
|||
|
timer.Reset(time.Duration(5) * time.Second)
|
|||
|
server.Miners.Range(func(k, v interface{}) bool {
|
|||
|
m, ok := v.(*(coin.MinerObj))
|
|||
|
if ok {
|
|||
|
//if m.User == "" || m.Miner == "" {
|
|||
|
//return true
|
|||
|
//}
|
|||
|
//if (m.Status != coin.MINER_STATUS_AUTHORIZED) && (m.Status != coin.MINER_STATUS_RUNNING) {
|
|||
|
//return true
|
|||
|
//}
|
|||
|
if time.Now().Sub(m.ConnSetupTime).Seconds() < 10 {
|
|||
|
return true
|
|||
|
}
|
|||
|
if m.RecvedLiveAck {
|
|||
|
m.RecvedLiveAck = false
|
|||
|
m.PongFailCnt = 0
|
|||
|
} else {
|
|||
|
m.PongFailCnt = m.PongFailCnt + 1
|
|||
|
//log.Println("ping", m.RecvedLiveAck, m.PongFailCnt)
|
|||
|
if m.PongFailCnt > stratum.STRATUM_PING_FAILED_MAX_CNT {
|
|||
|
//server.Logg.Error("[server]", zap.String("ping fail", fmt.Sprint(m.PongFailCnt)+m.User+"."+m.Miner))
|
|||
|
if m.ZlogInit {
|
|||
|
m.Zlog.Info().Msg("ping failed " + fmt.Sprint(m.PongFailCnt) + " " + m.User + "." + m.Miner)
|
|||
|
}
|
|||
|
m.Conn.Close()
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
m.PingCnt = m.PingCnt + 1
|
|||
|
if m.PingCnt > stratum.STRATUM_PING_INTERVAL_CNT {
|
|||
|
m.PingCnt = 0
|
|||
|
var msg stratum.Ping_msg
|
|||
|
msg.ID = m.KeepliveCnt
|
|||
|
msg.Method = "mining.ping"
|
|||
|
msg.Params = nil
|
|||
|
m.KeepliveCnt++
|
|||
|
body, err := json.Marshal(msg)
|
|||
|
if err != nil {
|
|||
|
//server.Logg.Error("[server]", zap.String("failed to do_keeplive_ping", err.Error()), zap.String("user", m.User), zap.String("miner", m.Miner))
|
|||
|
if m.ZlogInit {
|
|||
|
m.Zlog.Info().Msg("failed to Marshal " + err.Error() + " " + m.User + "." + m.Miner)
|
|||
|
}
|
|||
|
return true
|
|||
|
}
|
|||
|
body_string := string(body) + "\n"
|
|||
|
err = stratum.Conn_tx(m.Conn, []byte(body_string))
|
|||
|
if err != nil {
|
|||
|
//server.Logg.Error("[server]", zap.String("failed to do_keeplive_ping", err.Error()), zap.String("user", m.User), zap.String("miner", m.Miner))
|
|||
|
if m.ZlogInit {
|
|||
|
m.Zlog.Info().Msg("failed to do_keeplive_ping " + err.Error() + " " + m.User + "." + m.Miner)
|
|||
|
}
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
return true
|
|||
|
})
|
|||
|
case <-server.ExitPingChan:
|
|||
|
//log.Println("exit do_keepalive_ping!")
|
|||
|
server.Logg.Error("[server]", zap.String("do_keepalive_ping", "exit by chan"))
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func HandleKeepAlive(server *coin.ServerContext) {
|
|||
|
do_keepalive_ping(server)
|
|||
|
}
|
|||
|
|
|||
|
func init_miner(miner *coin.MinerObj, conn net.Conn, server *coin.ServerContext) {
|
|||
|
atomic.StoreInt32(&(miner.NeedExit), 0)
|
|||
|
miner.ZlogInit = false
|
|||
|
//miner.EndCh = make(chan bool, 32768)
|
|||
|
miner.FromIP = conn.RemoteAddr().String()
|
|||
|
est_time := time.Now()
|
|||
|
miner.OnlineTime = est_time
|
|||
|
miner.OfflineTime = est_time
|
|||
|
miner.Retry = 0
|
|||
|
miner.DurationTime = 0
|
|||
|
miner.MinerIndex = server.MinerIndex
|
|||
|
//server.MinerIndex++
|
|||
|
|
|||
|
miner.ErrStaleds = 0
|
|||
|
miner.ErrLowDiffs = 0
|
|||
|
miner.ErrDuplicates = 0
|
|||
|
miner.ErrFormats = 0
|
|||
|
miner.ErrOthers = 0
|
|||
|
|
|||
|
miner.IsDisabled = false
|
|||
|
|
|||
|
miner.Submits = 0
|
|||
|
miner.Blocks = 0
|
|||
|
miner.Orphans = 0
|
|||
|
|
|||
|
miner.Accepts5M = 0
|
|||
|
miner.Accepts15M = 0
|
|||
|
miner.Accepts30M = 0
|
|||
|
miner.Accepts1h = 0
|
|||
|
miner.Accepts3h = 0
|
|||
|
miner.Accepts6h = 0
|
|||
|
miner.Accepts12h = 0
|
|||
|
miner.Accepts24h = 0
|
|||
|
miner.Accepts48h = 0
|
|||
|
|
|||
|
miner.Rejects5M = 0
|
|||
|
miner.Rejects15M = 0
|
|||
|
miner.Rejects30M = 0
|
|||
|
miner.Rejects1h = 0
|
|||
|
miner.Rejects3h = 0
|
|||
|
miner.Rejects6h = 0
|
|||
|
miner.Rejects12h = 0
|
|||
|
miner.Rejects24h = 0
|
|||
|
miner.Rejects48h = 0
|
|||
|
|
|||
|
miner.Mhs5M = 0
|
|||
|
miner.Mhs15M = 0
|
|||
|
miner.Mhs30M = 0
|
|||
|
miner.Mhs1h = 0
|
|||
|
miner.Mhs3h = 0
|
|||
|
miner.Mhs6h = 0
|
|||
|
miner.Mhs12h = 0
|
|||
|
miner.Mhs24h = 0
|
|||
|
miner.Mhs48h = 0
|
|||
|
|
|||
|
miner.Reward = 0
|
|||
|
miner.Fee = 0
|
|||
|
|
|||
|
miner.Name = server.MinerType
|
|||
|
miner.MinerId = coin.Guid()
|
|||
|
miner.Accepts = 0
|
|||
|
miner.Rejects = 0
|
|||
|
miner.M5Accepts = 0
|
|||
|
miner.AverageHashrate = 0
|
|||
|
miner.M5Hashrate = 0
|
|||
|
miner.Conn = conn
|
|||
|
|
|||
|
miner.KeepliveCnt = 0
|
|||
|
miner.RecvedLiveAck = false
|
|||
|
miner.PongFailCnt = 0
|
|||
|
miner.PingCnt = 0
|
|||
|
|
|||
|
miner.Difficulty = server.Config.Diff.StartDifficulty
|
|||
|
miner.DifficultyNext = -1
|
|||
|
|
|||
|
var jobs sync.Map
|
|||
|
miner.Jobs = jobs
|
|||
|
|
|||
|
var jobs_lock sync.Mutex
|
|||
|
miner.LockForJobs = jobs_lock
|
|||
|
|
|||
|
miner.JobList = list.New()
|
|||
|
miner.LastJobId = ""
|
|||
|
miner.Server = server
|
|||
|
miner.StartSubmitTime = est_time
|
|||
|
miner.LastSubmitime = est_time
|
|||
|
miner.SubmitIndex = 0
|
|||
|
miner.M5SubmitTime = est_time
|
|||
|
|
|||
|
miner.Job = server.SJob
|
|||
|
|
|||
|
miner.Status = coin.MINER_STATUS_CONNECTED
|
|||
|
var txlock sync.Mutex
|
|||
|
miner.TxLock = txlock
|
|||
|
miner.ConnSetupTime = est_time
|
|||
|
miner.Reconnect = true
|
|||
|
miner.Authorized = false
|
|||
|
|
|||
|
miner.VarDiffOpt.VariancePercent = miner.Server.Config.Diff.DiffAdjustPercentage
|
|||
|
miner.VarDiffOpt.AdjustTime = miner.Server.Config.Diff.DiffAdjustTime
|
|||
|
miner.VarDiffOpt.MinShares = miner.VarDiffOpt.AdjustTime / miner.Server.Config.Diff.DiffAdjustInterval * miner.Difficulty * (1 - miner.VarDiffOpt.VariancePercent)
|
|||
|
miner.VarDiffOpt.MaxShares = miner.VarDiffOpt.AdjustTime / miner.Server.Config.Diff.DiffAdjustInterval * miner.Difficulty * (1 + miner.VarDiffOpt.VariancePercent)
|
|||
|
miner.VarDiffOpt.TargetShares = miner.VarDiffOpt.AdjustTime / miner.Server.Config.Diff.DiffAdjustInterval * miner.Difficulty
|
|||
|
miner.VarDiffOpt.MinDiff = miner.Server.Config.Diff.DiffMin
|
|||
|
miner.VarDiffOpt.MaxDiff = miner.Server.Config.Diff.DiffMax
|
|||
|
miner.VarDiffOpt.AdjustInterval = miner.Server.Config.Diff.DiffAdjustInterval
|
|||
|
miner.VarDiffOpt.SubmitShares = 0
|
|||
|
miner.VarDiffOpt.SilenceCount = 0
|
|||
|
miner.VarDiffOpt.LastCalcTime = est_time
|
|||
|
miner.VarDiffOpt.LastSubmitTime = est_time
|
|||
|
miner.VarDiffOpt.Level = coin.Mid
|
|||
|
miner.VarDiffOpt.Uptimes = 0
|
|||
|
miner.VarDiffOpt.Downtimes = 0
|
|||
|
|
|||
|
miner.DiffHandler.Init(miner.Difficulty, miner.Server.Config.Diff.DiffMin, miner.Server.Config.Diff.DiffMax, miner.Server.Config.Diff.DiffAdjustInterval)
|
|||
|
|
|||
|
server.CoinCtx.InitMiner(miner)
|
|||
|
}
|
|||
|
|
|||
|
func NotifyMinerEnd(miner *coin.MinerObj) {
|
|||
|
miner.Server.Miners.Range(func(k, v interface{}) bool {
|
|||
|
m, ok := v.(*(coin.MinerObj))
|
|||
|
if ok {
|
|||
|
if m.User == "" || m.Miner == "" || fmt.Sprint(m.MinerIndex) == "" {
|
|||
|
return true
|
|||
|
}
|
|||
|
if (m.Accepts > 0) && (m.Status == coin.MINER_STATUS_DISCONNECTED) {
|
|||
|
if (m.User == miner.User) && (m.Miner == miner.Miner) {
|
|||
|
miner.OfflineTime = m.OfflineTime
|
|||
|
//m.EndCh <- true
|
|||
|
atomic.StoreInt32(&(m.NeedExit), 1)
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return true
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
func RestoreMinerFromCache(miner *coin.MinerObj) {
|
|||
|
|
|||
|
val := cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "submits")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.Submits += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "blocks")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.Blocks += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.Rejects += float64(intVal)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "retry")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.Retry += int64(intVal)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "staleds")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.ErrStaleds += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lowdiffs")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.ErrLowDiffs += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "duplicates")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.ErrDuplicates += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "formats")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.ErrFormats += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "others")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
miner.ErrOthers += intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_f := cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
miner.Accepts = fVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_f = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rewards")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
miner.Reward = fVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_f = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "fee")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
miner.Fee = fVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*val_f = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "diff")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
miner.Difficulty = fVal
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
val_t := cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "startsubmit")
|
|||
|
if val_f != nil {
|
|||
|
if tVal, ok := val_t.(time.Time); ok {
|
|||
|
miner.StartSubmitTime = tVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*val_t = cache.LoadMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lastsubmit")
|
|||
|
if val_t != nil {
|
|||
|
if tVal, ok := val_t.(time.Time); ok {
|
|||
|
miner.LastSubmitTime = tVal
|
|||
|
}
|
|||
|
}*/
|
|||
|
StartSubmitTime_cnt := 0
|
|||
|
Accepts_cnt := 0
|
|||
|
Rejects_cnt := 0
|
|||
|
k := miner.User + "." + miner.Miner + "_" + fmt.Sprint(miner.MinerIndex)
|
|||
|
m, ok := miner.Server.MMhs.Load(k)
|
|||
|
if ok {
|
|||
|
var mhs coin.MhsObj = m.(coin.MhsObj)
|
|||
|
val_t = cache.LoadMhsCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "starttime")
|
|||
|
if val_t != nil {
|
|||
|
if tVal, ok := val_t.(time.Time); ok {
|
|||
|
mhs.StartSubmitTime = tVal
|
|||
|
StartSubmitTime_cnt++
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_items := cache.LoadMhsCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts")
|
|||
|
if val_items != nil {
|
|||
|
if itemsVal, ok := val_items.(*[]cache.CacheMhsItem); ok {
|
|||
|
for _, item := range *itemsVal {
|
|||
|
var mhsItem coin.MhsItem
|
|||
|
mhsItem.Tt, _ = time.Parse(time.RFC3339, item.Tt)
|
|||
|
mhsItem.Diff = item.Diff
|
|||
|
mhs.Accepts = append(mhs.Accepts, mhsItem)
|
|||
|
Accepts_cnt++
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
val_items = cache.LoadMhsCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects")
|
|||
|
if val_items != nil {
|
|||
|
if itemsVal, ok := val_items.(*[]cache.CacheMhsItem); ok {
|
|||
|
for _, item := range *itemsVal {
|
|||
|
var mhsItem coin.MhsItem
|
|||
|
mhsItem.Tt, _ = time.Parse(time.RFC3339, item.Tt)
|
|||
|
mhsItem.Diff = item.Diff
|
|||
|
mhs.Rejects = append(mhs.Rejects, mhsItem)
|
|||
|
Rejects_cnt++
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
miner.Server.MMhs.Store(k, m)
|
|||
|
}
|
|||
|
|
|||
|
log.Println("from redis", miner.User+"."+miner.Miner, "Submits", miner.Submits, "Blocks", miner.Blocks, "Rejects", miner.Rejects, "Retry", miner.Retry, "ErrStaleds", miner.ErrStaleds, "ErrLowDiffs", miner.ErrLowDiffs, "ErrDuplicates", miner.ErrDuplicates, "ErrFormats", miner.ErrFormats, "ErrOthers", miner.ErrOthers, "Accepts", miner.Accepts, "Reward", miner.Reward, "Fee", miner.Fee, "StartSubmitTime", miner.StartSubmitTime, "mhs cnt", StartSubmitTime_cnt, Accepts_cnt, Rejects_cnt)
|
|||
|
}
|
|||
|
|
|||
|
func RandomSleep(min time.Duration, max time.Duration) {
|
|||
|
// Seed the random number generator
|
|||
|
rand.Seed(time.Now().UnixNano())
|
|||
|
|
|||
|
// Generate a random duration between min and max
|
|||
|
duration := time.Duration(rand.Int63n(int64(max-min)) + int64(min))
|
|||
|
|
|||
|
// Sleep for the generated duration
|
|||
|
//fmt.Printf("Sleeping for %v\n", duration)
|
|||
|
time.Sleep(duration)
|
|||
|
//fmt.Println("Woke up!")
|
|||
|
}
|
|||
|
|
|||
|
func handle_miner_connection(miner *coin.MinerObj) {
|
|||
|
//var count int = 0
|
|||
|
reader := bufio.NewReader(miner.Conn)
|
|||
|
// fmt.Println("有机器连进来了!")
|
|||
|
//var rxmsg string
|
|||
|
for {
|
|||
|
/*msg, err := reader.ReadString('\n')
|
|||
|
if err != nil {
|
|||
|
//if err != io.EOF {
|
|||
|
logg.Error("[server]", zap.String("ReadString", err.Error()))
|
|||
|
break
|
|||
|
//} else {
|
|||
|
// continue
|
|||
|
//}
|
|||
|
}*/
|
|||
|
msg, err := reader.ReadString('\n')
|
|||
|
// fmt.Println("矿工发来消息:", msg)
|
|||
|
if err != nil {
|
|||
|
if err != io.EOF {
|
|||
|
logg.Error("[server]", zap.String("ReadString", err.Error()))
|
|||
|
break
|
|||
|
} else {
|
|||
|
if (err == io.EOF) && (len(msg) <= 0) {
|
|||
|
logg.Error("[server]", zap.String("ReadString", err.Error()))
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
msgSize := len(msg)
|
|||
|
if msgSize >= 256 {
|
|||
|
logg.Error("[server]", zap.Int("ReadString too long", msgSize))
|
|||
|
break
|
|||
|
}
|
|||
|
/*var msg string
|
|||
|
buffer := make([]byte, 1024)
|
|||
|
rdn, err := reader.Read(buffer)
|
|||
|
if err != nil {
|
|||
|
if err != io.EOF || ((err == io.EOF) && (rdn <= 0)) {
|
|||
|
//logg.Error("[server]", zap.String("Read", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " Read " + err.Error())
|
|||
|
}
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
if rdn > 0 {
|
|||
|
//count = 0
|
|||
|
//log.Println("rxmsg", string(buffer[:rdn]))
|
|||
|
rxmsg += string(buffer[:rdn])
|
|||
|
retch_index := strings.Index(rxmsg, "\n")
|
|||
|
if retch_index != -1 { //found
|
|||
|
msg = rxmsg[:retch_index+1]
|
|||
|
tempmsg := rxmsg[retch_index+1:]
|
|||
|
rxmsg = tempmsg
|
|||
|
} else { //not found, discard
|
|||
|
rxmsg = ""
|
|||
|
}
|
|||
|
}*/
|
|||
|
//log.Println("msg", msg)
|
|||
|
if len(msg) > 0 {
|
|||
|
miner.RecvedLiveAck = true
|
|||
|
msg = strings.TrimSpace(msg)
|
|||
|
if (miner.Status == coin.MINER_STATUS_AUTHORIZED) || (miner.Status == coin.MINER_STATUS_RUNNING) {
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg(msg)
|
|||
|
}
|
|||
|
}
|
|||
|
var ret map[string]interface{}
|
|||
|
err = json.Unmarshal([]byte(msg), &ret)
|
|||
|
// fmt.Println("矿工发来消息:", &ret)
|
|||
|
if err == nil {
|
|||
|
//logg.Debug("[server]", zap.Any("msg", msg))
|
|||
|
_, ok := ret["method"].(string)
|
|||
|
if ok {
|
|||
|
switch ret["method"].(string) {
|
|||
|
case "mining.pong":
|
|||
|
Miner_difficulty_adjust(miner)
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
break
|
|||
|
case "mining.subscribe":
|
|||
|
miner.Server.CoinCtx.HandleMinerSubscribe(miner, (ret["id"].(float64)), miner.Job.Extranonce1, msg)
|
|||
|
break
|
|||
|
case "mining.extranonce.subscribe":
|
|||
|
stratum.Handle_extranonce(miner, (ret["id"].(float64)))
|
|||
|
break
|
|||
|
case "mining.authorize":
|
|||
|
auth_ok := stratum.Handle_authorize(miner, (ret["id"].(float64)), msg, miner.Server.DbCtx)
|
|||
|
if auth_ok {
|
|||
|
miner.Server.CoinCtx.SetDifficulty(miner)
|
|||
|
miner.Server.CoinCtx.Notify(miner)
|
|||
|
}
|
|||
|
break
|
|||
|
case "mining.submit":
|
|||
|
prev_status := miner.Status
|
|||
|
var s stratum.AlphSubmitNonce
|
|||
|
if err = json.Unmarshal([]byte(msg), &s); err != nil {
|
|||
|
//logg.Error("[server]", zap.String("mining.submit Unmarshal", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("failed to mining.submit Unmarshal " + err.Error() + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
} else {
|
|||
|
accept_ok := false
|
|||
|
submit_ok := false
|
|||
|
handle_ok := false
|
|||
|
// if len(s.Params) == 3 {
|
|||
|
// accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params[0], s.Params[1], "", "", s.Params[2])
|
|||
|
// } else if len(s.Params) == 5 {
|
|||
|
// accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params[0], s.Params[1], s.Params[2], s.Params[3], s.Params[4])
|
|||
|
// } else {
|
|||
|
// stratum.Handle_exception(miner, (ret["id"].(float64)), stratum.MINER_ERR_ILLEGAL_PARARMS)
|
|||
|
// }
|
|||
|
accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params.Worker, s.Params.JobID, "", "", s.Params.Nonce)
|
|||
|
// fmt.Println("提交结果:", accept_ok, submit_ok, handle_ok)
|
|||
|
if handle_ok {
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
if (prev_status == coin.MINER_STATUS_AUTHORIZED) && (miner.Status == coin.MINER_STATUS_RUNNING) {
|
|||
|
stratum.InitMinerMhs(miner, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.MinerId, miner.Status, miner.Server.DbCtx)
|
|||
|
NotifyMinerEnd(miner)
|
|||
|
|
|||
|
RestoreMinerFromCache(miner)
|
|||
|
}
|
|||
|
if accept_ok {
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts", miner.Accepts)
|
|||
|
if submit_ok {
|
|||
|
miner.Submits += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "submits", miner.Submits)
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Submits += 1
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "submits", miner.Server.Submits)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
}
|
|||
|
if miner.Accepts == 1 {
|
|||
|
miner.Retry += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "retry", miner.Retry)
|
|||
|
}
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Accepts += miner.Difficulty
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "accepts", miner.Server.Accepts)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lastsubmit", miner.LastSubmitime)
|
|||
|
|
|||
|
stratum.UpdateMhs(miner, true, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "staleds", miner.ErrStaleds)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lowdiffs", miner.ErrLowDiffs)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "duplicates", miner.ErrDuplicates)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "formats", miner.ErrFormats)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "others", miner.ErrOthers)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects", miner.Rejects)
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Rejects += 1
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "rejects", miner.Server.Rejects)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
stratum.UpdateMhs(miner, false, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
}
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "diff", miner.Difficulty)
|
|||
|
//UpdateUserSets(miner, false)
|
|||
|
}
|
|||
|
}
|
|||
|
break
|
|||
|
case "alph_submitHashrate":
|
|||
|
params := ret["params"].([]interface{})
|
|||
|
hashrate := params[1].(string)
|
|||
|
stratum.Handle_submitHashrate(miner, hashrate)
|
|||
|
break
|
|||
|
}
|
|||
|
} else {
|
|||
|
switch ret["result"].(type) {
|
|||
|
case string:
|
|||
|
result := ret["result"].(string)
|
|||
|
if result == "mining.pong" || result == "pong" {
|
|||
|
Miner_difficulty_adjust(miner)
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
//logg.Info("[server]", zap.String("ack pong result", result), zap.String("user", miner.User), zap.String("miner", miner.Miner), zap.Float64("id", ret["id"].(float64)))
|
|||
|
}
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
//logg.Error("[server]", zap.String("Unmarshal", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("failed to mining.submit Unmarshal " + err.Error() + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
stratum.Handle_exception(miner, 0, stratum.MINER_ERR_ILLEGAL_PARARMS)
|
|||
|
}
|
|||
|
} else {
|
|||
|
//if rdn > 0 {
|
|||
|
|
|||
|
//} else {
|
|||
|
//time.Sleep(20 * time.Millisecond)
|
|||
|
/*if count++; count >= 10 {
|
|||
|
break
|
|||
|
}*/
|
|||
|
//time.Sleep(time.Duration(1) * time.Second / 50)
|
|||
|
runtime.Gosched()
|
|||
|
//}
|
|||
|
//time.Sleep(time.Duration(1) * time.Second / 2)
|
|||
|
|
|||
|
//runtime.Gosched()
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
miner.TxLock.Lock()
|
|||
|
prev_status := miner.Status
|
|||
|
miner.Status = coin.MINER_STATUS_DISCONNECTED
|
|||
|
miner.TxLock.Unlock()
|
|||
|
|
|||
|
//minDuration := 100 * time.Millisecond
|
|||
|
//maxDuration := 5 * time.Second
|
|||
|
//RandomSleep(minDuration, maxDuration)
|
|||
|
|
|||
|
stratum.UpdateMhsStatus(miner, miner.Server.DbCtx)
|
|||
|
miner.Conn.Close()
|
|||
|
if miner.Accepts > 0 /*|| (miner.Rejects > 0)*/ {
|
|||
|
miner.OfflineTime = miner.LastSubmitime
|
|||
|
}
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Level(zerolog.Disabled)
|
|||
|
}
|
|||
|
//defer miner.LogR.Close()
|
|||
|
if miner.LogR != nil {
|
|||
|
defer miner.LogR.Close()
|
|||
|
}
|
|||
|
//logg.Error("[server]", zap.String("miner disconnect", miner.MinerId))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("miner disconnect prepare " + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
|
|||
|
stratum.StaleAllJobs(miner)
|
|||
|
|
|||
|
if prev_status == coin.MINER_STATUS_RUNNING {
|
|||
|
/*
|
|||
|
select {
|
|||
|
case <-time.After(600 * time.Second):
|
|||
|
logg.Error("[server]", zap.String("miner end", miner.MinerId+" "+miner.User+"."+miner.Miner+"_"+fmt.Sprint(miner.MinerIndex)))
|
|||
|
case <-miner.EndCh:
|
|||
|
logg.Error("[server]", zap.String("miner end chan", miner.MinerId+" "+miner.User+"."+miner.Miner+"_"+fmt.Sprint(miner.MinerIndex)))
|
|||
|
}*/
|
|||
|
var to_cnt int = 0
|
|||
|
for {
|
|||
|
if atomic.LoadInt32(&(miner.NeedExit)) == 1 {
|
|||
|
break
|
|||
|
}
|
|||
|
if to_cnt > 360 {
|
|||
|
break
|
|||
|
}
|
|||
|
time.Sleep(time.Duration(1) * time.Second)
|
|||
|
to_cnt++
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//UpdateUserSets(miner, true)
|
|||
|
miner.Server.Miners.Delete(miner.MinerId)
|
|||
|
|
|||
|
//logg.Error("[server]", zap.String("miner disconnect", miner.MinerId), zap.String("user ", miner.User), zap.String("miner ", miner.Miner), zap.Int("connected num ", num))
|
|||
|
logg.Error("[server]", zap.String("miner disconnected", miner.MinerId), zap.String("user ", miner.User), zap.String("miner ", miner.Miner))
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
func handle_miner_connection_old(miner *coin.MinerObj) {
|
|||
|
//var count int = 0
|
|||
|
reader := bufio.NewReader(miner.Conn)
|
|||
|
var rxmsg string
|
|||
|
for {
|
|||
|
/*msg, err := reader.ReadString('\n')
|
|||
|
if err != nil {
|
|||
|
//if err != io.EOF {
|
|||
|
logg.Error("[server]", zap.String("ReadString", err.Error()))
|
|||
|
break
|
|||
|
//} else {
|
|||
|
// continue
|
|||
|
//}
|
|||
|
}*/
|
|||
|
var msg string
|
|||
|
buffer := make([]byte, 1024)
|
|||
|
rdn, err := reader.Read(buffer)
|
|||
|
if err != nil {
|
|||
|
if err != io.EOF || ((err == io.EOF) && (rdn <= 0)) {
|
|||
|
//logg.Error("[server]", zap.String("Read", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " Read " + err.Error())
|
|||
|
}
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
if rdn > 0 {
|
|||
|
//count = 0
|
|||
|
//log.Println("rxmsg", string(buffer[:rdn]))
|
|||
|
rxmsg += string(buffer[:rdn])
|
|||
|
retch_index := strings.Index(rxmsg, "\n")
|
|||
|
if retch_index != -1 { //found
|
|||
|
msg = rxmsg[:retch_index+1]
|
|||
|
tempmsg := rxmsg[retch_index+1:]
|
|||
|
rxmsg = tempmsg
|
|||
|
} else { //not found, discard
|
|||
|
rxmsg = ""
|
|||
|
}
|
|||
|
}
|
|||
|
//log.Println("msg", msg)
|
|||
|
if len(msg) > 0 {
|
|||
|
miner.RecvedLiveAck = true
|
|||
|
msg = strings.TrimSpace(msg)
|
|||
|
if (miner.Status == coin.MINER_STATUS_AUTHORIZED) || (miner.Status == coin.MINER_STATUS_RUNNING) {
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg(msg)
|
|||
|
}
|
|||
|
}
|
|||
|
var ret map[string]interface{}
|
|||
|
err = json.Unmarshal([]byte(msg), &ret)
|
|||
|
if err == nil {
|
|||
|
//logg.Debug("[server]", zap.Any("msg", msg))
|
|||
|
_, ok := ret["method"].(string)
|
|||
|
if ok {
|
|||
|
switch ret["method"].(string) {
|
|||
|
case "mining.pong":
|
|||
|
Miner_difficulty_adjust(miner)
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
break
|
|||
|
case "mining.subscribe":
|
|||
|
miner.Server.CoinCtx.HandleMinerSubscribe(miner, (ret["id"].(float64)), miner.Job.Extranonce1, msg)
|
|||
|
break
|
|||
|
case "mining.extranonce.subscribe":
|
|||
|
stratum.Handle_extranonce(miner, (ret["id"].(float64)))
|
|||
|
break
|
|||
|
case "mining.authorize":
|
|||
|
auth_ok := stratum.Handle_authorize(miner, (ret["id"].(float64)), msg, miner.Server.DbCtx)
|
|||
|
if auth_ok {
|
|||
|
miner.Server.CoinCtx.SetDifficulty(miner)
|
|||
|
miner.Server.CoinCtx.Notify(miner)
|
|||
|
}
|
|||
|
break
|
|||
|
case "mining.submit":
|
|||
|
prev_status := miner.Status
|
|||
|
if miner.Server.CoinCtx.Coin != "alph" {
|
|||
|
var s stratum.Submit_nonce
|
|||
|
if err = json.Unmarshal([]byte(msg), &s); err != nil {
|
|||
|
//logg.Error("[server]", zap.String("mining.submit Unmarshal", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("failed to mining.submit Unmarshal " + err.Error() + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
} else {
|
|||
|
accept_ok := false
|
|||
|
submit_ok := false
|
|||
|
handle_ok := false
|
|||
|
if len(s.Params) == 3 {
|
|||
|
// [0]: wallet.worker_name [1]: header hash [2]: nonce
|
|||
|
if miner.Server.CoinCtx.Coin == "enx" || miner.Server.CoinCtx.Coin == "kas" {
|
|||
|
accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params[0], s.ID.(string), s.Params[1], "", s.Params[2])
|
|||
|
} else {
|
|||
|
accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params[0], s.Params[1], "", "", s.Params[2])
|
|||
|
}
|
|||
|
|
|||
|
} else if len(s.Params) == 5 {
|
|||
|
accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, (ret["id"].(float64)), s.Params[0], s.Params[1], s.Params[2], s.Params[3], s.Params[4])
|
|||
|
} else {
|
|||
|
stratum.Handle_exception(miner, (ret["id"].(float64)), stratum.MINER_ERR_ILLEGAL_PARARMS)
|
|||
|
}
|
|||
|
if handle_ok {
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
if (prev_status == coin.MINER_STATUS_AUTHORIZED) && (miner.Status == coin.MINER_STATUS_RUNNING) {
|
|||
|
stratum.InitMinerMhs(miner, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.MinerId, miner.Status, miner.Server.DbCtx)
|
|||
|
NotifyMinerEnd(miner)
|
|||
|
|
|||
|
RestoreMinerFromCache(miner)
|
|||
|
}
|
|||
|
if accept_ok {
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts", miner.Accepts)
|
|||
|
if submit_ok {
|
|||
|
miner.Submits += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "submits", miner.Submits)
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Submits += 1
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "submits", miner.Server.Submits)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
}
|
|||
|
if miner.Accepts == 1 {
|
|||
|
miner.Retry += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "retry", miner.Retry)
|
|||
|
}
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Accepts += miner.Difficulty
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "accepts", miner.Server.Accepts)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lastsubmit", miner.LastSubmitime)
|
|||
|
|
|||
|
stratum.UpdateMhs(miner, true, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "staleds", miner.ErrStaleds)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lowdiffs", miner.ErrLowDiffs)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "duplicates", miner.ErrDuplicates)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "formats", miner.ErrFormats)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "others", miner.ErrOthers)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects", miner.Rejects)
|
|||
|
/*miner.Server.PoolSLock.Lock()
|
|||
|
miner.Server.Rejects += 1
|
|||
|
cache.StorePoolCache(miner.Server.RedisClient, miner.Server.MinerType, "rejects", miner.Server.Rejects)
|
|||
|
miner.Server.PoolSLock.Unlock()*/
|
|||
|
stratum.UpdateMhs(miner, false, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
}
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "diff", miner.Difficulty)
|
|||
|
//UpdateUserSets(miner, false)
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
var s stratum.AlphSubmitNonce
|
|||
|
if err = json.Unmarshal([]byte(msg), &s); err != nil {
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("failed to mining.submit Unmarshal " + err.Error() + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
} else {
|
|||
|
accept_ok := false
|
|||
|
submit_ok := false
|
|||
|
handle_ok := false
|
|||
|
accept_ok, submit_ok, handle_ok = miner.Server.CoinCtx.HandleMinerSubmit(miner, s.Id.(float64), s.Params.WorkerName, s.Params.JobID, "", "", s.Params.Nonce)
|
|||
|
if handle_ok {
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
if (prev_status == coin.MINER_STATUS_AUTHORIZED) && (miner.Status == coin.MINER_STATUS_RUNNING) {
|
|||
|
stratum.InitMinerMhs(miner, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.MinerId, miner.Status, miner.Server.DbCtx)
|
|||
|
NotifyMinerEnd(miner)
|
|||
|
RestoreMinerFromCache(miner)
|
|||
|
}
|
|||
|
if accept_ok {
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "accepts", miner.Accepts)
|
|||
|
if submit_ok {
|
|||
|
miner.Submits += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "submits", miner.Submits)
|
|||
|
}
|
|||
|
if miner.Accepts == 1 {
|
|||
|
miner.Retry += 1
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "retry", miner.Retry)
|
|||
|
}
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lastsubmit", miner.LastSubmitime)
|
|||
|
|
|||
|
stratum.UpdateMhs(miner, true, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "staleds", miner.ErrStaleds)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "lowdiffs", miner.ErrLowDiffs)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "duplicates", miner.ErrDuplicates)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "formats", miner.ErrFormats)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "others", miner.ErrOthers)
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "rejects", miner.Rejects)
|
|||
|
|
|||
|
stratum.UpdateMhs(miner, false, miner.Difficulty, 0, miner.Server.DbCtx)
|
|||
|
}
|
|||
|
cache.StoreMinerCache(miner.Server.RedisClient, miner.Server.MinerType, miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), "diff", miner.Difficulty)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break
|
|||
|
}
|
|||
|
} else {
|
|||
|
switch ret["result"].(type) {
|
|||
|
case string:
|
|||
|
result := ret["result"].(string)
|
|||
|
if result == "mining.pong" || result == "pong" {
|
|||
|
Miner_difficulty_adjust(miner)
|
|||
|
//miner.RecvedLiveAck = true
|
|||
|
//logg.Info("[server]", zap.String("ack pong result", result), zap.String("user", miner.User), zap.String("miner", miner.Miner), zap.Float64("id", ret["id"].(float64)))
|
|||
|
}
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
//logg.Error("[server]", zap.String("Unmarshal", err.Error()))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("failed to mining.submit Unmarshal " + err.Error() + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
stratum.Handle_exception(miner, 0, stratum.MINER_ERR_ILLEGAL_PARARMS)
|
|||
|
}
|
|||
|
} else {
|
|||
|
if rdn > 0 {
|
|||
|
|
|||
|
} else {
|
|||
|
//time.Sleep(20 * time.Millisecond)
|
|||
|
/*if count++; count >= 10 {
|
|||
|
break
|
|||
|
}*/
|
|||
|
//time.Sleep(time.Duration(1) * time.Second / 50)
|
|||
|
runtime.Gosched()
|
|||
|
}
|
|||
|
//time.Sleep(time.Duration(1) * time.Second / 2)
|
|||
|
|
|||
|
//runtime.Gosched()
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
miner.TxLock.Lock()
|
|||
|
prev_status := miner.Status
|
|||
|
miner.Status = coin.MINER_STATUS_DISCONNECTED
|
|||
|
miner.TxLock.Unlock()
|
|||
|
|
|||
|
//minDuration := 100 * time.Millisecond
|
|||
|
//maxDuration := 5 * time.Second
|
|||
|
//RandomSleep(minDuration, maxDuration)
|
|||
|
|
|||
|
stratum.UpdateMhsStatus(miner, miner.Server.DbCtx)
|
|||
|
miner.Conn.Close()
|
|||
|
if miner.Accepts > 0 /*|| (miner.Rejects > 0)*/ {
|
|||
|
miner.OfflineTime = miner.LastSubmitime
|
|||
|
}
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Level(zerolog.Disabled)
|
|||
|
}
|
|||
|
//defer miner.LogR.Close()
|
|||
|
if miner.LogR != nil {
|
|||
|
defer miner.LogR.Close()
|
|||
|
}
|
|||
|
//logg.Error("[server]", zap.String("miner disconnect", miner.MinerId))
|
|||
|
if miner.ZlogInit {
|
|||
|
miner.Zlog.Info().Msg("miner disconnect prepare " + " " + miner.User + "." + miner.Miner)
|
|||
|
}
|
|||
|
|
|||
|
if prev_status == coin.MINER_STATUS_RUNNING {
|
|||
|
/*
|
|||
|
select {
|
|||
|
case <-time.After(600 * time.Second):
|
|||
|
logg.Error("[server]", zap.String("miner end", miner.MinerId+" "+miner.User+"."+miner.Miner+"_"+fmt.Sprint(miner.MinerIndex)))
|
|||
|
case <-miner.EndCh:
|
|||
|
logg.Error("[server]", zap.String("miner end chan", miner.MinerId+" "+miner.User+"."+miner.Miner+"_"+fmt.Sprint(miner.MinerIndex)))
|
|||
|
}*/
|
|||
|
var to_cnt int = 0
|
|||
|
for {
|
|||
|
if atomic.LoadInt32(&(miner.NeedExit)) == 1 {
|
|||
|
break
|
|||
|
}
|
|||
|
if to_cnt > 360 {
|
|||
|
break
|
|||
|
}
|
|||
|
time.Sleep(time.Duration(1) * time.Second)
|
|||
|
to_cnt++
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//UpdateUserSets(miner, true)
|
|||
|
miner.Server.Miners.Delete(miner.MinerId)
|
|||
|
|
|||
|
//logg.Error("[server]", zap.String("miner disconnect", miner.MinerId), zap.String("user ", miner.User), zap.String("miner ", miner.Miner), zap.Int("connected num ", num))
|
|||
|
logg.Error("[server]", zap.String("miner disconnected", miner.MinerId), zap.String("user ", miner.User), zap.String("miner ", miner.Miner))
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
func Miner_difficulty_adjust(m *coin.MinerObj) {
|
|||
|
if m.User == "" || m.Miner == "" {
|
|||
|
return
|
|||
|
}
|
|||
|
now := time.Now()
|
|||
|
|
|||
|
//if now.Sub(m.LastSubmitime).Seconds() > m.VarDiffOpt.AdjustInterval*5 {
|
|||
|
if now.Sub(m.VarDiffOpt.LastSubmitTime).Seconds() > m.VarDiffOpt.AdjustInterval*10 {
|
|||
|
if m.Server.Config.Diff.Filter == "kalman" {
|
|||
|
share_interval := now.Sub(m.VarDiffOpt.LastSubmitTime).Seconds()
|
|||
|
mhs := m.Difficulty * share_interval
|
|||
|
diff_next, kalman_p := m.DiffHandler.Handler(m.Difficulty, share_interval)
|
|||
|
//diff_next, _ := m.DiffHandler.Handler(m.Difficulty, share_interval)
|
|||
|
mhs_est := diff_next * m.Server.Config.Diff.DiffAdjustInterval
|
|||
|
|
|||
|
ratio := diff_next / m.Difficulty
|
|||
|
if ratio > 0 {
|
|||
|
if now.Sub(m.StartSubmitTime).Seconds() > 180 {
|
|||
|
if ratio >= 2 {
|
|||
|
//m.DifficultyNext = math.Ceil(diff_next*100.0) / 100.0
|
|||
|
m.DifficultyNext = diff_next * 10000000 / 10000000
|
|||
|
} else if ratio <= 0.5 {
|
|||
|
//m.DifficultyNext = math.Ceil(diff_next*100.0) / 100.0
|
|||
|
m.DifficultyNext = diff_next * 10000000 / 10000000
|
|||
|
} else {
|
|||
|
}
|
|||
|
} else {
|
|||
|
//m.DifficultyNext = math.Ceil(diff_next*100.0) / 100.0
|
|||
|
m.DifficultyNext = diff_next * 10000000 / 10000000
|
|||
|
/*if ratio >= 1.1 {
|
|||
|
m.DifficultyNext = math.Ceil(diff_next*100.0) / 100.0
|
|||
|
} else if ratio <= 0.8 {
|
|||
|
m.DifficultyNext = math.Ceil(diff_next*100.0) / 100.0
|
|||
|
} else {
|
|||
|
}*/
|
|||
|
}
|
|||
|
}
|
|||
|
if m.DifficultyNext > 0.0 {
|
|||
|
if m.DifficultyNext < m.VarDiffOpt.MinDiff {
|
|||
|
m.DifficultyNext = m.VarDiffOpt.MinDiff
|
|||
|
} else if m.DifficultyNext > m.VarDiffOpt.MaxDiff {
|
|||
|
m.DifficultyNext = m.VarDiffOpt.MaxDiff
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if m.Server.Config.Diff.Dbg {
|
|||
|
coin.New_diff_into_db(m.User, m.Miner, fmt.Sprint(m.MinerIndex), m.Difficulty, diff_next, kalman_p, share_interval, mhs, mhs_est)
|
|||
|
}
|
|||
|
|
|||
|
//m.VarDiffOpt.LastCalcTime = now
|
|||
|
m.VarDiffOpt.LastSubmitTime = now
|
|||
|
//log.Println("diff adjust timeout", ratio, diff_next, m.Difficulty, m.DifficultyNext)
|
|||
|
} else {
|
|||
|
m.VarDiffOpt.Level = coin.Hign
|
|||
|
coin.VarAdjustDifficulty(m, coin.DOWN_DIFF)
|
|||
|
m.VarDiffOpt.LastCalcTime = now
|
|||
|
m.VarDiffOpt.LastSubmitTime = now
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*func difficulty_adjust(server *coin.ServerContext) {
|
|||
|
server.Miners.Range(func(k, v interface{}) bool {
|
|||
|
m, ok := v.(*coin.MinerObj)
|
|||
|
if ok {
|
|||
|
if m.User == "" || m.Miner == "" {
|
|||
|
return true
|
|||
|
}
|
|||
|
now := time.Now()
|
|||
|
if now.Sub(m.LastSubmitime).Seconds() > m.VarDiffOpt.AdjustInterval*5 {
|
|||
|
if server.Config.Diff.Filter == "kalman" {
|
|||
|
share_interval := now.Sub(m.LastSubmitime).Seconds()
|
|||
|
//mhs := m.Difficulty * share_interval
|
|||
|
//diff_next, kalman_p := m.DiffHandler.Handler(m.Difficulty, share_interval)
|
|||
|
diff_next, _ := m.DiffHandler.Handler(m.Difficulty, share_interval)
|
|||
|
//mhs_est := diff_next * share_interval
|
|||
|
|
|||
|
ratio := diff_next / m.Difficulty
|
|||
|
if ratio >= 2.0 {
|
|||
|
m.DifficultyNext = math.Ceil(diff_next*100) / 100
|
|||
|
} else if ratio <= 0.5 {
|
|||
|
m.DifficultyNext = math.Ceil(diff_next*100) / 100
|
|||
|
} else {
|
|||
|
}
|
|||
|
//new_diff_into_db(miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.Difficulty, miner.DifficultyNext, kalman_p, share_interval, mhs, mhs_est)
|
|||
|
m.LastSubmitime = now
|
|||
|
} else {
|
|||
|
m.VarDiffOpt.Level = coin.Hign
|
|||
|
coin.VarAdjustDifficulty(m, coin.DOWN_DIFF)
|
|||
|
m.LastSubmitime = now
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return true
|
|||
|
})
|
|||
|
}*/
|
|||
|
|
|||
|
/*func Handle_difficulty_adjust_timer(server *coin.ServerContext) {
|
|||
|
timer := time.NewTimer(time.Second * coin.MINER_DIFFICULTY_ADJUST_DURATION)
|
|||
|
for {
|
|||
|
select {
|
|||
|
case <-timer.C:
|
|||
|
difficulty_adjust(server)
|
|||
|
timer.Reset(time.Second * coin.MINER_DIFFICULTY_ADJUST_DURATION)
|
|||
|
case <-(server.ExitDiffVar):
|
|||
|
timer.Stop()
|
|||
|
server.ExitDiffVar <- true
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
func StartServer(server *coin.ServerContext) {
|
|||
|
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
|||
|
|
|||
|
select {
|
|||
|
case issync := <-server.SyncJobChan:
|
|||
|
if !issync {
|
|||
|
return
|
|||
|
}
|
|||
|
server.Synced = true
|
|||
|
log.Println("StartServer receive job! start")
|
|||
|
case <-time.After(time.Duration(60) * time.Second):
|
|||
|
log.Println("StartServer not receive job! exited")
|
|||
|
if logg != nil {
|
|||
|
logg.Error("[server]", zap.String("StartServer not receive job", "exited"))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var miners sync.Map
|
|||
|
server.Miners = miners
|
|||
|
var mmhs sync.Map
|
|||
|
server.MMhs = mmhs
|
|||
|
|
|||
|
/*var update_map sync.Map
|
|||
|
server.UpdateMap = update_map*/
|
|||
|
|
|||
|
listener, err := net.Listen("tcp", server.Config.Host.Listen)
|
|||
|
server.Listener = listener
|
|||
|
if err != nil {
|
|||
|
logg.Fatal("[server]", zap.String("Listen", err.Error()))
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
go HandleKeepAlive(server)
|
|||
|
//go Handle_difficulty_adjust_timer(server)
|
|||
|
|
|||
|
go dbif.Handle_miners_timer(server, server.DbCtx)
|
|||
|
//go dbif.Handle_miners_stats_timer(server, server.DbCtx)
|
|||
|
//go dbif.Handle_users_timer(server, server.DbCtx)
|
|||
|
//go dbif.Handle_users_stats_timer(server, server.DbCtx)
|
|||
|
|
|||
|
for {
|
|||
|
conn, err := listener.Accept()
|
|||
|
if err != nil {
|
|||
|
if server.ExitFlag {
|
|||
|
break
|
|||
|
}
|
|||
|
logg.Error("[server]", zap.String("Accept", err.Error()))
|
|||
|
continue
|
|||
|
}
|
|||
|
var miner coin.MinerObj
|
|||
|
init_miner(&miner, conn, server)
|
|||
|
|
|||
|
server.Miners.LoadOrStore(miner.MinerId, &miner)
|
|||
|
go handle_miner_connection(&miner)
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
var jobIndex int = 0
|
|||
|
|
|||
|
func HandleJob(server *coin.ServerContext) {
|
|||
|
for {
|
|||
|
if server.ExitFlag {
|
|||
|
fmt.Println("服务已退出!")
|
|||
|
break
|
|||
|
}
|
|||
|
if server.SubCh == nil {
|
|||
|
fmt.Println("任务zmq连接未初始化或已断开!")
|
|||
|
server.SubCh = utility.InitZmqSub(ServerCtx.Config.Zmq.Sub, "job"+server.MinerType)
|
|||
|
}
|
|||
|
if server.SubCh != nil {
|
|||
|
cmsg_sub, err := server.SubCh.RecvMessage()
|
|||
|
if err != nil {
|
|||
|
time.Sleep(time.Duration(1) * time.Second)
|
|||
|
server.SubCh.SetSubscribe("job" + server.MinerType)
|
|||
|
server.SubCh.Connect(ServerCtx.Config.Zmq.Sub)
|
|||
|
//server.SubCh.SetMaxmsgsize(1024 * 1024 * 8)
|
|||
|
//server.SubCh.Destroy()
|
|||
|
//server.SubCh = utility.InitZmqSub(ServerCtx.Config.Zmq.Sub, "job"+server.MinerType)
|
|||
|
logg.Error("[server]", zap.String("HandleJob", err.Error()))
|
|||
|
continue
|
|||
|
}
|
|||
|
if cmsg_sub != nil {
|
|||
|
// log.Println("HandleJob msg size", len(cmsg_sub), string(cmsg_sub[0]))
|
|||
|
if len(cmsg_sub) >= 2 {
|
|||
|
cmp_topic := "job" + server.CoinCtx.Coin
|
|||
|
// log.Println("job 1", string(cmsg_sub[0]), cmp_topic)
|
|||
|
if string(cmsg_sub[0]) == cmp_topic {
|
|||
|
//logg.Error("[server]", zap.String("job 1", string(cmsg_sub[0])+","+cmp_topic))
|
|||
|
//server.AlivingChan <- true
|
|||
|
atomic.StoreInt32(&(server.FlagAliving), 1)
|
|||
|
|
|||
|
cmsg := cmsg_sub[1]
|
|||
|
//logg.Error("[server]", zap.String("job 2", string(cmsg_sub[0])+","+cmp_topic))
|
|||
|
// log.Println("job 2", string(cmsg_sub[0]), string(cmsg_sub[0])+","+cmp_topic)
|
|||
|
jobIndex += 1
|
|||
|
// fmt.Println("gbt获取任务时间:", time.Now(), "\t", "gbt获取任务序号:", jobIndex)
|
|||
|
server.CoinCtx.HandleJobMsg(server, cmsg)
|
|||
|
if !server.Synced {
|
|||
|
server.SyncJobChan <- true
|
|||
|
}
|
|||
|
//logg.Error("[server]", zap.String("job 3", string(cmsg_sub[0])+","+cmp_topic))
|
|||
|
// log.Println("job 3")
|
|||
|
}
|
|||
|
} else {
|
|||
|
logg.Error("[server]", zap.Int("HandleJob len exceeded:", len(cmsg_sub)))
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
logg.Error("[server]", zap.String("HandleJob", "SubCh fail!"))
|
|||
|
time.Sleep(time.Duration(1) * time.Second)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/*func LoadCache(server *coin.ServerContext) {
|
|||
|
val := cache.LoadPoolCache(server.RedisClient, server.MinerType, "submits")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
server.Submits = intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadPoolCache(server.RedisClient, server.MinerType, "blocks")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
server.Blocks = intVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val = cache.LoadPoolCache(server.RedisClient, server.MinerType, "rejects")
|
|||
|
if val != nil {
|
|||
|
if intVal, ok := val.(int64); ok {
|
|||
|
server.Rejects = float64(intVal)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_f := cache.LoadPoolCache(server.RedisClient, server.MinerType, "accepts")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
server.Accepts = fVal
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
/*val_f = cache.LoadPoolCache(server.RedisClient, server.MinerType, "reward")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
server.Reward = fVal
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
val_f = cache.LoadPoolCache(server.RedisClient, server.MinerType, "fee")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
server.Fee = fVal
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
/*val_f = cache.LoadPoolCache(server.RedisClient, server.MinerType, "refdiff")
|
|||
|
if val_f != nil {
|
|||
|
if fVal, ok := val_f.(float64); ok {
|
|||
|
if fVal > 0 {
|
|||
|
server.RefDifficulty = fVal
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}*/
|
|||
|
|
|||
|
func Start(Coin string, DbCtx *db.DbContext) {
|
|||
|
zmqctx := C.zmq_ctx_new()
|
|||
|
defer C.zmq_ctx_term(zmqctx)
|
|||
|
C.set_max_msg_size(zmqctx, 1024*1024*8)
|
|||
|
|
|||
|
ServerCtx.MinerType = Coin
|
|||
|
|
|||
|
atomic.StoreInt32(&(ServerCtx.FlagAliving), 0)
|
|||
|
atomic.StoreInt32(&(ServerCtx.FlagAlivingExit), 0)
|
|||
|
//atomic.StoreInt32(&(ServerCtx.NotifyBlkDetailIdx), 0)
|
|||
|
|
|||
|
ServerCtx.MinerIndex = 0
|
|||
|
|
|||
|
ServerCtx.Accepts5M = 0
|
|||
|
ServerCtx.Accepts15M = 0
|
|||
|
ServerCtx.Accepts30M = 0
|
|||
|
ServerCtx.Accepts1h = 0
|
|||
|
ServerCtx.Accepts3h = 0
|
|||
|
ServerCtx.Accepts6h = 0
|
|||
|
ServerCtx.Accepts12h = 0
|
|||
|
ServerCtx.Accepts24h = 0
|
|||
|
ServerCtx.Accepts48h = 0
|
|||
|
|
|||
|
ServerCtx.Rejects5M = 0
|
|||
|
ServerCtx.Rejects15M = 0
|
|||
|
ServerCtx.Rejects30M = 0
|
|||
|
ServerCtx.Rejects1h = 0
|
|||
|
ServerCtx.Rejects3h = 0
|
|||
|
ServerCtx.Rejects6h = 0
|
|||
|
ServerCtx.Rejects12h = 0
|
|||
|
ServerCtx.Rejects24h = 0
|
|||
|
ServerCtx.Rejects48h = 0
|
|||
|
|
|||
|
ServerCtx.Mhs5M = 0
|
|||
|
ServerCtx.Mhs15M = 0
|
|||
|
ServerCtx.Mhs30M = 0
|
|||
|
ServerCtx.Mhs1h = 0
|
|||
|
ServerCtx.Mhs3h = 0
|
|||
|
ServerCtx.Mhs6h = 0
|
|||
|
ServerCtx.Mhs12h = 0
|
|||
|
ServerCtx.Mhs24h = 0
|
|||
|
ServerCtx.Mhs48h = 0
|
|||
|
|
|||
|
ServerCtx.Normal = 0
|
|||
|
ServerCtx.Abnormal = 0
|
|||
|
ServerCtx.Offline = 0
|
|||
|
ServerCtx.MhsZero = 0
|
|||
|
ServerCtx.MhsLow = 0
|
|||
|
ServerCtx.HighRejects = 0
|
|||
|
ServerCtx.Unstable = 0
|
|||
|
|
|||
|
ServerCtx.NetTarget = ""
|
|||
|
ServerCtx.NetHight = 0
|
|||
|
|
|||
|
ServerCtx.Submits = 0
|
|||
|
ServerCtx.Blocks = 0
|
|||
|
ServerCtx.Orphans = 0
|
|||
|
|
|||
|
ServerCtx.Reward = 0
|
|||
|
ServerCtx.Fee = 0
|
|||
|
|
|||
|
ServerCtx.Synced = false
|
|||
|
|
|||
|
ServerCtx.ExitFlag = false
|
|||
|
ServerCtx.ExitDiffVar = make(chan bool, 256)
|
|||
|
ServerCtx.SyncJobChan = make(chan bool, 256)
|
|||
|
ServerCtx.ExitDbMiners = make(chan bool, 256)
|
|||
|
ServerCtx.ExitDbMinersStats = make(chan bool, 256)
|
|||
|
|
|||
|
ServerCtx.ExitDbUser = make(chan bool, 256)
|
|||
|
ServerCtx.ExitDbUserStats = make(chan bool, 256)
|
|||
|
|
|||
|
ServerCtx.ExitDbPoolStats = make(chan bool, 256)
|
|||
|
|
|||
|
/*var users_slock sync.Mutex
|
|||
|
ServerCtx.UsersSLock = users_slock*/
|
|||
|
|
|||
|
/*var pool_slock sync.Mutex
|
|||
|
ServerCtx.PoolSLock = pool_slock*/
|
|||
|
|
|||
|
ServerCtx.DbCtx = DbCtx
|
|||
|
ServerCtx.Config = InitConfig()
|
|||
|
|
|||
|
ServerCtx.RefDifficulty = 1 //ServerCtx.Config.Diff.StartDifficulty
|
|||
|
|
|||
|
l, r, _ := utility.InitLogg(&(ServerCtx.Config.Zaplog), &(ServerCtx.Config.Logrotae), Coin, "server")
|
|||
|
logg = l
|
|||
|
logr = r
|
|||
|
ServerCtx.Logg = l
|
|||
|
ServerCtx.LogR = logr
|
|||
|
|
|||
|
opts := &redis.Options{
|
|||
|
Addr: ServerCtx.Config.Redis.Addr,
|
|||
|
Password: ServerCtx.Config.Redis.Password,
|
|||
|
DB: ServerCtx.Config.Redis.DB,
|
|||
|
}
|
|||
|
|
|||
|
ServerCtx.RedisClient = redis.NewClient(opts)
|
|||
|
|
|||
|
err := ServerCtx.RedisClient.Set(context.Background(), "server", Coin, 0).Err()
|
|||
|
if err != nil {
|
|||
|
fmt.Println(err)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
register_signal(DbCtx)
|
|||
|
register_user_signal(&ServerCtx)
|
|||
|
|
|||
|
dbif.Create_db_tables(ServerCtx.DbCtx, ServerCtx.MinerType)
|
|||
|
|
|||
|
ServerCtx.PubCh = utility.InitZmqPub(ServerCtx.Config.Zmq.Pub)
|
|||
|
|
|||
|
ServerCtx.SubCh = utility.InitZmqSub(ServerCtx.Config.Zmq.Sub, "job"+Coin)
|
|||
|
|
|||
|
//ServerCtx.AlivingChan = make(chan bool, 32768)
|
|||
|
//ServerCtx.LiveingExpired = false
|
|||
|
ServerCtx.ExitPingChan = make(chan bool, 32768)
|
|||
|
ServerCtx.ExitJobChan = make(chan bool, 32768)
|
|||
|
|
|||
|
ServerCtx.Started = true
|
|||
|
for _, coinobj := range coinobjs {
|
|||
|
if coinobj.Coin == Coin {
|
|||
|
ServerCtx.CoinCtx = coinobj
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
// fmt.Println(&ServerCtx)
|
|||
|
ServerCtx.CoinCtx.Init(&ServerCtx)
|
|||
|
|
|||
|
//LoadCache(&ServerCtx)
|
|||
|
|
|||
|
ServerCtx.CoinCtx.Start()
|
|||
|
|
|||
|
go StartServer(&ServerCtx)
|
|||
|
go HandleJob(&ServerCtx)
|
|||
|
|
|||
|
go ServerLivingHandler(&ServerCtx)
|
|||
|
|
|||
|
<-DbCtx.AppExit
|
|||
|
log.Println("received exit signal")
|
|||
|
}
|
|||
|
|
|||
|
func Stop() {
|
|||
|
log.Println("enter Stop")
|
|||
|
|
|||
|
ServerCtx.ExitFlag = true
|
|||
|
|
|||
|
if ServerCtx.Started == true {
|
|||
|
ServerCtx.Miners.Range(func(k, v interface{}) bool {
|
|||
|
m, ok := v.(*coin.MinerObj)
|
|||
|
if ok {
|
|||
|
m.Conn.Close()
|
|||
|
m.Server.Miners.Delete(m.MinerId)
|
|||
|
}
|
|||
|
return true
|
|||
|
})
|
|||
|
time.Sleep(time.Second)
|
|||
|
}
|
|||
|
ServerCtx.Started = false
|
|||
|
//if !ServerCtx.LiveingExpired {
|
|||
|
//ServerCtx.AlivingChan <- false
|
|||
|
//}
|
|||
|
atomic.StoreInt32(&(ServerCtx.FlagAlivingExit), 1)
|
|||
|
|
|||
|
ServerCtx.ExitPingChan <- true
|
|||
|
|
|||
|
ServerCtx.ExitDbMiners <- true
|
|||
|
ServerCtx.ExitDbMinersStats <- true
|
|||
|
|
|||
|
ServerCtx.ExitDbUser <- true
|
|||
|
ServerCtx.ExitDbUserStats <- true
|
|||
|
|
|||
|
ServerCtx.ExitDiffVar <- true
|
|||
|
|
|||
|
ServerCtx.CoinCtx.Stop()
|
|||
|
|
|||
|
time.Sleep(time.Second)
|
|||
|
|
|||
|
defer close(ServerCtx.SyncJobChan)
|
|||
|
//defer close(ServerCtx.AlivingChan)
|
|||
|
defer close(ServerCtx.ExitPingChan)
|
|||
|
defer close(ServerCtx.ExitJobChan)
|
|||
|
if ServerCtx.Listener != nil {
|
|||
|
defer ServerCtx.Listener.Close()
|
|||
|
}
|
|||
|
if ServerCtx.PubCh != nil {
|
|||
|
defer ServerCtx.PubCh.Destroy()
|
|||
|
|
|||
|
}
|
|||
|
if ServerCtx.SubCh != nil {
|
|||
|
defer ServerCtx.SubCh.Destroy()
|
|||
|
}
|
|||
|
|
|||
|
defer ServerCtx.RedisClient.Close()
|
|||
|
defer logg.Sync()
|
|||
|
defer logr.Close()
|
|||
|
|
|||
|
log.Println("Stopped")
|
|||
|
}
|