create git

This commit is contained in:
lzx
2025-09-03 16:00:42 +08:00
commit 953a76439a
55 changed files with 29879 additions and 0 deletions

View File

@@ -0,0 +1,681 @@
// coin.go
package coin
import (
"container/list"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"database/sql"
"encoding/hex"
"io"
"log"
"math"
"math/big"
"net"
"fmt"
"pool/internal/db"
"pool/internal/msg"
"pool/internal/server/diff"
"pool/internal/utility"
"sync"
"time"
"github.com/rs/zerolog"
"github.com/redis/go-redis/v9"
"github.com/zeromq/goczmq"
"go.uber.org/zap"
"gopkg.in/natefinch/lumberjack.v2"
_ "github.com/mattn/go-sqlite3"
)
const JOB_EXPIRED_TIME uint32 = 3600 //job expired time (second)
const LOCAL_JOBS_EXPIRED_TIME uint32 = 3600 //300 //local jobs expired time (second)
const LOCAL_JOBS_TOTAL_SIZE uint32 = 100 //300 //total local jobs
const CONN_EXPIRED_TIME uint32 = 600 //connection expired time
// miner status
// const MINER_STATUS_OFFLINE string = "offline"
const MINER_STATUS_CONNECTED string = "connected"
const MINER_STATUS_SUBSCRIBED string = "subscribed"
const MINER_STATUS_AUTHORIZED string = "authorized"
const MINER_STATUS_RUNNING string = "online"
const MINER_STATUS_DISCONNECTED string = "offline"
const MINER_STATUS_DISABLED string = "disabled"
const MINER_DURATION_TIME time.Duration = 1
const MINER_DIFFICULTY_ADJUST_DURATION time.Duration = 5
// vardiff
const UP_DIFF int = 0
const DOWN_DIFF int = 1
const UPDATE_DIFF int = 2
const DIFFICULTY_WAIT_TIMES int = 6
const (
Low = 0
Mid = 1
Hign = 2
)
type BindConfig struct {
Listen string `json:"listen"`
Auth bool `json:"auth"`
}
type DiffConfig struct {
StartDifficulty float64 `json:"start_diff"`
DiffMin float64 `json:"diff_min"`
DiffMax float64 `json:"diff_max"`
DiffAdjustInterval float64 `json:"diff_adjust_interval"`
DiffAdjustPercentage float64 `json:"diff_adjust_percentage"`
DiffAdjustTime float64 `json:"diff_adjust_time"`
Filter string `json:"filter"`
Dbg bool `json:"dbg"`
}
type ServerConfig struct {
Coin string `json:"coin"`
Host BindConfig `json:"host"`
Diff DiffConfig `json:"diff"`
Zmq utility.ZmqConfig `json:"zmq"`
Redis utility.RedisConfig `json:"redis"`
Zaplog zap.Config `json:"zap"`
Logrotae utility.LogRotateConfig `json:"logrotate"`
}
type CoinObj struct {
Coin string
Init func(server *ServerContext)
Start func()
Stop func()
InitMiner func(miner *MinerObj)
HandleMinerSubscribe func(miner *MinerObj, id float64, extranonce1 string, msg string)
HandleMinerAuth func(miner *MinerObj)
HandleMinerSubmit func(miner *MinerObj, id float64, miner_user string, job_id string, nonce2 string, ntime string, nonce string) (bool, bool, bool)
SetDifficulty func(miner *MinerObj)
Notify func(miner *MinerObj)
HandleJobMsg func(server *ServerContext, Msg []byte)
IsMhsLow func(miner *MinerObj) bool
GetBlockInterval func() int
}
type PoolBlkMsg struct {
Height int64
Hash string
Pow string
Net_target string
Submit string
Success bool
Accepts float64
Rejects float64
Reward float64
Fee float64
Nonce string
SubIdx int64
}
type ServerContext struct {
CoinCtx CoinObj
DbCtx *db.DbContext
Config *ServerConfig
PubCh *goczmq.Sock
SubCh *goczmq.Sock
Listener net.Listener
MinerType string
Extranonce1 uint64
Difficulty float64
RefDifficulty float64
Accepts float64
Rejects float64
AverageHashrate float64
Miners sync.Map
MMhs sync.Map
Started bool
SLock sync.Mutex
ExitFlag bool
//AlivingChan chan bool
//LiveingExpired bool
FlagAliving int32
FlagAlivingExit int32
ExitPingChan chan bool
ExitJobChan chan bool
Logg *zap.Logger
LogR *lumberjack.Logger
SJob msg.StratumJob
//UpdateMap sync.Map
SyncJobChan chan bool
Synced bool
ExitDbMiners chan bool
ExitDbMinersStats chan bool
ExitDbUser chan bool
ExitDbUserStats chan bool
ExitDbPoolStats chan bool
NexaJob msg.NexaStratumJob
Sha3xJob msg.Sha3xStratumJob
MoneroJob msg.MoneroStratumJob
Tari_Sha3xJob msg.GbtSendMsg
ExitDiffVar chan bool
RedisClient *redis.Client
Accepts5M float64
Accepts15M float64
Accepts30M float64
Accepts1h float64
Accepts3h float64
Accepts6h float64
Accepts12h float64
Accepts24h float64
Accepts48h float64
Rejects5M float64
Rejects15M float64
Rejects30M float64
Rejects1h float64
Rejects3h float64
Rejects6h float64
Rejects12h float64
Rejects24h float64
Rejects48h float64
Mhs5M float64
Mhs15M float64
Mhs30M float64
Mhs1h float64
Mhs3h float64
Mhs6h float64
Mhs12h float64
Mhs24h float64
Mhs48h float64
RejectRatio5M float64
RejectRatio15M float64
RejectRatio30M float64
RejectRatio1h float64
RejectRatio3h float64
RejectRatio6h float64
RejectRatio12h float64
RejectRatio24h float64
RejectRatio48h float64
TotalMiners int64
Normal int64
Abnormal int64
Offline int64
MhsZero int64
MhsLow int64
HighRejects int64
Unstable int64
NetTarget string
NetHight uint64
Submits int64
Blocks int64
Orphans int64
Reward float64
Fee float64
/*Users sync.Map
UsersSLock sync.Mutex*/
/*PoolSLock sync.Mutex*/
SubIdx int64
MinerIndex int64
//NotifyBlkDetailIdx int32
CacheUsers sync.Map
CacheUsersCnt int32
CurrentConns int32
IpCounts map[string]int
IpMutex sync.Mutex
}
type JobListEntry struct {
Job_id string
Ts time.Time
}
type MhsItem struct {
Tt time.Time
Diff float64
}
type MhsObj struct {
MinerId string
Name string
Accepts []MhsItem
Rejects []MhsItem
StartSubmitTime time.Time
StartDayTime time.Time
User string
Miner string
Index string
Status string
Algo int
Release bool
LockForMhs sync.Mutex
}
type VarDiffOptions struct {
VariancePercent float64
AdjustTime float64
AdjustInterval float64
MinDiff float64
MaxDiff float64
MinShares float64
MaxShares float64
TargetShares float64
SubmitShares float64
SilenceCount float64
LastCalcTime time.Time
Uptimes int
Downtimes int
Level int
LastSubmitTime time.Time
}
type BlockMsg struct {
Target string
Submit_target string
Height int64
Success bool
Pow string
Net_target string
Submit string
Hash string
Header string
Accepts float64
Total_accepts float64
Rejects float64
Total_rejects float64
Reward float64
Fee float64
Nonce string
SubIdx int64
}
type MinerObj struct {
Server *ServerContext
Conn net.Conn
Authorized bool
MinerId string
JobId uint32
Name string
Jobs sync.Map
LockForJobs sync.Mutex
JobList *list.List
Target *big.Int
Difficulty float64
DifficultyNext float64
ServerDifficulty float64
ServerTarget *big.Int
ServerTargetS string
Accepts float64
Rejects float64
StartSubmitTime time.Time
LastSubmitime time.Time
SubmitIndex uint32
AverageHashrate float64
M5Accepts float64
M5Hashrate float64
M5SubmitTime time.Time
LastJobId string
LastNonce string
CurHeight uint32
CurHeight64 uint64
Reconnect bool
LastHeader string
VarDiffOpt VarDiffOptions
Version int32
Job msg.StratumJob
User string
PassWord string
Miner string
Session string
Duration float64
Status string
ConnSetupTime time.Time
TxLock sync.Mutex
KeepliveCnt float64
RecvedLiveAck bool
PongFailCnt int
PingCnt int
NexaJob msg.NexaStratumJob
Sha3xJob msg.Sha3xStratumJob
MoneroJob msg.MoneroStratumJob
Tari_Sha3xJob msg.GbtSendMsg
FromIP string
OnlineTime time.Time
OfflineTime time.Time
Retry int64
DurationTime float64
MinerIndex int64
Protocol string
ErrStaleds int64
ErrLowDiffs int64
ErrDuplicates int64
ErrFormats int64
ErrOthers int64
IsDisabled bool
Submits int64
Blocks int64
Orphans int64
Accepts5M float64
Accepts15M float64
Accepts30M float64
Accepts1h float64
Accepts3h float64
Accepts6h float64
Accepts12h float64
Accepts24h float64
Accepts48h float64
Rejects5M float64
Rejects15M float64
Rejects30M float64
Rejects1h float64
Rejects3h float64
Rejects6h float64
Rejects12h float64
Rejects24h float64
Rejects48h float64
Mhs5M float64
Mhs15M float64
Mhs30M float64
Mhs1h float64
Mhs3h float64
Mhs6h float64
Mhs12h float64
Mhs24h float64
Mhs48h float64
RejectRatio5M float64
RejectRatio15M float64
RejectRatio30M float64
RejectRatio1h float64
RejectRatio3h float64
RejectRatio6h float64
RejectRatio12h float64
RejectRatio24h float64
RejectRatio48h float64
Reward float64
Fee float64
Zlog zerolog.Logger
LogR *lumberjack.Logger
ZlogInit bool
//EndCh chan bool
DiffHandler diff.KalmanVarDiff
NeedExit int32
PingEnabled bool
}
/*type UserBlockMsg struct {
User string
Miner string
Index string
Height int64
Hash string
Pow string
Net_target string
Submit string
Success bool
Accepts float64
Rejects float64
Reward float64
Fee float64
Nonce string
SubIdx int64
}*/
/*
type UserMinerContainer struct {
Data map[string]string
}*/
/*
type UserObj struct {
Server *ServerContext
User string
Name string
Normal int64
Abnormal int64
Offline int64
MhsZero int64
MhsLow int64
HighRejects int64
Unstable int64
Submits int64
Blocks int64
Orphans int64
Reward float64
Fee float64
Accepts5M float64
Accepts15M float64
Accepts30M float64
Accepts1h float64
Accepts3h float64
Accepts6h float64
Accepts12h float64
Accepts24h float64
Accepts48h float64
Rejects5M float64
Rejects15M float64
Rejects30M float64
Rejects1h float64
Rejects3h float64
Rejects6h float64
Rejects12h float64
Rejects24h float64
Rejects48h float64
Mhs5M float64
Mhs15M float64
Mhs30M float64
Mhs1h float64
Mhs3h float64
Mhs6h float64
Mhs12h float64
Mhs24h float64
Mhs48h float64
RejectRatio5M float64
RejectRatio15M float64
RejectRatio30M float64
RejectRatio1h float64
RejectRatio3h float64
RejectRatio6h float64
RejectRatio12h float64
RejectRatio24h float64
RejectRatio48h float64
}*/
func md5md5(v string) string {
md5Obj := md5.New()
md5Obj.Write([]byte(v))
char := md5Obj.Sum(nil)
return hex.EncodeToString(char)
}
func Guid() string {
c := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, c); err != nil {
return ""
}
return md5md5(base64.URLEncoding.EncodeToString(c))
}
func VarAdjustDifficulty(miner *MinerObj, adjust int) {
if adjust != UP_DIFF && adjust != DOWN_DIFF && adjust != UPDATE_DIFF {
miner.Server.Logg.Error("[server]", zap.Int("Not support adjust ", adjust))
return
}
if adjust == UP_DIFF {
miner.DifficultyNext = miner.Difficulty
if miner.VarDiffOpt.Level == Mid {
miner.DifficultyNext *= math.Pow(2, 1)
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
} else if miner.VarDiffOpt.Level == Hign {
miner.DifficultyNext *= math.Pow(2, 2)
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
}
} else if adjust == DOWN_DIFF {
miner.DifficultyNext = miner.Difficulty
if miner.VarDiffOpt.Level == Mid {
miner.DifficultyNext /= math.Pow(2, 1)
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
} else if miner.VarDiffOpt.Level == Hign {
miner.DifficultyNext /= math.Pow(2, 2)
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
}
} else if adjust == UPDATE_DIFF {
if miner.VarDiffOpt.SubmitShares > 0 {
// re-target if outside bounds
if miner.VarDiffOpt.SubmitShares < miner.VarDiffOpt.MinShares || miner.VarDiffOpt.SubmitShares > miner.VarDiffOpt.MaxShares {
var change float64 = miner.VarDiffOpt.SubmitShares / miner.VarDiffOpt.TargetShares
miner.DifficultyNext = miner.Difficulty
miner.DifficultyNext *= change
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
}
miner.VarDiffOpt.SilenceCount = 0
} else {
// radical measures if there were no shares submitted
miner.VarDiffOpt.SilenceCount++
miner.DifficultyNext = miner.Difficulty / math.Pow(2, miner.VarDiffOpt.SilenceCount)
miner.DifficultyNext = math.Round(miner.DifficultyNext*1000) / 1000
}
}
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.TargetShares = miner.VarDiffOpt.AdjustTime / miner.VarDiffOpt.AdjustInterval * miner.DifficultyNext
miner.VarDiffOpt.MinShares = miner.VarDiffOpt.AdjustTime / miner.VarDiffOpt.AdjustInterval * miner.DifficultyNext * (1 - miner.VarDiffOpt.VariancePercent)
miner.VarDiffOpt.MaxShares = miner.VarDiffOpt.AdjustTime / miner.VarDiffOpt.AdjustInterval * miner.DifficultyNext * (1 + miner.VarDiffOpt.VariancePercent)
miner.VarDiffOpt.SubmitShares = 0
miner.VarDiffOpt.Uptimes = 0
miner.VarDiffOpt.Downtimes = 0
miner.Server.Logg.Info("[server]", zap.Float64("DifficultyNext", miner.DifficultyNext))
miner.Server.Logg.Info("[server]", zap.Float64("TargetShares", miner.VarDiffOpt.TargetShares), zap.Float64("MinShares", miner.VarDiffOpt.MinShares), zap.Float64("MaxShares", miner.VarDiffOpt.MaxShares))
now := time.Now()
share_interval := now.Sub(miner.LastSubmitime).Seconds()
New_diff_into_db(miner.User, miner.Miner, fmt.Sprint(miner.MinerIndex), miner.Difficulty, miner.DifficultyNext, miner.VarDiffOpt.SubmitShares, share_interval, miner.VarDiffOpt.MinShares, miner.VarDiffOpt.MaxShares)
}
var gdiff_db *sql.DB
func Init_diff_db() {
db, err := sql.Open("sqlite3", "./diffs.db")
if err != nil {
log.Printf("Error opening database: %v", err)
return
}
//defer db.Close()
gdiff_db = db
createTableSQL := `
CREATE TABLE IF NOT EXISTS diffs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts TEXT NOT NULL,
user TEXT NOT NULL,
miner TEXT NOT NULL,
minerid TEXT NOT NULL,
diff REAL NOT NULL,
next REAL NOT NULL,
kp REAL NOT NULL,
interval REAL NOT NULL,
mhs REAL NOT NULL,
mhs_est REAL NOT NULL
);`
_, err = gdiff_db.Exec(createTableSQL)
if err != nil {
log.Printf("Error creating table: %v", err)
return
}
}
func New_diff_into_db(user string, miner string, minerid string, diff float64, diff_next float64, kp float64, interval float64, mhs float64, mhs_est float64) {
if gdiff_db != nil {
insertSQL := `INSERT INTO diffs (ts, user, miner, minerid, diff, next, kp, interval, mhs, mhs_est) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
_, err := gdiff_db.Exec(insertSQL, time.Now().Format("2006-01-02 15:04:05"), user, miner, minerid, diff, diff_next, kp, interval, mhs, mhs_est)
if err != nil {
log.Printf("Error inserting data from diffs %s: %v", user+"."+miner+"_"+minerid, err)
return
}
}
}
func DiffStop() {
if gdiff_db != nil {
defer gdiff_db.Close()
}
}

1224
internal/server/dbif/dbif.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
// diff.go
package diff
import (
"fmt"
"math"
"strconv"
"strings"
)
type KalmanVarDiff struct {
Kf *KalmanFilter
StartDiff float64
MinDiff float64
MaxDiff float64
TargetInterval float64
MhsEst float64
DiffEst float64
}
type KalmanFilter struct {
X float64 // 估计的状态
P float64 // 状态协方差
F float64 // 状态转移矩阵
H float64 // 观测矩阵
Q float64 // 过程噪声协方差
R float64 // 观测噪声协方差
K float64 // 卡尔曼增益
}
func NewKalmanFilter(init_mhs float64, init_state_p float64) *KalmanFilter {
x := init_mhs
p := init_state_p
f := 1.0
h := 1.0
q := 0.01
r := init_mhs * 3
//r := 1.0
return &KalmanFilter{
X: x,
P: p,
F: f,
H: h,
Q: q,
R: r,
}
/*
return &KalmanFilter{
X: 1.0,
P: 1.0,
F: 1.0,
H: 1.0,
Q: 0.1,
R: 1.0,
}*/
}
func (kf *KalmanFilter) Update(measurement float64) (float64, float64) {
kf.R = measurement * 2
// 预测
p := kf.X*kf.Q + kf.P
// 计算卡尔曼增益
kf.K = p / (p + kf.R + 1)
// 更新状态估计
if measurement >= kf.X {
kf.X = kf.X + kf.K*(measurement-kf.X)
} else {
kf.X = kf.X - kf.K*(kf.X-measurement)
}
// 更新协方差矩阵
kf.P = (1 - kf.K) * p
// 自适应调整过程噪声和观测噪声
//kf.adapt()
return kf.X, kf.P
}
func (kf *KalmanFilter) adapt() {
// 自适应调整参数
if kf.P > 10.0 {
kf.Q *= 1.1 // 增加过程噪声
} else {
kf.Q *= 0.9 // 减少过程噪声
}
if kf.K > 0.5 {
kf.R *= 1.1 // 增加观测噪声
} else {
kf.R *= 0.9 // 减少观测噪声
}
}
func (kd *KalmanVarDiff) Init(startDiff float64, minDiff float64, maxDiff float64, targetTime float64) {
kd.StartDiff = startDiff
kd.MinDiff = minDiff
kd.MaxDiff = maxDiff
kd.TargetInterval = targetTime
kd.DiffEst = startDiff
kd.MhsEst = startDiff / targetTime
kd.Kf = NewKalmanFilter(startDiff/targetTime, 1.0)
}
func (kd *KalmanVarDiff) DeInit() {
}
// 提取科学计数法有效数字(整数部分和一位小数)及指数,并合并为新的浮点数
func extractAndCombine(num float64) float64 {
// 将浮点数格式化为科学计数法
scientificStr := fmt.Sprintf("%.10e", num)
// 分离小数部分和指数部分
parts := strings.Split(scientificStr, "e")
if len(parts) != 2 {
fmt.Println("Error: unexpected scientific notation format")
return 0
}
// 处理小数部分
decimalPart := parts[0]
exponentPart := parts[1]
// 去除小数部分前的 "0."
decimalPart = strings.TrimPrefix(decimalPart, "0.")
// 提取整数部分和一位小数
decimalParts := strings.Split(decimalPart, ".")
if len(decimalParts) < 2 {
decimalPart = decimalParts[0] + ".0" // 没有小数部分时,添加 ".0"
} else {
decimalPart = decimalParts[0] + "." + decimalParts[1][:1] // 只取一位小数
//decimalPart = decimalParts[0]
}
// 将指数部分转换为整数
exponent, err := strconv.Atoi(exponentPart)
if err != nil {
fmt.Println("Error parsing exponent:", err)
return 0
}
// 计算新的浮点数
newNumber := (func() float64 {
digit, err := strconv.ParseFloat(decimalPart, 64)
if err != nil {
fmt.Println("Error parsing decimal part:", err)
return 0
}
return digit * math.Pow(10, float64(exponent))
})()
return newNumber
}
func (kd *KalmanVarDiff) Handler(diff float64, interval float64) (float64, float64) {
//newx, newp := kd.Kf.Update(kd.DiffEst / interval)
newx, newp := kd.Kf.Update(diff / interval)
kd.MhsEst = newx
newdiff := newx * kd.TargetInterval
kd.DiffEst = newdiff
newdiff2 := extractAndCombine(newdiff)
return newdiff2, newp
}

View File

@@ -0,0 +1,14 @@
#ifndef NEXAAPI_H
#define NEXAAPI_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool nexa_hash( unsigned char *out, unsigned char *in);
bool nexa_hash12( unsigned char *out, unsigned char *in);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,288 @@
/*
Copyright (c) 2018-2019, tevador <tevador@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RANDOMX_H
#define RANDOMX_H
#include <stddef.h>
#include <stdint.h>
#define RANDOMX_HASH_SIZE 32
#define RANDOMX_DATASET_ITEM_SIZE 64
#ifndef RANDOMX_EXPORT
#define RANDOMX_EXPORT
#endif
typedef enum {
RANDOMX_FLAG_DEFAULT = 0,
RANDOMX_FLAG_LARGE_PAGES = 1,
RANDOMX_FLAG_HARD_AES = 2,
RANDOMX_FLAG_FULL_MEM = 4,
RANDOMX_FLAG_JIT = 8,
RANDOMX_FLAG_SECURE = 16,
RANDOMX_FLAG_ARGON2_SSSE3 = 32,
RANDOMX_FLAG_ARGON2_AVX2 = 64,
RANDOMX_FLAG_ARGON2 = 96
} randomx_flags;
typedef struct randomx_dataset randomx_dataset;
typedef struct randomx_cache randomx_cache;
typedef struct randomx_vm randomx_vm;
#if defined(__cplusplus)
#ifdef __cpp_constexpr
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif
inline CONSTEXPR randomx_flags operator |(randomx_flags a, randomx_flags b) {
return static_cast<randomx_flags>(static_cast<int>(a) | static_cast<int>(b));
}
inline CONSTEXPR randomx_flags operator &(randomx_flags a, randomx_flags b) {
return static_cast<randomx_flags>(static_cast<int>(a) & static_cast<int>(b));
}
inline randomx_flags& operator |=(randomx_flags& a, randomx_flags b) {
return a = a | b;
}
extern "C" {
#endif
/**
* @return The recommended flags to be used on the current machine.
* Does not include:
* RANDOMX_FLAG_LARGE_PAGES
* RANDOMX_FLAG_FULL_MEM
* RANDOMX_FLAG_SECURE
* These flags must be added manually if desired.
* On OpenBSD RANDOMX_FLAG_SECURE is enabled by default in JIT mode as W^X is enforced by the OS.
*/
RANDOMX_EXPORT randomx_flags randomx_get_flags(void);
/**
* Creates a randomx_cache structure and allocates memory for RandomX Cache.
*
* @param flags is any combination of these 2 flags (each flag can be set or not set):
* RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages
* RANDOMX_FLAG_JIT - create cache structure with JIT compilation support; this makes
* subsequent Dataset initialization faster
* Optionally, one of these two flags may be selected:
* RANDOMX_FLAG_ARGON2_SSSE3 - optimized Argon2 for CPUs with the SSSE3 instruction set
* makes subsequent cache initialization faster
* RANDOMX_FLAG_ARGON2_AVX2 - optimized Argon2 for CPUs with the AVX2 instruction set
* makes subsequent cache initialization faster
*
* @return Pointer to an allocated randomx_cache structure.
* Returns NULL if:
* (1) memory allocation fails
* (2) the RANDOMX_FLAG_JIT is set and JIT compilation is not supported on the current platform
* (3) an invalid or unsupported RANDOMX_FLAG_ARGON2 value is set
*/
RANDOMX_EXPORT randomx_cache *randomx_alloc_cache(randomx_flags flags);
/**
* Initializes the cache memory and SuperscalarHash using the provided key value.
* Does nothing if called again with the same key value.
*
* @param cache is a pointer to a previously allocated randomx_cache structure. Must not be NULL.
* @param key is a pointer to memory which contains the key value. Must not be NULL.
* @param keySize is the number of bytes of the key.
*/
RANDOMX_EXPORT void randomx_init_cache(randomx_cache *cache, const void *key, size_t keySize);
/**
* Returns a pointer to the internal memory buffer of the cache structure. The size
* of the internal memory buffer is RANDOMX_ARGON_MEMORY KiB.
*
* @param cache is a pointer to a previously allocated randomx_cache structure. Must not be NULL.
*
* @return Pointer to the internal memory buffer of the cache structure.
*/
RANDOMX_EXPORT void *randomx_get_cache_memory(randomx_cache *cache);
/**
* Releases all memory occupied by the randomx_cache structure.
*
* @param cache is a pointer to a previously allocated randomx_cache structure.
*/
RANDOMX_EXPORT void randomx_release_cache(randomx_cache* cache);
/**
* Creates a randomx_dataset structure and allocates memory for RandomX Dataset.
*
* @param flags is the initialization flags. Only one flag is supported (can be set or not set):
* RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages
*
* @return Pointer to an allocated randomx_dataset structure.
* NULL is returned if memory allocation fails.
*/
RANDOMX_EXPORT randomx_dataset *randomx_alloc_dataset(randomx_flags flags);
/**
* Gets the number of items contained in the dataset.
*
* @return the number of items contained in the dataset.
*/
RANDOMX_EXPORT unsigned long randomx_dataset_item_count(void);
/**
* Initializes dataset items.
*
* Note: In order to use the Dataset, all items from 0 to (randomx_dataset_item_count() - 1) must be initialized.
* This may be done by several calls to this function using non-overlapping item sequences.
*
* @param dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL.
* @param cache is a pointer to a previously allocated and initialized randomx_cache structure. Must not be NULL.
* @param startItem is the item number where initialization should start.
* @param itemCount is the number of items that should be initialized.
*/
RANDOMX_EXPORT void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount);
/**
* Returns a pointer to the internal memory buffer of the dataset structure. The size
* of the internal memory buffer is randomx_dataset_item_count() * RANDOMX_DATASET_ITEM_SIZE.
*
* @param dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL.
*
* @return Pointer to the internal memory buffer of the dataset structure.
*/
RANDOMX_EXPORT void *randomx_get_dataset_memory(randomx_dataset *dataset);
/**
* Releases all memory occupied by the randomx_dataset structure.
*
* @param dataset is a pointer to a previously allocated randomx_dataset structure.
*/
RANDOMX_EXPORT void randomx_release_dataset(randomx_dataset *dataset);
/**
* Creates and initializes a RandomX virtual machine.
*
* @param flags is any combination of these 5 flags (each flag can be set or not set):
* RANDOMX_FLAG_LARGE_PAGES - allocate scratchpad memory in large pages
* RANDOMX_FLAG_HARD_AES - virtual machine will use hardware accelerated AES
* RANDOMX_FLAG_FULL_MEM - virtual machine will use the full dataset
* RANDOMX_FLAG_JIT - virtual machine will use a JIT compiler
* RANDOMX_FLAG_SECURE - when combined with RANDOMX_FLAG_JIT, the JIT pages are never
* writable and executable at the same time (W^X policy)
* The numeric values of the first 4 flags are ordered so that a higher value will provide
* faster hash calculation and a lower numeric value will provide higher portability.
* Using RANDOMX_FLAG_DEFAULT (all flags not set) works on all platforms, but is the slowest.
* @param cache is a pointer to an initialized randomx_cache structure. Can be
* NULL if RANDOMX_FLAG_FULL_MEM is set.
* @param dataset is a pointer to a randomx_dataset structure. Can be NULL
* if RANDOMX_FLAG_FULL_MEM is not set.
*
* @return Pointer to an initialized randomx_vm structure.
* Returns NULL if:
* (1) Scratchpad memory allocation fails.
* (2) The requested initialization flags are not supported on the current platform.
* (3) cache parameter is NULL and RANDOMX_FLAG_FULL_MEM is not set
* (4) dataset parameter is NULL and RANDOMX_FLAG_FULL_MEM is set
*/
RANDOMX_EXPORT randomx_vm *randomx_create_vm(randomx_flags flags, randomx_cache *cache, randomx_dataset *dataset);
/**
* Reinitializes a virtual machine with a new Cache. This function should be called anytime
* the Cache is reinitialized with a new key. Does nothing if called with a Cache containing
* the same key value as already set.
*
* @param machine is a pointer to a randomx_vm structure that was initialized
* without RANDOMX_FLAG_FULL_MEM. Must not be NULL.
* @param cache is a pointer to an initialized randomx_cache structure. Must not be NULL.
*/
RANDOMX_EXPORT void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache);
/**
* Reinitializes a virtual machine with a new Dataset.
*
* @param machine is a pointer to a randomx_vm structure that was initialized
* with RANDOMX_FLAG_FULL_MEM. Must not be NULL.
* @param dataset is a pointer to an initialized randomx_dataset structure. Must not be NULL.
*/
RANDOMX_EXPORT void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset);
/**
* Releases all memory occupied by the randomx_vm structure.
*
* @param machine is a pointer to a previously created randomx_vm structure.
*/
RANDOMX_EXPORT void randomx_destroy_vm(randomx_vm *machine);
/**
* Calculates a RandomX hash value.
*
* @param machine is a pointer to a randomx_vm structure. Must not be NULL.
* @param input is a pointer to memory to be hashed. Must not be NULL.
* @param inputSize is the number of bytes to be hashed.
* @param output is a pointer to memory where the hash will be stored. Must not
* be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing.
*/
RANDOMX_EXPORT void randomx_calculate_hash(randomx_vm *machine, const void *input, size_t inputSize, void *output);
/**
* Set of functions used to calculate multiple RandomX hashes more efficiently.
* randomx_calculate_hash_first will begin a hash calculation.
* randomx_calculate_hash_next will output the hash value of the previous input
* and begin the calculation of the next hash.
* randomx_calculate_hash_last will output the hash value of the previous input.
*
* WARNING: These functions may alter the floating point rounding mode of the calling thread.
*
* @param machine is a pointer to a randomx_vm structure. Must not be NULL.
* @param input is a pointer to memory to be hashed. Must not be NULL.
* @param inputSize is the number of bytes to be hashed.
* @param nextInput is a pointer to memory to be hashed for the next hash. Must not be NULL.
* @param nextInputSize is the number of bytes to be hashed for the next hash.
* @param output is a pointer to memory where the hash will be stored. Must not
* be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing.
*/
RANDOMX_EXPORT void randomx_calculate_hash_first(randomx_vm* machine, const void* input, size_t inputSize);
RANDOMX_EXPORT void randomx_calculate_hash_next(randomx_vm* machine, const void* nextInput, size_t nextInputSize, void* output);
RANDOMX_EXPORT void randomx_calculate_hash_last(randomx_vm* machine, void* output);
/**
* Calculate a RandomX commitment from a RandomX hash and its input.
*
* @param input is a pointer to memory that was hashed. Must not be NULL.
* @param inputSize is the number of bytes in the input.
* @param hash_in is the output from randomx_calculate_hash* (RANDOMX_HASH_SIZE bytes).
* @param com_out is a pointer to memory where the commitment will be stored. Must not
* be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing.
*/
RANDOMX_EXPORT void randomx_calculate_commitment(const void* input, size_t inputSize, const void* hash_in, void* com_out);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,4 @@
#ifndef SHA3X_API_H
#define SHA3X_API_H
void sha3x_hash(unsigned char * out, unsigned char * in);
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,117 @@
package monero
/*
#cgo CFLAGS: -I/home/lizixuan/桌面/tari-server/internal/server/include
#cgo LDFLAGS: -L/home/lizixuan/桌面/tari-server/internal/server/lib/randomx -lrandomx
#include <randomx.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
*/
import "C"
import (
"fmt"
"unsafe"
"golang.org/x/sys/cpu"
)
type RandomXValidator struct {
cache *C.randomx_cache
vm *C.randomx_vm
flags C.randomx_flags
seed []byte
}
// NewRandomXValidator 初始化 cache + vm
func NewRandomXValidator(seed []byte) (*RandomXValidator, error) {
if len(seed) != 32 {
return nil, fmt.Errorf("seed must be 32 bytes")
}
var flags C.randomx_flags = 0
// 检测 AES-NI 支持
if cpu.X86.HasAES {
flags |= C.RANDOMX_FLAG_HARD_AES
} else {
fmt.Println("[RandomX] CPU 不支持 AES-NI将使用纯软件模式")
}
// ⚠️ 默认启用 JIT如果系统禁止会报错
flags |= C.RANDOMX_FLAG_JIT
// 分配 cache
cache := C.randomx_alloc_cache(flags)
if cache == nil {
return nil, fmt.Errorf("failed to alloc cache")
}
C.randomx_init_cache(cache, unsafe.Pointer(&seed[0]), C.size_t(len(seed)))
// 创建 vm
vm := C.randomx_create_vm(flags, cache, nil)
if vm == nil {
C.randomx_release_cache(cache)
return nil, fmt.Errorf("failed to create randomx vm")
}
return &RandomXValidator{
cache: cache,
vm: vm,
flags: flags,
seed: append([]byte{}, seed...),
}, nil
}
// SetSeed 更新 seed替换 cache并重置 vm
func (v *RandomXValidator) SetSeed(seed []byte) error {
if len(seed) != 32 {
return fmt.Errorf("seed must be 32 bytes")
}
// 如果相同 seed不用更新
if string(seed) == string(v.seed) {
return nil
}
C.randomx_init_cache(v.cache, unsafe.Pointer(&seed[0]), C.size_t(len(seed)))
C.randomx_vm_set_cache(v.vm, v.cache)
v.seed = append(v.seed[:0], seed...)
return nil
}
// Destroy 释放 vm + cache
func (v *RandomXValidator) Destroy() {
if v.vm != nil {
C.randomx_destroy_vm(v.vm)
v.vm = nil
}
if v.cache != nil {
C.randomx_release_cache(v.cache)
v.cache = nil
}
}
// BuildPowHash 计算区块哈希
func (v *RandomXValidator) BuildPowHash(blockBlob, nonce []byte) ([]byte, []byte, error) {
if v.vm == nil {
return nil, nil, fmt.Errorf("vm is nil")
}
if len(nonce) != 4 {
return nil, nil, fmt.Errorf("nonce must be 4 bytes")
}
blockHeader := make([]byte, len(blockBlob))
copy(blockHeader, blockBlob)
copy(blockHeader[39:43], nonce)
var hash [32]byte
C.randomx_calculate_hash(v.vm,
unsafe.Pointer(&blockHeader[0]),
C.size_t(len(blockHeader)),
unsafe.Pointer(&hash[0]),
)
return hash[:], blockHeader, nil
}

View File

@@ -0,0 +1,756 @@
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)
// Indexuint32 → 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
}

View File

@@ -0,0 +1,61 @@
// hash_nexa.go
package nexa
/*
#cgo CFLAGS : -I../include
#cgo LDFLAGS: -L../lib -lnexa
#include <stdio.h>
#include <stdlib.h>
#include "nexaapi.h"
*/
import "C"
import (
//"encoding/hex"
//"log"
"unsafe"
)
func BuildPowHash(h NexaBlockHeader) []byte {
outputs := make([]byte, 32)
inb := NexaBlockHeaderToBytes(h)
//log.Println("[nexa]in", hex.EncodeToString(inb))
in := (*C.uchar)(C.CBytes(inb))
output := (*C.uchar)(C.malloc(32))
C.nexa_hash(output, in)
p := uintptr(unsafe.Pointer(output))
for i := 0; i < 32; i++ {
j := *(*byte)(unsafe.Pointer(p))
outputs[i] = j
p += unsafe.Sizeof(j)
}
C.free(unsafe.Pointer(output))
C.free(unsafe.Pointer(in))
outputs32 := make([]byte, 32)
for i := 0; i < 32; i++ {
outputs32[i] = outputs[i]
}
return outputs32
}
func BuildPowHash12(h NexaBlockHeader12) []byte {
outputs := make([]byte, 32)
inb := NexaBlockHeaderToBytes12(h)
//log.Println("[nexa]in", hex.EncodeToString(inb))
in := (*C.uchar)(C.CBytes(inb))
output := (*C.uchar)(C.malloc(32))
C.nexa_hash12(output, in)
p := uintptr(unsafe.Pointer(output))
for i := 0; i < 32; i++ {
j := *(*byte)(unsafe.Pointer(p))
outputs[i] = j
p += unsafe.Sizeof(j)
}
C.free(unsafe.Pointer(output))
C.free(unsafe.Pointer(in))
outputs32 := make([]byte, 32)
for i := 0; i < 32; i++ {
outputs32[i] = outputs[i]
}
return outputs32
}

View File

@@ -0,0 +1,943 @@
// nexa.go
package nexa
import (
//"database/sql"
"encoding/binary"
"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_NEXA_VERSION string = "nexa v2.0x"
type NexaBlockHeader struct {
Header [32]byte
Nonce [16]byte
}
func NexaBlockHeaderToBytes(h NexaBlockHeader) []byte {
out := make([]byte, 49)
for i := 0; i < 32; i++ {
out[i] = h.Header[i]
}
out[32] = 0x10
for i := 0; i < 16; i++ {
out[33+i] = h.Nonce[i]
}
return out
}
type NexaBlockHeader12 struct {
Header [32]byte
Nonce [12]byte
}
func NexaBlockHeaderToBytes12(h NexaBlockHeader12) []byte {
out := make([]byte, 45)
for i := 0; i < 32; i++ {
out[i] = h.Header[i]
}
out[32] = 0x0c
for i := 0; i < 12; i++ {
out[33+i] = h.Nonce[i]
}
return out
}
type ServerNexaContext struct {
ServerCtx *coin.ServerContext
logg *zap.Logger
NexaJob msg.NexaStratumJob
}
var logg *zap.Logger
var ServerNexaCtx ServerNexaContext
type Notify_msg_nexa struct {
ID interface{} `json:"id"`
Method string `json:"method"`
Params [5]interface{} `json:"params"`
}
type Notify_msg_nexa_gpu struct {
Jsonrpc string `json:"jsonrpc"`
ID interface{} `json:"id"`
Method string `json:"method"`
Params [4]interface{} `json:"params"`
}
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
//logg.Warn("[server]", zap.String("user", miner.User), zap.String("miner", miner.Miner))
//logg.Debug("[server]", zap.Float64("id", id), zap.String("job_id", job_id))
//logg.Debug("[server]", zap.String("nonce2", nonce2), zap.String("ntime", ntime), zap.String("nonce", nonce))
//stratum.UpdateJobs(miner)
v, ok := miner.Jobs.Load(job_id)
if ok {
job := v.(msg.NexaStratumJob)
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
}
//logg.Debug("[server]", zap.Uint64("ntime", nt), zap.Uint64("mintime", uint64(job.Mintime)), zap.Uint64("jobtime", jt_reverse))
/*if nt < uint64(job.Mintime) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_OLD)
} else if nt > jt_reverse+uint64(600) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_NEW)
} else */{
if (miner.LastNonce != nonce) || (miner.LastHeader != job.Header) {
miner.LastHeader = job.Header
miner.LastNonce = nonce
job.Nonce = nonce
job.Extranonce2 = nonce2
//logg.Debug("[server]", zap.Uint32("height", job.Height), zap.String("target", job.Target))
if miner.ZlogInit {
miner.Zlog.Info().Msg("height " + string(job.Height) + " target " + job.Target + " " + miner.User + "." + miner.Miner)
}
phb, _ := hex.DecodeString(job.Header)
nb, _ := hex.DecodeString(nonce)
var calc_hash []byte
if miner.Protocol == "yxminer" {
var header NexaBlockHeader
for i := 0; i < 32; i++ {
header.Header[i] = phb[i]
}
for i := 0; i < 16; i++ {
header.Nonce[i] = nb[i]
}
submit_item.Header = hex.EncodeToString(NexaBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
var header NexaBlockHeader12
for i := 0; i < 32; i++ {
header.Header[i] = phb[i]
}
for i := 0; i < 12; i++ {
header.Nonce[i] = nb[i]
}
submit_item.Header = hex.EncodeToString(NexaBlockHeaderToBytes12(header))
calc_hash = BuildPowHash12(header)
} else {
var header NexaBlockHeader
for i := 0; i < 32; i++ {
header.Header[i] = phb[i]
}
for i := 0; i < 16; i++ {
header.Nonce[i] = nb[i]
}
submit_item.Header = hex.EncodeToString(NexaBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
}
//logg.Debug("[server]", zap.String("hash in", submit_item.Header))
//calc_hash, header := util.BuildBlockHash(&(job), true, Build_PowHash)
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
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(common.Reverse(calc_hash))
//hashs, _ := utility.ReverseS(hex.EncodeToString(calc_hash))
//hashb, _ := hex.DecodeString(hashs)
//submit_target.SetBytes(hashb)
submit_target.SetBytes(calc_hash)
/*logg.Debug("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
if submit_target.Cmp(miner.Target) > 0 {*/
//calc_diff := Target2Diff(common.Reverse(calc_hash))
calc_diff := utility.Target2Diff(calc_hash)
//log.Printf("diff,calc_diff:%f difficulty:%f ", calc_diff, miner.Difficulty)
//logg.Warn("[server]", zap.String("user", miner.User+"."+miner.Miner), zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff))
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)))
}
//logg.Debug("[server]", zap.String("target", miner.Target.String()), zap.Any("bytes", miner.Target.Bytes()))
//logg.Info("[server]", zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
//if calc_diff < miner.Difficulty {
if calc_diff < job.JobDifficulty {
//gpu protocol handler
/*for i := 0; i < 8; i++ {
temp_nonce := header.Nonce[8+i]
header.Nonce[8+i] = header.Nonce[i]
header.Nonce[i] = temp_nonce
}
submit_item.Header = hex.EncodeToString(NexaBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
logg.Debug("[server]", zap.String("hash in", hex.EncodeToString(NexaBlockHeaderToBytes(header))))*/
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
//submit_target = new(big.Int)
/*submit_target.SetBytes(calc_hash)
calc_diff = utility.Target2Diff(calc_hash)
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 < miner.Difficulty {
*/
ack.Result = false
miner.ErrLowDiffs = miner.ErrLowDiffs + 1
stratum.Handle_exception(miner, id, stratum.MINER_ERR_LOW_DIF_SHARE)
return false, false, false
//}
}
//logg.Warn("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.ServerTarget.Bytes())))
//submit_target.Text(16)
/*if submit_target.Cmp(miner.ServerTarget) <= 0 {*/
//log.Println("[server]server_target", miner.ServerTargetS)
//stb, _ := hex.DecodeString(miner.ServerTargetS)
stb, _ := hex.DecodeString(job.Target)
//logg.Info("[server]", zap.String("target", job.Target))
//server_diff := Target2Diff(common.Reverse(stb))
server_diff := utility.Target2Diff(utility.Reverse(stb))
//log.Printf("[server]server_diff %f", server_diff)
//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("ServerTargetS", miner.ServerTargetS))
network_target := new(big.Int)
network_target.SetBytes(stb)
//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)))
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())
/*user_blk_item.Height = int64(job.Height)
user_blk_item.Hash = hex.EncodeToString(calc_hash)
user_blk_item.Pow = hex.EncodeToString(calc_hash)
user_blk_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 = miner.Difficulty
blk_detail_miner_diff = job.JobDifficulty
blk_detail_pool_diff = miner.Server.RefDifficulty
if ack.Result == true {
/*if miner.CurHeight != 0 && miner.CurHeight == job.Height {
return
}*/
//if 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
//miner.CurHeight = job.Height
new_found = true
}
}
} else {
miner.LastHeader = job.Header
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()))
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)
}
//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 ratio >= 1.1 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else if ratio <= 0.8 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else {
}*/
}
}
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("Accepts", miner.Accepts), zap.Float64("Rejects", miner.Rejects))
//logg.Info("[server]", zap.Float64("TargetShares", miner.VarDiffOpt.TargetShares), zap.Float64("MinShares", miner.VarDiffOpt.MinShares), zap.Float64("MaxShares", miner.VarDiffOpt.MaxShares), zap.Float64("SubmitShares", miner.VarDiffOpt.SubmitShares))
//logg.Warn("[server]", zap.Float64("reject rate", miner.Rejects/(miner.Accepts+miner.Rejects)), zap.Float64("Hashrate(MH/S)", miner.AverageHashrate))
//logg.Warn("[server]", zap.Float64("M5Accepts", miner.M5Accepts), zap.Float64("M5Hashrate(MH/S)", miner.M5Hashrate))
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)))
}
//logg.Info("[server]", zap.Float64("LastCalcTime", float64(now.Sub(miner.VarDiffOpt.LastCalcTime))/1000000000))
//calc acutal submit shares period of time, then compare with target shares and adjust diff
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 {
//util.StaleAllJobs(miner)
/*user_blk_item.User = miner.User
user_blk_item.Miner = miner.Miner
user_blk_item.Index = fmt.Sprint(miner.MinerIndex)
user_blk_item.Submit = "y"
user_blk_item.Success = false
user_blk_item.Accepts = miner.Accepts
user_blk_item.Rejects = miner.Rejects
user_blk_item.Reward = 0
user_blk_item.Fee = 0
user_blk_item.Nonce = nonce
user_blk_item.SubIdx = miner.Server.SubIdx
dbif.NotifyUsersBlkStatsDb2(miner, &user_blk_item)*/
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.NotifyMinerDb2(miner, &submit_item)
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 Produce_block_submit(miner *coin.MinerObj /*header NexaBlockHeader,*/, job *msg.NexaStratumJob, PowHash string, SubIdx int64) {
var nm msg.BlockNexaMsg
nm.Id = job.Id
nm.Header = job.Header
nm.Nonce = job.Nonce
nm.Pow = PowHash
nm.SubIdx = SubIdx
nm.User = miner.User
nm.Miner = miner.Miner
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("blknexa"), []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"))
}
}
}
// server-->miner
func nexa_parse_miner_notify(miner *coin.MinerObj, msg msg.NexaStratumJob) int {
if miner.NexaJob.Height != msg.Height {
miner.Job.IsClean = true
}
miner.NexaJob = msg
miner.NexaJob.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) {
ServerNexaCtx.ServerCtx = server
logg = server.Logg
logg.Info("[server]", zap.String("server_nexa_version", SERVER_NEXA_VERSION))
coin.Init_diff_db()
}
func Start() {
}
func Stop() {
coin.DiffStop()
}
func InitMiner(miner *coin.MinerObj) {
miner.NexaJob = miner.Server.NexaJob
miner.NexaJob.Extranonce1 = miner.Job.Extranonce1
server_target := new(big.Int)
t_bytes, err := hex.DecodeString(miner.NexaJob.Target)
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
}
func Handle_subscribe_nexa(miner *coin.MinerObj, id float64, extranonce1 string) {
miner.TxLock.Lock()
var result [3]interface{}
//result[0] = results
result[0] = nil
if miner.Protocol == "yxminer" {
result[1] = extranonce1
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
var result2 [2]interface{}
var result3 [2]interface{}
var result4 [2]interface{}
result3[0] = "mining.set_difficulty"
if miner.Protocol == "WildRig" {
result3[1] = extranonce1[:8]
} else {
result3[1] = miner.Difficulty
}
result4[0] = "mining.notify"
//result4[1] = extranonce1
result4[1] = extranonce1[:8]
result2[0] = result3
result2[1] = result4
result[0] = result2
result[1] = extranonce1[:8]
//result[0] = fmt.Sprintf("[[%s,%.2f],[%s,%s]]", "mining.set_difficulty", miner.Difficulty, "mining.notify", extranonce1)
} else {
result[1] = extranonce1
}
//result[1] = miner.Job.Extranonce1
//miner.Server.Logg.Debug("[server]", zap.Uint64("extra2", miner.Job.Extranonce2_size))
if miner.Job.Extranonce2_size == 0 {
result[2] = 4
} else {
if miner.Protocol == "yxminer" {
result[2] = miner.Job.Extranonce2_size
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
result[2] = 4
} else {
result[2] = miner.Job.Extranonce2_size
}
}
if extranonce1 == "" {
miner.TxLock.Unlock()
stratum.Handle_exception(miner, id, stratum.MINER_ERR_NOT_SUBSCRIBED)
return
}
var body []byte
var err error
if miner.Protocol == "yxminer" {
var ack stratum.Subscribe_reply
ack.ID = id
ack.Result = result
ack.Error = nil
body, err = json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
var ack stratum.SubscribeGpu_reply
ack.Jsonrpc = "2.0"
ack.ID = id
ack.Result = result
ack.Error = nil
body, err = json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
} else {
var ack stratum.Subscribe_reply
ack.ID = id
ack.Result = result
ack.Error = nil
body, err = json.Marshal(ack)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
}
var body_string = string(body) + "\n"
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
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.Status = coin.MINER_STATUS_SUBSCRIBED
miner.TxLock.Unlock()
if miner.ZlogInit {
miner.Zlog.Info().Msg(body_string)
}
}
func HandleMinerSubscribe(miner *coin.MinerObj, id float64, extranonce1 string, msg string) {
if strings.Contains(msg, "YxMiner") {
miner.Protocol = "yxminer"
} else if strings.Contains(msg, "BzMiner") {
miner.Protocol = "bzminer"
} else if strings.Contains(msg, "lolMiner") {
miner.Protocol = "lolminer"
} else if strings.Contains(msg, "Rigel") {
miner.Protocol = "Rigel"
} else if strings.Contains(msg, "WildRig") {
miner.Protocol = "WildRig"
} else {
miner.Protocol = "standard"
}
Handle_subscribe_nexa(miner, id, extranonce1)
}
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) {
if miner.Protocol == "yxminer" {
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
nonce = nonce2 + nonce
} else {
}
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) {
if miner.Protocol == "yxminer" {
stratum.Set_difficulty(miner)
} else {
stratum.Set_difficulty_nexa(miner)
}
}
func NexaNotify(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
if miner.Protocol == "yxminer" {
stratum.Set_difficulty(miner)
} else {
stratum.Set_difficulty_nexa(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 params [5]interface{}
idb := make([]byte, 4)
binary.BigEndian.PutUint32(idb, miner.JobId)
miner.Job.Job_id = hex.EncodeToString(idb)
params[0] = miner.Job.Job_id
params[1] = miner.NexaJob.Header
//params[3] = miner.NexaJob.Height
if miner.Protocol == "yxminer" {
params[2] = miner.NexaJob.NBits
params[3] = miner.NexaJob.CurTime
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
params[2] = miner.NexaJob.Height
params[3] = miner.NexaJob.NBits
} else {
params[2] = miner.NexaJob.NBits
params[3] = miner.NexaJob.CurTime
}
miner.CurHeight = miner.NexaJob.Height
if miner.Reconnect {
params[4] = true
miner.Reconnect = false
} else {
params[4] = miner.Job.IsClean
}
params[4] = true
miner.NexaJob.JobDifficulty = miner.Difficulty
miner.Jobs.LoadOrStore(miner.Job.Job_id, miner.NexaJob)
/*var entry coin.JobListEntry
entry.Job_id = miner.Job.Job_id
entry.Ts = time.Now()
miner.LockForJobs.Lock()
miner.JobList.PushFront(entry)
var removes string = ""
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
e := miner.JobList.Back()
entry := e.Value.(coin.JobListEntry)
removes = entry.Job_id
miner.JobList.Remove(e)
}
miner.LockForJobs.Unlock()
if len(removes) > 0 {
miner.Jobs.Delete(removes)
}*/
stratum.AddAndUpdateJob(miner)
stratum.UpdateJobs(miner)
//miner.LastJobId = miner.Job.Job_id
miner.JobId++
var body []byte
var err error
if miner.Protocol == "yxminer" {
var msg Notify_msg_nexa
msg.ID = nil
msg.Method = "mining.notify"
msg.Params = params
body, err = json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
} else if miner.Protocol == "bzminer" || miner.Protocol == "lolminer" || miner.Protocol == "Rigel" || miner.Protocol == "WildRig" {
var msg Notify_msg_nexa_gpu
msg.ID = nil
msg.Method = "mining.notify"
var params4 [4]interface{}
params4[0] = params[0]
params4[1] = params[1]
params4[2] = params[2]
params4[3] = params[3]
msg.Params = params4
msg.Jsonrpc = "2.0"
body, err = json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
} else {
var msg Notify_msg_nexa
msg.ID = nil
msg.Method = "mining.notify"
msg.Params = params
body, err = json.Marshal(msg)
if err != nil {
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
miner.TxLock.Unlock()
return
}
}
var body_string = string(body) + "\n"
err = 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) {
NexaNotify(miner)
}
func HandleJobMsg(server *coin.ServerContext, Msg []byte) {
var result msg.NexaStratumJob
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.NexaJob = msg.NexaStratumJob(result)
logg.Debug("[gbt]", zap.String("Target", server.NexaJob.Target))
server.NexaJob.Extranonce2_size = 8
server.SJob.Extranonce2_size = 8
logg.Debug("[gbt]", zap.Uint32("Height", server.NexaJob.Height), zap.String("Target", server.NexaJob.Target), zap.String("Header", server.NexaJob.Header) /*, zap.Uint64("Timastamp", server.NexaJob.CurTime)*/)
targetb, _ := hex.DecodeString(server.NexaJob.Target)
logg.Debug("[gbt]", zap.Uint64("Id", server.NexaJob.Id), zap.Float64("network diff", utility.Target2Diff(utility.Reverse(targetb))))
server.NetHight = uint64(server.NexaJob.Height)
server.NetTarget = server.NexaJob.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 := nexa_parse_miner_notify(m, server.NexaJob)
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
NexaNotify(m)
}
}
}
}
}
return true
})
}
func IsMhsLow(miner *coin.MinerObj) bool {
if miner.Mhs5M < 1 {
return true
}
return false
}
func GetBlockInterval() int {
return 180
}

View File

@@ -0,0 +1,117 @@
package randomxT
/*
#cgo CFLAGS: -I/home/lizixuan/桌面/tari-server/internal/server/include
#cgo LDFLAGS: -L/home/lizixuan/桌面/tari-server/internal/server/lib/randomx -lrandomx
#include <randomx.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
*/
import "C"
import (
"fmt"
"unsafe"
"golang.org/x/sys/cpu"
)
type RandomXValidator struct {
cache *C.randomx_cache
vm *C.randomx_vm
flags C.randomx_flags
seed []byte
}
// NewRandomXValidator 初始化 cache + vm
func NewRandomXValidator(seed []byte) (*RandomXValidator, error) {
if len(seed) != 32 {
return nil, fmt.Errorf("seed must be 32 bytes")
}
var flags C.randomx_flags = 0
// 检测 AES-NI 支持
if cpu.X86.HasAES {
flags |= C.RANDOMX_FLAG_HARD_AES
} else {
fmt.Println("[RandomX] CPU 不支持 AES-NI将使用纯软件模式")
}
// ⚠️ 默认启用 JIT如果系统禁止会报错
flags |= C.RANDOMX_FLAG_JIT
// 分配 cache
cache := C.randomx_alloc_cache(flags)
if cache == nil {
return nil, fmt.Errorf("failed to alloc cache")
}
C.randomx_init_cache(cache, unsafe.Pointer(&seed[0]), C.size_t(len(seed)))
// 创建 vm
vm := C.randomx_create_vm(flags, cache, nil)
if vm == nil {
C.randomx_release_cache(cache)
return nil, fmt.Errorf("failed to create randomx vm")
}
return &RandomXValidator{
cache: cache,
vm: vm,
flags: flags,
seed: append([]byte{}, seed...),
}, nil
}
// SetSeed 更新 seed替换 cache并重置 vm
func (v *RandomXValidator) SetSeed(seed []byte) error {
if len(seed) != 32 {
return fmt.Errorf("seed must be 32 bytes")
}
// 如果相同 seed不用更新
if string(seed) == string(v.seed) {
return nil
}
C.randomx_init_cache(v.cache, unsafe.Pointer(&seed[0]), C.size_t(len(seed)))
C.randomx_vm_set_cache(v.vm, v.cache)
v.seed = append(v.seed[:0], seed...)
return nil
}
// Destroy 释放 vm + cache
func (v *RandomXValidator) Destroy() {
if v.vm != nil {
C.randomx_destroy_vm(v.vm)
v.vm = nil
}
if v.cache != nil {
C.randomx_release_cache(v.cache)
v.cache = nil
}
}
// BuildPowHash 计算区块哈希
func (v *RandomXValidator) BuildPowHash(blockBlob, nonce []byte) ([]byte, []byte, error) {
if v.vm == nil {
return nil, nil, fmt.Errorf("vm is nil")
}
if len(nonce) != 4 {
return nil, nil, fmt.Errorf("nonce must be 4 bytes")
}
blockHeader := make([]byte, len(blockBlob))
copy(blockHeader, blockBlob)
copy(blockHeader[39:43], nonce)
var hash [32]byte
C.randomx_calculate_hash(v.vm,
unsafe.Pointer(&blockHeader[0]),
C.size_t(len(blockHeader)),
unsafe.Pointer(&hash[0]),
)
return hash[:], blockHeader, nil
}

View File

@@ -0,0 +1,761 @@
// sha3x.go
package randomxT
import (
"strconv"
//"database/sql"
"encoding/binary"
"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_SHA3X_VERSION string = "sha3x v0.1a"
type Sha3xBlockHeader struct {
Nonce [8]byte
Header [32]byte
Algo byte
}
func Sha3xBlockHeaderToBytes(h Sha3xBlockHeader) []byte {
out := make([]byte, 8+32+1)
for i := 0; i < 8; i++ {
out[i] = h.Nonce[i]
}
for i := 0; i < 32; i++ {
out[8+i] = h.Header[i]
}
out[8+32] = h.Algo
return out
}
type ServerSha3xContext struct {
ServerCtx *coin.ServerContext
logg *zap.Logger
Sha3xJob msg.Sha3xStratumJob
}
var logg *zap.Logger
var ServerSha3xCtx ServerSha3xContext
type Sha3xNotify_params_msg struct {
Algo string `json:"algo"`
Blob string `json:"blob"`
Height uint32 `json:"height"`
Job_id string `json:"job_id"`
Target string `json:"target"`
}
type Sha3xNotify_msg struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params Sha3xNotify_params_msg `json:"params"`
}
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
//logg.Warn("[server]", zap.String("user", miner.User), zap.String("miner", miner.Miner))
//logg.Debug("[server]", zap.Float64("id", id), zap.String("job_id", job_id))
//logg.Debug("[server]", zap.String("nonce2", nonce2), zap.String("ntime", ntime), zap.String("nonce", nonce))
//stratum.UpdateJobs(miner)
v, ok := miner.Jobs.Load(job_id)
if ok {
job := v.(msg.Sha3xStratumJob)
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
}
//logg.Debug("[server]", zap.Uint64("ntime", nt), zap.Uint64("mintime", uint64(job.Mintime)), zap.Uint64("jobtime", jt_reverse))
/*if nt < uint64(job.Mintime) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_OLD)
} else if nt > jt_reverse+uint64(600) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_NEW)
} else */{
if (miner.LastNonce != nonce) || (miner.LastHeader != job.Header) {
miner.LastHeader = job.Header
miner.LastNonce = nonce
job.Nonce = nonce
job.Extranonce2 = nonce2
//logg.Debug("[server]", zap.Uint32("height", job.Height), zap.String("target", job.Target))
if miner.ZlogInit {
miner.Zlog.Info().Msg("height " + string(job.Height) + " target " + job.Target + " " + miner.User + "." + miner.Miner)
}
phb, _ := hex.DecodeString(job.Header)
nb, _ := hex.DecodeString(nonce)
var calc_hash []byte
var header Sha3xBlockHeader
for i := 0; i < 8; i++ {
header.Nonce[i] = nb[i]
}
for i := 0; i < 32; i++ {
header.Header[i] = phb[i]
}
header.Algo = 1
submit_item.Header = hex.EncodeToString(Sha3xBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
//logg.Debug("[server]", zap.String("hash in", submit_item.Header))
//calc_hash, header := util.BuildBlockHash(&(job), true, Build_PowHash)
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
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(common.Reverse(calc_hash))
//hashs, _ := utility.ReverseS(hex.EncodeToString(calc_hash))
//hashb, _ := hex.DecodeString(hashs)
//submit_target.SetBytes(hashb)
submit_target.SetBytes(calc_hash)
/*logg.Debug("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
if submit_target.Cmp(miner.Target) > 0 {*/
calc_diff := utility.Target2Diff(utility.Reverse(calc_hash))
//calc_diff := utility.Target2Diff(calc_hash)
//log.Printf("diff,calc_diff:%f difficulty:%f ", calc_diff, miner.Difficulty)
//logg.Warn("[server]", zap.String("user", miner.User+"."+miner.Miner), zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff))
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)))
}
//logg.Debug("[server]", zap.String("target", miner.Target.String()), zap.Any("bytes", miner.Target.Bytes()))
//logg.Info("[server]", zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
//if calc_diff < miner.Difficulty {
if calc_diff < job.JobDifficulty {
//gpu protocol handler
/*for i := 0; i < 8; i++ {
temp_nonce := header.Nonce[8+i]
header.Nonce[8+i] = header.Nonce[i]
header.Nonce[i] = temp_nonce
}
submit_item.Header = hex.EncodeToString(Sha3xBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
logg.Debug("[server]", zap.String("hash in", hex.EncodeToString(Sha3xBlockHeaderToBytes(header))))*/
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
//submit_target = new(big.Int)
/*submit_target.SetBytes(calc_hash)
calc_diff = utility.Target2Diff(calc_hash)
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 < miner.Difficulty {
*/
ack.Result = false
miner.ErrLowDiffs = miner.ErrLowDiffs + 1
stratum.Handle_exception(miner, id, stratum.MINER_ERR_LOW_DIF_SHARE)
return false, false, false
//}
}
//logg.Warn("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.ServerTarget.Bytes())))
//submit_target.Text(16)
/*if submit_target.Cmp(miner.ServerTarget) <= 0 {*/
//log.Println("[server]server_target", miner.ServerTargetS)
//stb, _ := hex.DecodeString(miner.ServerTargetS)
stb, _ := hex.DecodeString(job.Target)
//logg.Info("[server]", zap.String("target", job.Target))
//server_diff := Target2Diff(common.Reverse(stb))
server_diff := utility.Target2Diff(utility.Reverse(stb))
//log.Printf("[server]server_diff %f", server_diff)
//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("ServerTargetS", miner.ServerTargetS))
network_target := new(big.Int)
network_target.SetBytes(stb)
//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)))
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())
/*user_blk_item.Height = int64(job.Height)
user_blk_item.Hash = hex.EncodeToString(calc_hash)
user_blk_item.Pow = hex.EncodeToString(calc_hash)
user_blk_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 = miner.Difficulty
blk_detail_miner_diff = job.JobDifficulty
blk_detail_pool_diff = miner.Server.RefDifficulty
if ack.Result == true {
/*if miner.CurHeight != 0 && miner.CurHeight == job.Height {
return
}*/
//if true {
if (calc_diff >= server_diff) || (network_target.Cmp(submit_target) >= 0) {
//if true {
miner.Server.SubIdx++
Produce_block_submit(miner /*header,*/, &job, submit_item.Hash, miner.Server.SubIdx)
miner.SubmitIndex++
miner.Submits = miner.Submits + 1
//miner.CurHeight = job.Height
new_found = true
}
if new_found && float64(miner.Server.Sha3xJob.U64target) <= 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.Header
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()))
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)
}
//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 ratio >= 1.1 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else if ratio <= 0.8 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else {
}*/
}
}
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)
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
//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("Accepts", miner.Accepts), zap.Float64("Rejects", miner.Rejects))
//logg.Info("[server]", zap.Float64("TargetShares", miner.VarDiffOpt.TargetShares), zap.Float64("MinShares", miner.VarDiffOpt.MinShares), zap.Float64("MaxShares", miner.VarDiffOpt.MaxShares), zap.Float64("SubmitShares", miner.VarDiffOpt.SubmitShares))
//logg.Warn("[server]", zap.Float64("reject rate", miner.Rejects/(miner.Accepts+miner.Rejects)), zap.Float64("Hashrate(MH/S)", miner.AverageHashrate))
//logg.Warn("[server]", zap.Float64("M5Accepts", miner.M5Accepts), zap.Float64("M5Hashrate(MH/S)", miner.M5Hashrate))
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)))
}
//logg.Info("[server]", zap.Float64("LastCalcTime", float64(now.Sub(miner.VarDiffOpt.LastCalcTime))/1000000000))
//calc acutal submit shares period of time, then compare with target shares and adjust diff
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.NotifyMinerDb2(miner, &submit_item)
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 Produce_block_submit(miner *coin.MinerObj /*header Sha3xBlockHeader,*/, job *msg.Sha3xStratumJob, PowHash string, SubIdx int64) {
var nm msg.BlockSha3xMsg
nm.Id = job.Id
nm.Header = job.Header
//nm.Nonce = job.Nonce
noncer, _ := stratum.ReverseHexStringByByte(job.Nonce)
nonce, err := strconv.ParseUint(noncer, 16, 64)
nm.Nonce = nonce
nm.Pow = PowHash
nm.SubIdx = SubIdx
nm.User = miner.User
nm.Miner = miner.Miner
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)*/
nm.Height = (uint64)(job.Height)
logg.Warn("[server]", zap.Uint32("Height", job.Height))
//Add SubmitIndex
/*indexb := utility.Uint32ToByte(miner.SubmitIndex)
indexs := hex.EncodeToString(indexb)
blk += indexs
var SubmitIndex uint32 = utility.ByteToUint32(indexb)*/
nm.SubmitIdx = (uint64)(miner.SubmitIndex)
logg.Info("[server]", zap.Uint32("SubmitIndex", miner.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("blksha3x"), []byte(blk)}
err := miner.Server.PubCh.SendMessage([][]byte{[]byte("blksha3x"), []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"))
}
}
}
// server-->miner
func sha3x_parse_miner_notify(miner *coin.MinerObj, msg msg.Sha3xStratumJob) int {
if miner.Sha3xJob.Height != msg.Height {
miner.Job.IsClean = true
}
miner.Sha3xJob = msg
miner.Sha3xJob.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) {
ServerSha3xCtx.ServerCtx = server
logg = server.Logg
logg.Info("[server]", zap.String("server_sha3x_version", SERVER_SHA3X_VERSION))
coin.Init_diff_db()
}
func Start() {
}
func Stop() {
coin.DiffStop()
}
func InitMiner(miner *coin.MinerObj) {
miner.Sha3xJob = miner.Server.Sha3xJob
miner.Sha3xJob.Extranonce1 = miner.Job.Extranonce1
server_target := new(big.Int)
t_bytes, err := hex.DecodeString(miner.NexaJob.Target)
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
}
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 Sha3xNotify(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 Sha3xNotify_msg
idb := make([]byte, 4)
binary.BigEndian.PutUint32(idb, miner.JobId)
miner.Job.Job_id = hex.EncodeToString(idb)
msg.Params.Algo = "sha3x"
msg.Params.Job_id = miner.Job.Job_id
msg.Params.Blob = miner.Sha3xJob.Header
msg.Params.Height = miner.Sha3xJob.Height
//target_s, _ := stratum.ReverseHexStringByByte(miner.Sha3xJob.Target)
//msg.Params.Target = target_s[48:]
target_new, _ := utility.DiffToTarget(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 = miner.Sha3xJob.Height
miner.Sha3xJob.JobDifficulty = miner.Difficulty
miner.Jobs.LoadOrStore(miner.Job.Job_id, miner.Sha3xJob)
/*var entry coin.JobListEntry
entry.Job_id = miner.Job.Job_id
entry.Ts = time.Now()
miner.LockForJobs.Lock()
miner.JobList.PushFront(entry)
var removes string = ""
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
e := miner.JobList.Back()
entry := e.Value.(coin.JobListEntry)
removes = entry.Job_id
miner.JobList.Remove(e)
}
miner.LockForJobs.Unlock()
if len(removes) > 0 {
miner.Jobs.Delete(removes)
}*/
stratum.AddAndUpdateJob(miner)
stratum.UpdateJobs(miner)
//miner.LastJobId = miner.Job.Job_id
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.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) {
Sha3xNotify(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 HandleJobMsg(server *coin.ServerContext, Msg []byte) {
var result msg.Sha3xStratumJob
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 = formatUint64ToHexWithPadding(result.U64target)
//target_new, _ := utility.DiffToTarget((float64)(result.U64target))
//target_str := fmt.Sprintf("%064x", target_new.Bytes())
//result.Target = target_str
server.Sha3xJob = msg.Sha3xStratumJob(result)
logg.Debug("[gbt]", zap.String("Target", server.Sha3xJob.Target))
server.Sha3xJob.Extranonce2_size = 8
server.SJob.Extranonce2_size = 8
logg.Debug("[gbt]", zap.Uint32("Height", server.Sha3xJob.Height), zap.String("Target", server.Sha3xJob.Target), zap.String("Header", server.Sha3xJob.Header) /*, zap.Uint64("Timastamp", server.Sha3xJob.CurTime)*/)
targetb, _ := hex.DecodeString(server.Sha3xJob.Target)
logg.Debug("[gbt]", zap.Uint64("Id", server.Sha3xJob.Id), zap.Float64("network diff", utility.Target2Diff(utility.Reverse(targetb))))
server.NetHight = uint64(server.Sha3xJob.Height)
server.NetTarget = server.Sha3xJob.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 := sha3x_parse_miner_notify(m, server.Sha3xJob)
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
Sha3xNotify(m)
}
}
}
}
}
return true
})
}
func IsMhsLow(miner *coin.MinerObj) bool {
if miner.Mhs5M < 1 {
return true
}
return false
}
func GetBlockInterval() int {
return 3600
}

1865
internal/server/server.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
// hash_sha3x.go
package sha3x
/*
#cgo CFLAGS : -I../include
#cgo LDFLAGS: -L../lib -lsha3x
#include <stdio.h>
#include <stdlib.h>
#include "sha3xapi.h"
*/
import "C"
import (
//"encoding/hex"
//"log"
"unsafe"
)
func BuildPowHash(h Sha3xBlockHeader) []byte {
outputs := make([]byte, 32)
inb := Sha3xBlockHeaderToBytes(h)
//log.Println("[sha3x]in", hex.EncodeToString(inb))
in := (*C.uchar)(C.CBytes(inb))
output := (*C.uchar)(C.malloc(32))
C.sha3x_hash(output, in)
p := uintptr(unsafe.Pointer(output))
for i := 0; i < 32; i++ {
j := *(*byte)(unsafe.Pointer(p))
outputs[i] = j
p += unsafe.Sizeof(j)
}
C.free(unsafe.Pointer(output))
C.free(unsafe.Pointer(in))
outputs32 := make([]byte, 32)
for i := 0; i < 32; i++ {
outputs32[i] = outputs[i]
}
return outputs32
}

View File

@@ -0,0 +1,761 @@
// sha3x.go
package sha3x
import (
"strconv"
//"database/sql"
"encoding/binary"
"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_SHA3X_VERSION string = "sha3x v0.1a"
type Sha3xBlockHeader struct {
Nonce [8]byte
Header [32]byte
Algo byte
}
func Sha3xBlockHeaderToBytes(h Sha3xBlockHeader) []byte {
out := make([]byte, 8+32+1)
for i := 0; i < 8; i++ {
out[i] = h.Nonce[i]
}
for i := 0; i < 32; i++ {
out[8+i] = h.Header[i]
}
out[8+32] = h.Algo
return out
}
type ServerSha3xContext struct {
ServerCtx *coin.ServerContext
logg *zap.Logger
Sha3xJob msg.Sha3xStratumJob
}
var logg *zap.Logger
var ServerSha3xCtx ServerSha3xContext
type Sha3xNotify_params_msg struct {
Algo string `json:"algo"`
Blob string `json:"blob"`
Height uint32 `json:"height"`
Job_id string `json:"job_id"`
Target string `json:"target"`
}
type Sha3xNotify_msg struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params Sha3xNotify_params_msg `json:"params"`
}
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
//logg.Warn("[server]", zap.String("user", miner.User), zap.String("miner", miner.Miner))
//logg.Debug("[server]", zap.Float64("id", id), zap.String("job_id", job_id))
//logg.Debug("[server]", zap.String("nonce2", nonce2), zap.String("ntime", ntime), zap.String("nonce", nonce))
//stratum.UpdateJobs(miner)
v, ok := miner.Jobs.Load(job_id)
if ok {
job := v.(msg.Sha3xStratumJob)
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
}
//logg.Debug("[server]", zap.Uint64("ntime", nt), zap.Uint64("mintime", uint64(job.Mintime)), zap.Uint64("jobtime", jt_reverse))
/*if nt < uint64(job.Mintime) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_OLD)
} else if nt > jt_reverse+uint64(600) {
ack.Result = false
util.Handle_exception(miner, id, util.MINER_ERR_TIME_TOO_NEW)
} else */{
if (miner.LastNonce != nonce) || (miner.LastHeader != job.Header) {
miner.LastHeader = job.Header
miner.LastNonce = nonce
job.Nonce = nonce
job.Extranonce2 = nonce2
//logg.Debug("[server]", zap.Uint32("height", job.Height), zap.String("target", job.Target))
if miner.ZlogInit {
miner.Zlog.Info().Msg("height " + string(job.Height) + " target " + job.Target + " " + miner.User + "." + miner.Miner)
}
phb, _ := hex.DecodeString(job.Header)
nb, _ := hex.DecodeString(nonce)
var calc_hash []byte
var header Sha3xBlockHeader
for i := 0; i < 8; i++ {
header.Nonce[i] = nb[i]
}
for i := 0; i < 32; i++ {
header.Header[i] = phb[i]
}
header.Algo = 1
submit_item.Header = hex.EncodeToString(Sha3xBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
//logg.Debug("[server]", zap.String("hash in", submit_item.Header))
//calc_hash, header := util.BuildBlockHash(&(job), true, Build_PowHash)
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
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(common.Reverse(calc_hash))
//hashs, _ := utility.ReverseS(hex.EncodeToString(calc_hash))
//hashb, _ := hex.DecodeString(hashs)
//submit_target.SetBytes(hashb)
submit_target.SetBytes(calc_hash)
/*logg.Debug("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
if submit_target.Cmp(miner.Target) > 0 {*/
calc_diff := utility.Target2Diff(utility.Reverse(calc_hash))
//calc_diff := utility.Target2Diff(calc_hash)
//log.Printf("diff,calc_diff:%f difficulty:%f ", calc_diff, miner.Difficulty)
//logg.Warn("[server]", zap.String("user", miner.User+"."+miner.Miner), zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff))
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)))
}
//logg.Debug("[server]", zap.String("target", miner.Target.String()), zap.Any("bytes", miner.Target.Bytes()))
//logg.Info("[server]", zap.Float64("target diff", miner.Difficulty), zap.Float64("submit diff", calc_diff), zap.String("target", hex.EncodeToString(miner.Target.Bytes())))
//if calc_diff < miner.Difficulty {
if calc_diff < job.JobDifficulty {
//gpu protocol handler
/*for i := 0; i < 8; i++ {
temp_nonce := header.Nonce[8+i]
header.Nonce[8+i] = header.Nonce[i]
header.Nonce[i] = temp_nonce
}
submit_item.Header = hex.EncodeToString(Sha3xBlockHeaderToBytes(header))
calc_hash = BuildPowHash(header)
logg.Debug("[server]", zap.String("hash in", hex.EncodeToString(Sha3xBlockHeaderToBytes(header))))*/
//logg.Debug("[server]", zap.String("calc_hash", hex.EncodeToString(calc_hash)) /*, zap.String("merkle root", hex.EncodeToString(merkle_root))*/)
//submit_target = new(big.Int)
/*submit_target.SetBytes(calc_hash)
calc_diff = utility.Target2Diff(calc_hash)
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 < miner.Difficulty {
*/
ack.Result = false
miner.ErrLowDiffs = miner.ErrLowDiffs + 1
stratum.Handle_exception(miner, id, stratum.MINER_ERR_LOW_DIF_SHARE)
return false, false, false
//}
}
//logg.Warn("[server]", zap.String("pow", hex.EncodeToString(submit_target.Bytes())), zap.String("target", hex.EncodeToString(miner.ServerTarget.Bytes())))
//submit_target.Text(16)
/*if submit_target.Cmp(miner.ServerTarget) <= 0 {*/
//log.Println("[server]server_target", miner.ServerTargetS)
//stb, _ := hex.DecodeString(miner.ServerTargetS)
stb, _ := hex.DecodeString(job.Target)
//logg.Info("[server]", zap.String("target", job.Target))
//server_diff := Target2Diff(common.Reverse(stb))
server_diff := utility.Target2Diff(utility.Reverse(stb))
//log.Printf("[server]server_diff %f", server_diff)
//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("ServerTargetS", miner.ServerTargetS))
network_target := new(big.Int)
network_target.SetBytes(stb)
//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)))
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())
/*user_blk_item.Height = int64(job.Height)
user_blk_item.Hash = hex.EncodeToString(calc_hash)
user_blk_item.Pow = hex.EncodeToString(calc_hash)
user_blk_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 = miner.Difficulty
blk_detail_miner_diff = job.JobDifficulty
blk_detail_pool_diff = miner.Server.RefDifficulty
if ack.Result == true {
/*if miner.CurHeight != 0 && miner.CurHeight == job.Height {
return
}*/
//if true {
if (calc_diff >= server_diff) || (network_target.Cmp(submit_target) >= 0) {
//if true {
miner.Server.SubIdx++
Produce_block_submit(miner /*header,*/, &job, submit_item.Hash, miner.Server.SubIdx)
miner.SubmitIndex++
miner.Submits = miner.Submits + 1
//miner.CurHeight = job.Height
new_found = true
}
if new_found && float64(miner.Server.Sha3xJob.U64target) <= 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.Header
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()))
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)
}
//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 ratio >= 1.1 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else if ratio <= 0.8 {
miner.DifficultyNext = math.Ceil(diff_next*100) / 100
} else {
}*/
}
}
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)
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
//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("Accepts", miner.Accepts), zap.Float64("Rejects", miner.Rejects))
//logg.Info("[server]", zap.Float64("TargetShares", miner.VarDiffOpt.TargetShares), zap.Float64("MinShares", miner.VarDiffOpt.MinShares), zap.Float64("MaxShares", miner.VarDiffOpt.MaxShares), zap.Float64("SubmitShares", miner.VarDiffOpt.SubmitShares))
//logg.Warn("[server]", zap.Float64("reject rate", miner.Rejects/(miner.Accepts+miner.Rejects)), zap.Float64("Hashrate(MH/S)", miner.AverageHashrate))
//logg.Warn("[server]", zap.Float64("M5Accepts", miner.M5Accepts), zap.Float64("M5Hashrate(MH/S)", miner.M5Hashrate))
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)))
}
//logg.Info("[server]", zap.Float64("LastCalcTime", float64(now.Sub(miner.VarDiffOpt.LastCalcTime))/1000000000))
//calc acutal submit shares period of time, then compare with target shares and adjust diff
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.NotifyMinerDb2(miner, &submit_item)
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 Produce_block_submit(miner *coin.MinerObj /*header Sha3xBlockHeader,*/, job *msg.Sha3xStratumJob, PowHash string, SubIdx int64) {
var nm msg.BlockSha3xMsg
nm.Id = job.Id
nm.Header = job.Header
//nm.Nonce = job.Nonce
noncer, _ := stratum.ReverseHexStringByByte(job.Nonce)
nonce, err := strconv.ParseUint(noncer, 16, 64)
nm.Nonce = nonce
nm.Pow = PowHash
nm.SubIdx = SubIdx
nm.User = miner.User
nm.Miner = miner.Miner
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)*/
nm.Height = (uint64)(job.Height)
logg.Warn("[server]", zap.Uint32("Height", job.Height))
//Add SubmitIndex
/*indexb := utility.Uint32ToByte(miner.SubmitIndex)
indexs := hex.EncodeToString(indexb)
blk += indexs
var SubmitIndex uint32 = utility.ByteToUint32(indexb)*/
nm.SubmitIdx = (uint64)(miner.SubmitIndex)
logg.Info("[server]", zap.Uint32("SubmitIndex", miner.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("blksha3x"), []byte(blk)}
err := miner.Server.PubCh.SendMessage([][]byte{[]byte("blksha3x"), []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"))
}
}
}
// server-->miner
func sha3x_parse_miner_notify(miner *coin.MinerObj, msg msg.Sha3xStratumJob) int {
if miner.Sha3xJob.Height != msg.Height {
miner.Job.IsClean = true
}
miner.Sha3xJob = msg
miner.Sha3xJob.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) {
ServerSha3xCtx.ServerCtx = server
logg = server.Logg
logg.Info("[server]", zap.String("server_sha3x_version", SERVER_SHA3X_VERSION))
coin.Init_diff_db()
}
func Start() {
}
func Stop() {
coin.DiffStop()
}
func InitMiner(miner *coin.MinerObj) {
miner.Sha3xJob = miner.Server.Sha3xJob
miner.Sha3xJob.Extranonce1 = miner.Job.Extranonce1
server_target := new(big.Int)
t_bytes, err := hex.DecodeString(miner.NexaJob.Target)
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
}
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 Sha3xNotify(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 Sha3xNotify_msg
idb := make([]byte, 4)
binary.BigEndian.PutUint32(idb, miner.JobId)
miner.Job.Job_id = hex.EncodeToString(idb)
msg.Params.Algo = "sha3x"
msg.Params.Job_id = miner.Job.Job_id
msg.Params.Blob = miner.Sha3xJob.Header
msg.Params.Height = miner.Sha3xJob.Height
//target_s, _ := stratum.ReverseHexStringByByte(miner.Sha3xJob.Target)
//msg.Params.Target = target_s[48:]
target_new, _ := utility.DiffToTarget(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 = miner.Sha3xJob.Height
miner.Sha3xJob.JobDifficulty = miner.Difficulty
miner.Jobs.LoadOrStore(miner.Job.Job_id, miner.Sha3xJob)
/*var entry coin.JobListEntry
entry.Job_id = miner.Job.Job_id
entry.Ts = time.Now()
miner.LockForJobs.Lock()
miner.JobList.PushFront(entry)
var removes string = ""
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
e := miner.JobList.Back()
entry := e.Value.(coin.JobListEntry)
removes = entry.Job_id
miner.JobList.Remove(e)
}
miner.LockForJobs.Unlock()
if len(removes) > 0 {
miner.Jobs.Delete(removes)
}*/
stratum.AddAndUpdateJob(miner)
stratum.UpdateJobs(miner)
//miner.LastJobId = miner.Job.Job_id
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.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) {
Sha3xNotify(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 HandleJobMsg(server *coin.ServerContext, Msg []byte) {
var result msg.Sha3xStratumJob
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 = formatUint64ToHexWithPadding(result.U64target)
//target_new, _ := utility.DiffToTarget((float64)(result.U64target))
//target_str := fmt.Sprintf("%064x", target_new.Bytes())
//result.Target = target_str
server.Sha3xJob = msg.Sha3xStratumJob(result)
logg.Debug("[gbt]", zap.String("Target", server.Sha3xJob.Target))
server.Sha3xJob.Extranonce2_size = 8
server.SJob.Extranonce2_size = 8
logg.Debug("[gbt]", zap.Uint32("Height", server.Sha3xJob.Height), zap.String("Target", server.Sha3xJob.Target), zap.String("Header", server.Sha3xJob.Header) /*, zap.Uint64("Timastamp", server.Sha3xJob.CurTime)*/)
targetb, _ := hex.DecodeString(server.Sha3xJob.Target)
logg.Debug("[gbt]", zap.Uint64("Id", server.Sha3xJob.Id), zap.Float64("network diff", utility.Target2Diff(utility.Reverse(targetb))))
server.NetHight = uint64(server.Sha3xJob.Height)
server.NetTarget = server.Sha3xJob.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 := sha3x_parse_miner_notify(m, server.Sha3xJob)
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
Sha3xNotify(m)
}
}
}
}
}
return true
})
}
func IsMhsLow(miner *coin.MinerObj) bool {
if miner.Mhs5M < 1 {
return true
}
return false
}
func GetBlockInterval() int {
return 3600
}