757 lines
21 KiB
Go
757 lines
21 KiB
Go
package monero
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"strings"
|
||
|
||
//"database/sql"
|
||
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
|
||
//"log"
|
||
//"math"
|
||
"math/big"
|
||
//"strings"
|
||
|
||
"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_MONERO_VERSION string = "monero v0.1a"
|
||
|
||
type ServerMoneroContext struct {
|
||
ServerCtx *coin.ServerContext
|
||
logg *zap.Logger
|
||
|
||
Sha3xJob msg.Sha3xStratumJob
|
||
|
||
RandomxVM *RandomXValidator
|
||
}
|
||
|
||
var logg *zap.Logger
|
||
var ServerMoneroCtx ServerMoneroContext
|
||
|
||
type MoneroNotify_params_msg struct {
|
||
Id string `json:"id"`
|
||
JobId string `json:"job_id"`
|
||
SeedHash string `json:"seed_hash"`
|
||
Blob string `json:"blob"`
|
||
Height uint32 `json:"height"`
|
||
Target string `json:"target"`
|
||
NextSeedHash string `json:"next_seed_hash"`
|
||
Algo string `json:"algo"`
|
||
}
|
||
|
||
type Monero_msg struct {
|
||
Jsonrpc string `json:"jsonrpc"`
|
||
Method string `json:"method"`
|
||
Params MoneroNotify_params_msg `json:"params"`
|
||
}
|
||
|
||
// 辅助函数:反转每个字节的小端 hex
|
||
func reverseHexBytes(s string) string {
|
||
if len(s)%2 != 0 {
|
||
s = "0" + s
|
||
}
|
||
res := ""
|
||
for i := len(s); i > 0; i -= 2 {
|
||
res += s[i-2 : i]
|
||
}
|
||
return res
|
||
}
|
||
|
||
func calc_target(diff uint64) string {
|
||
difficulty := new(big.Int)
|
||
difficulty.SetString(fmt.Sprintf("%d", diff), 10)
|
||
|
||
// 2^256 - 1
|
||
max := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
|
||
|
||
// target = (2^256 - 1) / difficulty
|
||
target := new(big.Int).Div(max, difficulty)
|
||
|
||
// 转为32字节 hex(大端)
|
||
targetHexBE := fmt.Sprintf("%064x", target)
|
||
return targetHexBE
|
||
}
|
||
|
||
func calc_diff(hash string) uint64 {
|
||
be := reverseHexBytes(hash)
|
||
target_be := new(big.Int)
|
||
target_be.SetString(be, 16)
|
||
|
||
max := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
|
||
difficulty := new(big.Rat).SetFrac(max, target_be)
|
||
// Convert *big.Rat to *big.Float, then to uint64
|
||
diffFloat := new(big.Float).SetRat(difficulty)
|
||
result, _ := diffFloat.Uint64()
|
||
return result
|
||
}
|
||
|
||
func handle_submit(miner *coin.MinerObj, id float64, miner_user string, job_id string, nonce2 string, ntime 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
|
||
// fmt.Println("提交job_id:", job_id)
|
||
var keys []string
|
||
miner.Jobs.Range(func(k, v interface{}) bool {
|
||
if key, ok := k.(string); ok {
|
||
keys = append(keys, key)
|
||
}
|
||
return true // 继续遍历
|
||
})
|
||
|
||
// fmt.Println("目前任务所有key:", keys)
|
||
v, ok := miner.Jobs.Load(job_id)
|
||
if ok {
|
||
job := v.(msg.MoneroStratumJob)
|
||
|
||
if uint32(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.MoneroJob.BlocktemplateBlob != job.BlocktemplateBlob) {
|
||
miner.MoneroJob.BlocktemplateBlob = job.BlocktemplateBlob
|
||
miner.LastNonce = nonce
|
||
job.Nonce = nonce
|
||
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg("height " + string(job.Height) + " target " + job.Target + " " + miner.User + "." + miner.Miner)
|
||
}
|
||
|
||
var calc_hash []byte
|
||
var completeHeader []byte
|
||
nonceByte, err := hex.DecodeString(nonce)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return false, false, false
|
||
}
|
||
headerByte, err := hex.DecodeString(job.BlockhashingBlob)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return false, false, false
|
||
}
|
||
|
||
calc_hash, completeHeader, err = ServerMoneroCtx.RandomxVM.BuildPowHash(headerByte, nonceByte)
|
||
if err != nil {
|
||
fmt.Println("calc_hash error:", err)
|
||
return false, false, false
|
||
}
|
||
job.CompleteHeader = completeHeader
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg("hash in " + submit_item.Header + " calc_hash " + hex.EncodeToString(calc_hash) + " " + miner.User + "." + miner.Miner)
|
||
}
|
||
submit_target := new(big.Int)
|
||
|
||
submit_target.SetBytes(calc_hash)
|
||
|
||
calc_diff := utility.MoneroTarget2Diff(calc_hash)
|
||
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " target diff " + fmt.Sprintf("%f", (miner.Difficulty)) + " submit diff " + fmt.Sprintf("%f", (calc_diff)))
|
||
}
|
||
|
||
if calc_diff < float64(job.Difficulty) {
|
||
ack.Result = false
|
||
miner.ErrLowDiffs = miner.ErrLowDiffs + 1
|
||
stratum.Handle_exception(miner, id, stratum.MINER_ERR_LOW_DIF_SHARE)
|
||
return false, false, false
|
||
|
||
}
|
||
|
||
stb, _ := hex.DecodeString(job.Target)
|
||
|
||
server_diff := utility.MoneroTarget2Diff(utility.Reverse(stb))
|
||
network_target := new(big.Int)
|
||
network_target.SetBytes(stb)
|
||
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " calc_diff " + fmt.Sprintf("%f", (calc_diff)) + " miner.Difficulty " + fmt.Sprintf("%f", (miner.Difficulty)) + " server_diff " + fmt.Sprintf("%f", (server_diff)))
|
||
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " submit_target " + hex.EncodeToString(submit_target.Bytes()) + " network_target " + hex.EncodeToString(network_target.Bytes()) + " target " + hex.EncodeToString(miner.ServerTarget.Bytes()) + " cmp " + fmt.Sprintf("%d", (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 = hex.EncodeToString(network_target.Bytes())
|
||
|
||
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 = hex.EncodeToString(network_target.Bytes())
|
||
|
||
blk_detail_height = int64(job.Height)
|
||
blk_detail_hash = hex.EncodeToString(calc_hash)
|
||
blk_detail_success = false
|
||
|
||
blk_detail_miner_diff = float64(job.Difficulty)
|
||
blk_detail_pool_diff = miner.Server.RefDifficulty
|
||
if ack.Result == true {
|
||
if (calc_diff >= server_diff) || (network_target.Cmp(submit_target) >= 0) {
|
||
miner.Server.SubIdx++
|
||
Produce_block_submit(miner /*header,*/, &job, submit_item.Hash, miner.Server.SubIdx)
|
||
miner.SubmitIndex++
|
||
miner.Submits = miner.Submits + 1
|
||
new_found = true
|
||
}
|
||
if new_found && float64(miner.Server.MoneroJob.Difficulty) <= calc_diff {
|
||
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)
|
||
}
|
||
}
|
||
} else {
|
||
miner.LastHeader = job.BlocktemplateBlob
|
||
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 {
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " handle_submit 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)
|
||
}
|
||
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 = diff_next * 10000000 / 10000000
|
||
} else if ratio <= 0.5 {
|
||
miner.DifficultyNext = diff_next * 10000000 / 10000000
|
||
} else {
|
||
}
|
||
} else {
|
||
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
|
||
}
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
}
|
||
} else {
|
||
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
|
||
}
|
||
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(0xFFFFFFFFFFFFFFFF)
|
||
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
|
||
|
||
miner.M5Accepts = 0
|
||
}
|
||
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg(miner.User + "." + miner.Miner + " handle_submit M5Accepts " + fmt.Sprintf("%f", (miner.M5Accepts)) + " M5Hashrate(MH/S) " + fmt.Sprintf("%f", (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 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
|
||
}
|
||
|
||
func contractBlockTemplateBlob(miner *coin.MinerObj, nonceHex string) (string, error) {
|
||
blockTemplateStr := miner.MoneroJob.BlocktemplateBlob
|
||
block, err := hex.DecodeString(blockTemplateStr)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if len(block) < 43 {
|
||
return "", fmt.Errorf("blocktemplate blob too short: %d", len(block))
|
||
}
|
||
|
||
// nonce 是 4 字节 hex,直接解码成 bytes
|
||
nonceBytes, err := hex.DecodeString(nonceHex)
|
||
if err != nil {
|
||
return "", fmt.Errorf("invalid nonce hex: %v", err)
|
||
}
|
||
if len(nonceBytes) != 4 {
|
||
return "", fmt.Errorf("nonce must be 4 bytes, got %d", len(nonceBytes))
|
||
}
|
||
|
||
// 覆盖 nonce 区域 (39~42)
|
||
copy(block[39:43], nonceBytes)
|
||
|
||
return hex.EncodeToString(block), nil
|
||
}
|
||
|
||
func Produce_block_submit(miner *coin.MinerObj /*header Sha3xBlockHeader,*/, job *msg.MoneroStratumJob, PowHash string, SubIdx int64) {
|
||
var nm msg.BlockMoneroMsg
|
||
blockBlob, err := contractBlockTemplateBlob(miner, job.Nonce)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
nm.Id = job.Id
|
||
nm.Header = blockBlob
|
||
nm.Nonce = job.Nonce
|
||
nm.Pow = PowHash
|
||
nm.SubIdx = SubIdx
|
||
nm.User = miner.User
|
||
nm.Miner = miner.Miner
|
||
nm.Height = job.Height
|
||
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
|
||
}
|
||
|
||
// JSON主体
|
||
blk := string(body)
|
||
|
||
// 高度(uint32 → 4字节 → hex编码8字符)
|
||
heightHex := fmt.Sprintf("%08x", job.Height)
|
||
|
||
// Index(uint32 → 4字节 → hex编码8字符)
|
||
indexHex := fmt.Sprintf("%08x", miner.MinerIndex)
|
||
|
||
// 拼接最终消息
|
||
msg := blk + heightHex + indexHex
|
||
// fmt.Println(msg)
|
||
logg.Info("[server]", zap.String("final_msg", msg))
|
||
|
||
if miner.Server.PubCh == nil {
|
||
miner.Server.PubCh = utility.InitZmqPub(miner.Server.Config.Zmq.Pub)
|
||
}
|
||
if miner.Server.PubCh != nil {
|
||
// fmt.Println(msg)
|
||
err := miner.Server.PubCh.SendMessage([][]byte{[]byte("blkmonero"), []byte(msg)})
|
||
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"))
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// var start_job_id uint64 = 0
|
||
|
||
// server-->miner
|
||
func parse_miner_notify(miner *coin.MinerObj, msg msg.MoneroStratumJob) int {
|
||
if miner.MoneroJob.Height != msg.Height {
|
||
miner.Job.IsClean = true
|
||
}
|
||
miner.MoneroJob = msg
|
||
miner.MoneroJob.JobId = msg.JobId
|
||
return 1
|
||
}
|
||
|
||
func Init(server *coin.ServerContext) {
|
||
ServerMoneroCtx.ServerCtx = server
|
||
ServerMoneroCtx.RandomxVM = &RandomXValidator{}
|
||
logg = server.Logg
|
||
logg.Info("[server]", zap.String("server_sha3x_version", SERVER_MONERO_VERSION))
|
||
coin.Init_diff_db()
|
||
}
|
||
|
||
func Start() {
|
||
|
||
}
|
||
|
||
func Stop() {
|
||
coin.DiffStop()
|
||
}
|
||
|
||
func InitMiner(miner *coin.MinerObj) {
|
||
miner.MoneroJob = miner.Server.MoneroJob
|
||
// miner.MoneroJob.Extranonce1 = miner.Job.Extranonce1
|
||
|
||
server_target := new(big.Int)
|
||
t_bytes, err := hex.DecodeString(miner.MoneroJob.Target)
|
||
if err != nil {
|
||
logg.Error("[server]", zap.String("DecodeString", err.Error()))
|
||
return
|
||
}
|
||
//server_target.SetBytes(common.Reverse(t_bytes))
|
||
miner.MoneroJob = ServerMoneroCtx.ServerCtx.MoneroJob
|
||
server_target.SetBytes(t_bytes)
|
||
miner.ServerTarget = server_target
|
||
miner.ServerTargetS = miner.Server.SJob.Target
|
||
miner.CurHeight = uint32(miner.MoneroJob.Height)
|
||
}
|
||
|
||
func Handle_subscribe_sha3x(miner *coin.MinerObj, id float64, extranonce1 string) {
|
||
|
||
}
|
||
|
||
func HandleMinerSubscribe(miner *coin.MinerObj, id float64, extranonce1 string, msg string) {
|
||
|
||
}
|
||
|
||
func HandleMinerAuth(miner *coin.MinerObj) {
|
||
|
||
}
|
||
|
||
func HandleMinerSubmit(miner *coin.MinerObj, id float64, miner_user string, job_id string, nonce2 string, ntime string, nonce string) (bool, bool, bool) {
|
||
//nonce_str, _ := stratum.ReverseHexStringByByte(nonce)
|
||
accept_ok, submit_ok, handle_ok := handle_submit(miner, id, miner_user, job_id, nonce2, ntime, nonce)
|
||
return accept_ok, submit_ok, handle_ok
|
||
}
|
||
|
||
func SetDifficulty(miner *coin.MinerObj) {
|
||
stratum.Set_difficulty(miner)
|
||
}
|
||
|
||
func MoneroNotify(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()
|
||
//log.Println("[server]extra1, id", miner.Job.Extranonce1, miner.Job.Job_id, miner.MinerId)
|
||
|
||
var msg Monero_msg
|
||
|
||
msg.Params.Id = miner.Session
|
||
msg.Params.SeedHash = miner.MoneroJob.SeedHash
|
||
msg.Params.JobId = miner.MoneroJob.JobId
|
||
msg.Params.Blob = miner.MoneroJob.BlockhashingBlob
|
||
msg.Params.Height = uint32(miner.MoneroJob.Height)
|
||
miner.MoneroJob.Difficulty = uint64(miner.Difficulty)
|
||
msg.Params.NextSeedHash = ""
|
||
msg.Params.Algo = "rx/0"
|
||
//target_s, _ := stratum.ReverseHexStringByByte(miner.Sha3xJob.Target)
|
||
//msg.Params.Target = target_s[48:]
|
||
target_new, _ := utility.MoneroDiffToTarget(miner.Difficulty)
|
||
target_str := fmt.Sprintf("%064x", target_new.Bytes())
|
||
target_strr, strerr := stratum.ReverseHexStringByByte(target_str)
|
||
if strerr != nil {
|
||
println("ReverseHexStringByByte", strerr.Error())
|
||
}
|
||
//println("target=", target_str, "r=", target_strr)
|
||
msg.Params.Target = target_strr[48:]
|
||
|
||
miner.CurHeight = uint32(miner.MoneroJob.Height)
|
||
|
||
// miner.MoneroJob.JobDifficulty = miner.Difficulty
|
||
|
||
miner.Jobs.LoadOrStore(miner.MoneroJob.JobId, miner.MoneroJob)
|
||
|
||
stratum.AddAndUpdateJob(miner)
|
||
|
||
stratum.UpdateJobs(miner)
|
||
|
||
miner.JobId++
|
||
|
||
var body []byte
|
||
var err error
|
||
|
||
msg.Jsonrpc = "2.0"
|
||
msg.Method = "job"
|
||
|
||
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.TxLock.Unlock()
|
||
if miner.ZlogInit {
|
||
miner.Zlog.Info().Msg(body_string)
|
||
}
|
||
}
|
||
|
||
func Notify(miner *coin.MinerObj) {
|
||
MoneroNotify(miner)
|
||
}
|
||
|
||
func formatUint64ToHexWithPadding(val uint64) string {
|
||
hexStr := fmt.Sprintf("%016x", val)
|
||
|
||
if len(hexStr) < 64 {
|
||
paddingLen := 64 - len(hexStr)
|
||
hexStr += string(make([]byte, paddingLen))
|
||
for i := len(hexStr) - paddingLen; i < 64; i++ {
|
||
hexStr = hexStr[:i] + "0" + hexStr[i+1:]
|
||
}
|
||
}
|
||
return hexStr
|
||
}
|
||
|
||
func formatWideTargetTo32BytesTarget(wide_target string) string {
|
||
if len(wide_target) > 64 {
|
||
panic("任务中的wide_target错误:")
|
||
}
|
||
// 去掉前缀 0x 或 0X
|
||
wide_target = strings.TrimPrefix(wide_target, "0x")
|
||
wide_target = strings.TrimPrefix(wide_target, "0X")
|
||
wide_target = fmt.Sprintf("%0*s%s", 64-len(wide_target), "", wide_target)
|
||
return wide_target
|
||
}
|
||
|
||
var last_seed string = ""
|
||
|
||
func randomxJobId() string {
|
||
// 生成4个字节
|
||
bytes := make([]byte, 4)
|
||
_, err := rand.Read(bytes)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// 转成 hex 字符串
|
||
hexStr := hex.EncodeToString(bytes)
|
||
return hexStr
|
||
}
|
||
|
||
func HandleJobMsg(server *coin.ServerContext, Msg []byte) {
|
||
var result msg.MoneroStratumJob
|
||
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
|
||
}
|
||
result.Target = calc_target(result.Difficulty)
|
||
// 上个模板 seed_hash 和本次 seed_hash 不一致时,重置 randomx 虚拟机
|
||
if result.SeedHash != last_seed {
|
||
fmt.Println("开始创建新的 randomx vm, 本次 seed_hash:", result.SeedHash)
|
||
|
||
seedBytes, err := hex.DecodeString(result.SeedHash)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// 如果已有旧 VM,先释放
|
||
if ServerMoneroCtx.RandomxVM != nil {
|
||
ServerMoneroCtx.RandomxVM.Destroy()
|
||
}
|
||
|
||
// 创建新 VM
|
||
vm, err := NewRandomXValidator(seedBytes)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
ServerMoneroCtx.RandomxVM = vm
|
||
last_seed = result.SeedHash
|
||
}
|
||
|
||
server.MoneroJob = msg.MoneroStratumJob(result)
|
||
logg.Debug("[gbt]", zap.String("Target", server.MoneroJob.Target))
|
||
logg.Debug("[gbt]", zap.Uint64("Id", server.MoneroJob.Id), zap.Float64("network diff", float64(server.MoneroJob.Difficulty)))
|
||
|
||
server.NetHight = uint64(server.MoneroJob.Height) // 当前server中的全网高度
|
||
server.NetTarget = result.Target // 当前server中的全网target
|
||
server.Miners.Range(func(k, v interface{}) bool {
|
||
if v != nil {
|
||
m, ok := v.(*(coin.MinerObj))
|
||
if ok {
|
||
if m != nil {
|
||
server.Logg.Info("[server]", zap.String("lock", "start"))
|
||
m.TxLock.Lock()
|
||
status := m.Status
|
||
cmd := parse_miner_notify(m, server.MoneroJob)
|
||
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
|
||
MoneroNotify(m)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return true
|
||
})
|
||
}
|
||
|
||
func IsMhsLow(miner *coin.MinerObj) bool {
|
||
if miner.Mhs5M < 1 {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
func GetBlockInterval() int {
|
||
return 3600
|
||
}
|