package alph import ( //"database/sql" "encoding/binary" "encoding/hex" "encoding/json" "strings" //"log" //"math" "math/big" "fmt" "pool/internal/msg" "pool/internal/server/coin" "pool/internal/server/dbif" "pool/internal/stratum" "pool/internal/utility" "time" _ "github.com/mattn/go-sqlite3" "go.uber.org/zap" ) const SERVER_ALPH_VERSION = "alph v1.0" type ServerAlphContext struct { ServerCtx *coin.ServerContext logg *zap.Logger AlphJob msg.AlphStratumJob } var logg *zap.Logger var ServerAlphCtx ServerAlphContext type Notify_msg_alph struct { ID interface{} `json:"id"` Method string `json:"method"` Params []msg.AlphStratumJob `json:"params"` } func handle_submit(miner *coin.MinerObj, id float64, miner_user string, job_id string, nonce string) (bool, bool, bool) { var submit_item coin.BlockMsg /*var user_blk_item coin.UserBlockMsg*/ var pool_blk_item coin.PoolBlkMsg var blk_detail_height int64 var blk_detail_hash string var blk_detail_success bool var blk_detail_miner_diff float64 var blk_detail_pool_diff float64 if miner.Authorized != true { miner.ErrOthers = miner.ErrOthers + 1 stratum.Handle_exception(miner, id, stratum.MINER_ERR_UNAUTH_WORKER) stratum.Send_reconnect_msg(miner) return false, false, false } var new_found bool = false var ack stratum.Submit_ack ack.ID = id ack.Result = true v, ok := miner.Jobs.Load(job_id) if ok { job := v.(msg.AlphStratumJob) if job.Height < miner.CurHeight-1 { ack.Result = false stratum.Handle_exception(miner, id, stratum.MINER_ERR_STALED_JOB) miner.ErrStaleds = miner.ErrStaleds + 1 return false, false, false } if (miner.LastNonce != nonce) || (miner.LastHeader != job.HeaderBlob) { miner.LastHeader = job.HeaderBlob miner.LastNonce = nonce job.Nonce = nonce logg.Debug("[server]", zap.Uint32("height", job.Height), zap.String("target", job.TargetBlob)) // ============== 对nonce+区块头进行double blake3 hash ============== phb, _ := hex.DecodeString(job.HeaderBlob) // 区块头 []byte nb, _ := hex.DecodeString(nonce) // nonce []byte // Blake3 Hash Test Code // headerStr := "00070000000208dd35a4511b86c67d4559b94a09a64f7b35ad8d821b5bb0fb4e8c05000000008e7b7b385b727d6bedfc7b9fa8b76f39a2ef318b406a522007b56faa000000020901b382ec2b14149902d5e825d5785b41794f6dfe7a767fe5488e5f00000003178d4a043a964da8afea94bb315711a29ab70532e69f0a51c77501200000000030d339eadb0ce263f5ee49a356dd967a7f23152b6453ab57671d550100000001632ef4dab01759432e37b50d818d39f15cabab11ae0ad9949b2ca742000000000be91faa2a5b6c68762856350092244146fa5d03899ed8aecd8ef0d3446b0d40f1f22eb47dc4df7c9f2a849fa2c2f5ed5c50d626360be988b92244aa0c5dfbc04f756fc5de7f962edf62996f08ed373c51112bcb09637f9a64e6640e00000194f909ea901dff0b28" // nonceStr := "cd1359f22f500001cd1359f22f50000151956b6400000000" // _header, _ := hex.DecodeString(headerStr) // _nonce, _ := hex.DecodeString(nonceStr) // _hash := Hash(append(_nonce, _header...)) // fmt.Println("测试用hash:", _hash) // Hash Diff Test Code // _hash, _ := hex.DecodeString("0000000044b373be04ff72d317ec7f1e63b7d8eb8babbc6f33ab29bf640feee7") // diff := utility.AlphShareDiff(_hash) // fmt.Println("测试用diff:", diff) // 计算hash, nonce + header var calc_hash []byte calc_hash = append(nb, phb...) calc_hash = Hash(calc_hash) logg.Debug("[server]", zap.String("hash in", submit_item.Header)) logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash))) // ================================================================== submit_target := new(big.Int) submit_target.SetBytes(calc_hash) // bignum.fromBuffer(hash); calc_diff := utility.AlphShareDiff(calc_hash) // 根据hash计算难度,shareDiff // fmt.Println("提交shares难度:", calc_diff) // fmt.Println("任务难度:", job.JobDifficulty) logg.Warn("[server]", zap.String("user", miner.User+"."+miner.Miner), zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff)) if calc_diff < job.JobDifficulty { ack.Result = false miner.ErrLowDiffs = miner.ErrLowDiffs + 1 stratum.Handle_exception(miner, id, stratum.MINER_ERR_LOW_DIF_SHARE) return false, false, false } var addZero string = "" if len(job.TargetBlob) < 64 { addZero = strings.Repeat("0", 64-len(job.TargetBlob)) } stb, _ := hex.DecodeString(addZero + job.TargetBlob) // server_diff := utility.Target2Diff(utility.Reverse(stb)) server_diff := utility.AlphShareDiff(utility.Reverse(stb)) network_target := new(big.Int) network_target.SetBytes(stb) Net_target := hex.EncodeToString(network_target.Bytes()) // jobTarget logg.Info("[server]", zap.Float64("calc_diff", calc_diff), zap.Float64("miner.Difficulty", miner.Difficulty), zap.Float64("server_diff", server_diff)) logg.Debug("[server]", zap.String("submit_target", hex.EncodeToString(submit_target.Bytes())), zap.String("network_target", hex.EncodeToString(network_target.Bytes())), zap.String("target", hex.EncodeToString(miner.ServerTarget.Bytes())), zap.Int("cmp", network_target.Cmp(submit_target))) submit_item.Hash = hex.EncodeToString(calc_hash) submit_item.Target = hex.EncodeToString(miner.Target.Bytes()) submit_item.Submit_target = hex.EncodeToString(calc_hash) submit_item.Height = int64(job.Height) submit_item.Pow = hex.EncodeToString(calc_hash) submit_item.Net_target = strings.Repeat("0", 64-len(Net_target)) + Net_target pool_blk_item.Height = int64(job.Height) pool_blk_item.Hash = hex.EncodeToString(calc_hash) pool_blk_item.Pow = hex.EncodeToString(calc_hash) pool_blk_item.Net_target = strings.Repeat("0", 64-len(Net_target)) + Net_target blk_detail_height = int64(job.Height) blk_detail_hash = hex.EncodeToString(calc_hash) blk_detail_success = false // blk_detail_miner_diff = job.JobDifficulty blk_detail_miner_diff = miner.Difficulty blk_detail_pool_diff = miner.Server.RefDifficulty // fmt.Println("mienr.Diff:", miner.Difficulty) // fmt.Println("job.Diff:", job.JobDifficulty) // fmt.Println("提交任务:", "Hash:", submit_item.Hash, "\tTarget:", submit_item.Target, "\tNet_target:", submit_item.Net_target) // fmt.Println("submit_item:", submit_item) // fmt.Println("pool_blk_item:", pool_blk_item) // fmt.Println("blk_detail_height:", blk_detail_height) if ack.Result == true { if (calc_diff >= server_diff) || (network_target.Cmp(submit_target) >= 0) { miner.Server.SubIdx++ Produce_block_submit(miner, &job, submit_item.Hash, miner.Server.SubIdx) miner.SubmitIndex++ miner.Submits = miner.Submits + 1 new_found = true } } } else { miner.LastHeader = job.HeaderBlob miner.LastNonce = nonce ack.Result = false stratum.Handle_exception(miner, id, stratum.MINER_ERR_DUP_SHARE) miner.ErrDuplicates = miner.ErrDuplicates + 1 return false, false, false } } else { ack.Result = false stratum.Handle_exception(miner, id, stratum.MINER_ERR_NOT_FOUND_JOB) miner.ErrStaleds = miner.ErrStaleds + 1 return false, false, false } miner.LastJobId = job_id ack.Error = nil body, err := json.Marshal(ack) if err != nil { logg.Error("[server]", zap.String("Marshal", err.Error())) miner.ErrOthers = miner.ErrOthers + 1 stratum.Handle_exception(miner, id, stratum.MINER_ERR_UNKNOWN) return false, false, false } var body_string = string(body) + "\n" err = stratum.Conn_tx(miner.Conn, []byte(body_string)) if err != nil { //miner.Server.Miners.Delete(miner.MinerId) } if miner.ZlogInit { miner.Zlog.Info().Msg(body_string) } logg.Debug("[server]", zap.String("tx", body_string)) miner.TxLock.Lock() miner.Status = coin.MINER_STATUS_RUNNING miner.TxLock.Unlock() if ack.Result { miner.Accepts += miner.Difficulty miner.M5Accepts += miner.Difficulty miner.VarDiffOpt.SubmitShares += miner.Difficulty } else { miner.Rejects += miner.Difficulty } now := time.Now() if miner.Server.Config.Diff.Filter == "kalman" { if ack.Result { share_interval := now.Sub(miner.LastSubmitime).Seconds() mhs := miner.Difficulty * share_interval diff_next, kalman_p := miner.DiffHandler.Handler(miner.Difficulty, share_interval) mhs_est := diff_next * miner.Server.Config.Diff.DiffAdjustInterval ratio := diff_next / miner.Difficulty if ratio > 0 { if now.Sub(miner.StartSubmitTime).Seconds() > 180 { if ratio >= 2 { //miner.DifficultyNext = math.Ceil(diff_next*100) / 100 miner.DifficultyNext = diff_next * 10000000 / 10000000 } else if ratio <= 0.5 { //miner.DifficultyNext = math.Ceil(diff_next*100) / 100 miner.DifficultyNext = diff_next * 10000000 / 10000000 } else { } } else { //miner.DifficultyNext = math.Ceil(diff_next*100) / 100 miner.DifficultyNext = diff_next * 10000000 / 10000000 } } if miner.DifficultyNext > 0.0 { if miner.DifficultyNext < miner.VarDiffOpt.MinDiff { miner.DifficultyNext = miner.VarDiffOpt.MinDiff } else if miner.DifficultyNext > miner.VarDiffOpt.MaxDiff { miner.DifficultyNext = miner.VarDiffOpt.MaxDiff } } //miner.VarDiffOpt.LastCalcTime = now if miner.Server.Config.Diff.Dbg { coin.New_diff_into_db(miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.Difficulty, diff_next, kalman_p, share_interval, mhs, mhs_est) } //log.Println("diff adjust", ratio, diff_next, miner.Difficulty, miner.DifficultyNext) } } else { // submit time < DiffAdjustInterval,then up adjust diff if now.Sub(miner.LastSubmitime).Seconds() < miner.Server.Config.Diff.DiffAdjustInterval { if ack.Result { if miner.VarDiffOpt.Uptimes++; miner.VarDiffOpt.Uptimes >= coin.DIFFICULTY_WAIT_TIMES { coin.VarAdjustDifficulty(miner, coin.UP_DIFF) miner.VarDiffOpt.LastCalcTime = now } } } else { miner.VarDiffOpt.Uptimes = 0 } // submit time > 2 * DiffAdjustInterval,then down adjust diff if now.Sub(miner.LastSubmitime).Seconds() > miner.Server.Config.Diff.DiffAdjustInterval*2 { if ack.Result { if miner.VarDiffOpt.Downtimes++; miner.VarDiffOpt.Downtimes >= coin.DIFFICULTY_WAIT_TIMES { coin.VarAdjustDifficulty(miner, coin.DOWN_DIFF) miner.VarDiffOpt.LastCalcTime = now } } } else { miner.VarDiffOpt.Downtimes = 0 } } if ack.Result { miner.LastSubmitime = now miner.VarDiffOpt.LastSubmitTime = now } var duration float64 = float64(now.Sub(miner.StartSubmitTime)) / 1000000000 if duration < 1 { duration = 1 } diffOneShareHashesAvg := uint64(0x00000000FFFFFFFF) miner.AverageHashrate = miner.Accepts * float64(diffOneShareHashesAvg) / duration / 1000000 var m5_duration float64 = float64(now.Sub(miner.M5SubmitTime)) / 1000000000 if m5_duration >= float64(time.Minute*5)/1000000000 { miner.M5SubmitTime = now miner.M5Hashrate = miner.M5Accepts * float64(diffOneShareHashesAvg) / m5_duration / 1000000 logg.Info("[server]", zap.Float64("Accepts", miner.Accepts), zap.Float64("M5Accepts", miner.M5Accepts), zap.Float64("M5Hashrate(MH/S)", miner.M5Hashrate)) miner.M5Accepts = 0 } logg.Warn("[server]", zap.Float64("M5Accepts", miner.M5Accepts), zap.Float64("M5Hashrate(MH/S)", miner.M5Hashrate)) if miner.Server.Config.Diff.Filter == "kalman" { } else { if float64(now.Sub(miner.VarDiffOpt.LastCalcTime))/1000000000 >= miner.VarDiffOpt.AdjustTime { coin.VarAdjustDifficulty(miner, coin.UPDATE_DIFF) miner.VarDiffOpt.LastCalcTime = now } } if new_found { pool_blk_item.Submit = "y" pool_blk_item.Success = false pool_blk_item.Accepts = miner.Accepts pool_blk_item.Rejects = miner.Rejects pool_blk_item.Reward = 0 pool_blk_item.Fee = 0 pool_blk_item.Nonce = nonce pool_blk_item.SubIdx = miner.Server.SubIdx dbif.NotifyPoolBlkStatsDb2(miner.Server, &pool_blk_item) } if ack.Result { submit_item.Success = false if new_found { submit_item.Submit = "y" submit_item.SubIdx = miner.Server.SubIdx } else { submit_item.Submit = "n" submit_item.SubIdx = -1 } submit_item.Accepts = miner.Accepts submit_item.Total_accepts = miner.Accepts submit_item.Rejects = miner.Rejects submit_item.Total_rejects = miner.Rejects submit_item.Reward = 0 submit_item.Fee = 0 submit_item.Nonce = nonce dbif.NotifyBlkDetailDb(miner, blk_detail_height, blk_detail_hash, blk_detail_success, blk_detail_miner_diff, blk_detail_pool_diff, nonce, submit_item.SubIdx) return true, new_found, true } return false, false, true } // 该方法和alph-gbt的768行代码相对应 func Produce_block_submit(miner *coin.MinerObj, job *msg.AlphStratumJob, PowHash string, SubIdx int64) { var nm msg.BlockAlphMsg nm.Id = job.JobId nm.Header = job.HeaderBlob nm.Nonce = job.Nonce nm.FromGroup = job.FromGroup nm.ToGroup = job.ToGroup nm.SubIdx = SubIdx nm.User = miner.User nm.Miner = miner.Miner nm.Hash = PowHash nm.Index = fmt.Sprint(miner.MinerIndex) body, err := json.Marshal(nm) if err != nil { logg.Error("[server]", zap.String("failed to Marshal job", err.Error())) return } blk := string(body) //Add Height heightb := utility.Uint32ToByte(job.Height) heights := hex.EncodeToString(heightb) blk += heights var Height uint32 = utility.ByteToUint32(heightb) logg.Warn("[server]", zap.Uint32("Height", Height)) //Add SubmitIndex indexb := utility.Uint32ToByte(miner.SubmitIndex) indexs := hex.EncodeToString(indexb) blk += indexs var SubmitIndex uint32 = utility.ByteToUint32(indexb) logg.Info("[server]", zap.Uint32("SubmitIndex", SubmitIndex)) logg.Info("[server]", zap.String("blk", blk)) if miner.Server.PubCh == nil { miner.Server.PubCh = utility.InitZmqPub(miner.Server.Config.Zmq.Pub) } if miner.Server.PubCh != nil { //miner.Server.PubCh.SendChan <- [][]byte{[]byte("blknexa"), []byte(blk)} err := miner.Server.PubCh.SendMessage([][]byte{[]byte("blkalph"), []byte(blk)}) if err != nil { miner.Server.PubCh.Destroy() miner.Server.PubCh = nil logg.Info("[server]", zap.String("blk", err.Error())) } else { logg.Info("[server]", zap.String("blk", "sent")) } } } func alph_parse_miner_notify(miner *coin.MinerObj, msg msg.AlphStratumJob) int { if miner.AlphJob.Height != msg.Height { miner.Job.IsClean = true } miner.AlphJob = msg miner.AlphJob.Extranonce1 = miner.Job.Extranonce1 miner.Job.Extranonce2_size = msg.Extranonce2_size //miner.Server.Logg.Info("[server]", zap.Int32("miner.Version", miner.Version), zap.Int32("msg.Version", msg.Version)) return 1 } func Init(server *coin.ServerContext) { ServerAlphCtx.ServerCtx = server logg = server.Logg logg.Info("[server]", zap.String("server_alph_version", SERVER_ALPH_VERSION)) coin.Init_diff_db() } func Start() { } func Stop() { coin.DiffStop() } func InitMiner(miner *coin.MinerObj) { be1 := make([]byte, 8) binary.LittleEndian.PutUint64(be1, (miner.Server.Extranonce1 /* + 0x81000000*/)) miner.Job.Extranonce1 = hex.EncodeToString(be1) miner.AlphJob.Extranonce1 = miner.Job.Extranonce1 miner.Server.Extranonce1++ // fmt.Println("miner.Server.Extranonce1:", miner.Server.Extranonce1) target, err := utility.DiffToTarget(miner.Difficulty) if err != nil { logg.Error("[server]", zap.String("DiffToTarget", err.Error())) return } miner.Target = target logg.Debug("[target]", zap.String("target", hex.EncodeToString(target.Bytes())), zap.Float64("diff", miner.Difficulty)) server_target := new(big.Int) t_bytes, err := hex.DecodeString(miner.AlphJob.TargetBlob) if err != nil { logg.Error("[server]", zap.String("DecodeString", err.Error())) return } //server_target.SetBytes(common.Reverse(t_bytes)) server_target.SetBytes(t_bytes) miner.ServerTarget = server_target miner.ServerTargetS = miner.Server.SJob.Target miner.AlphJob = miner.Server.AlphJob } func HandleMinerSubscribe(miner *coin.MinerObj, id float64, extranonce1 string, msg string) { } func HandleMinerAuth(miner *coin.MinerObj) { // randomNumber := rand.Intn(65536) // hexString := fmt.Sprintf("%04x", randomNumber) // method := "mining.set_extranonce" // params := []string{hexString} // setExtranonce_msg := stratum.AlphSetExtranonce{Method: method, ID: nil, Params: params} // fmt.Println("extranonce:", setExtranonce_msg) // ex_msg, err := json.Marshal(setExtranonce_msg) // if err != nil { // logg.Error("[server]", zap.String("Marshal", err.Error())) // return // } // stratum.Conn_tx(miner.Conn, ex_msg) } func HandleMinerSubmit(miner *coin.MinerObj, id float64, miner_user string, job_id string, nonce2 string, ntime string, nonce string) (bool, bool, bool) { accept_ok, submit_ok, handle_ok := handle_submit(miner, id, miner_user, job_id, nonce) return accept_ok, submit_ok, handle_ok } func SetDifficulty(miner *coin.MinerObj) { extra_msg := stratum.AlphSetExtranonce() err := stratum.Conn_tx(miner.Conn, extra_msg) if err != nil { fmt.Println("alph.set_extranonce发送消息失败:", err) } stratum.Set_difficulty(miner) } func AlphNotify(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 stratum.Set_difficulty(miner) logg.Info("[gbt]", zap.Float64("update Diff", miner.Difficulty)) } else { miner.DifficultyNext = -1 } } miner.TxLock.Lock() var params msg.AlphStratumJob target := utility.AlphDiffToTarget2(miner.Difficulty) params.JobId = miner.AlphJob.JobId params.HeaderBlob = miner.AlphJob.HeaderBlob params.TargetBlob = target params.Height = miner.AlphJob.Height params.FromGroup = miner.AlphJob.FromGroup params.ToGroup = miner.AlphJob.ToGroup params.TxsBlob = miner.AlphJob.TxsBlob miner.CurHeight = miner.AlphJob.Height miner.AlphJob.JobDifficulty = miner.Difficulty miner.Jobs.LoadOrStore(miner.AlphJob.JobId, miner.AlphJob) stratum.AddAndUpdateJob(miner) stratum.UpdateJobs(miner) miner.JobId++ // 发送mining.notify消息 var body []byte var err error var msg Notify_msg_alph msg.ID = nil msg.Method = "mining.notify" msg.Params = append(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 = stratum.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 Notify(miner *coin.MinerObj) { AlphNotify(miner) } func HandleJobMsg(server *coin.ServerContext, Msg []byte) { var result msg.AlphStratumJob server.Logg.Warn("[server]", zap.String("receive", "job")) if err := json.Unmarshal(Msg, &result); err != nil { server.Logg.Error("[server]", zap.String("Unmarshal", err.Error())) return } server.AlphJob = msg.AlphStratumJob(result) logg.Debug("[gbt]", zap.String("Target", server.AlphJob.TargetBlob)) // server.NexaJob.Extranonce2_size = 8 // server.SJob.Extranonce2_size = 8 logg.Debug("[gbt]", zap.Uint32("Height", server.AlphJob.Height), zap.String("Target", server.AlphJob.TargetBlob), zap.String("Header", server.AlphJob.HeaderBlob) /*, zap.Uint64("Timastamp", server.NexaJob.CurTime)*/) // targetb, _ := hex.DecodeString(server.AlphJob.TargetBlob) // jobIdInt, err := strconv.ParseInt(server.AlphJob.JobId, 16, 64) // if err != nil { // logg.Error("[server]", zap.String("Atoi", err.Error())) // return // } // uint64Id := uint64(jobIdInt) // logg.Debug("[gbt]", zap.Uint64("Id", uint64Id), zap.Float64("network diff", utility.Target2Diff(utility.Reverse(targetb)))) server.NetHight = uint64(server.AlphJob.Height) server.NetTarget = server.AlphJob.TargetBlob // 任务的target就是全网target server.Miners.Range(func(k, v interface{}) bool { m, ok := v.(*(coin.MinerObj)) if ok { server.Logg.Info("[server]", zap.String("lock", "start")) m.TxLock.Lock() status := m.Status cmd := alph_parse_miner_notify(m, server.AlphJob) m.TxLock.Unlock() server.Logg.Info("[server]", zap.String("lock", "end")) var need_notify bool = true if time.Now().Sub(m.ConnSetupTime) >= time.Duration(coin.CONN_EXPIRED_TIME)*time.Second { if (status != coin.MINER_STATUS_RUNNING) && (status != coin.MINER_STATUS_AUTHORIZED) { //m.Conn.Close() need_notify = false } } if need_notify { switch cmd { case 0: //extranonce 1 and extranonce2 size //TODO case 1: //notify AlphNotify(m) } } } return true }) } func IsMhsLow(miner *coin.MinerObj) bool { if miner.Mhs5M < 1 { return true } return false } func GetBlockInterval() int { return 180 }