create git
This commit is contained in:
385
internal/gbt/tari/sha3x/sha3x.go
Normal file
385
internal/gbt/tari/sha3x/sha3x.go
Normal file
@@ -0,0 +1,385 @@
|
||||
package sha3x
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"pool/internal/db"
|
||||
"pool/internal/gbt/coin"
|
||||
"pool/internal/gbt/dbif"
|
||||
"pool/internal/gbt/tari"
|
||||
"pool/internal/msg"
|
||||
"pool/internal/utility"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const GBT_SHA3X_VERSION string = "sha3x v1.0"
|
||||
|
||||
type Sha3xAddrConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
type Sha3xConfig struct {
|
||||
Monero Sha3xAddrConfig `json:"sha3x"`
|
||||
}
|
||||
|
||||
type GbtSha3xContext struct {
|
||||
Config Sha3xConfig
|
||||
GbtCtx *coin.GbtContext
|
||||
Ctx context.Context
|
||||
last_time time.Time
|
||||
last_gbt tari.TariBlockTemplate
|
||||
last_blockhash string
|
||||
|
||||
last_height uint64
|
||||
|
||||
Submits float64
|
||||
|
||||
addressIndex int
|
||||
|
||||
Target []byte
|
||||
Header []byte
|
||||
last_body string
|
||||
|
||||
Jobs sync.Map
|
||||
JobIds []string
|
||||
JobGenCount int
|
||||
new_block_chan chan int
|
||||
new_block_index int
|
||||
}
|
||||
|
||||
var logg *zap.Logger
|
||||
var GbtSha3xCtx GbtSha3xContext
|
||||
|
||||
func configInit(config *Sha3xConfig) {
|
||||
data, err := ioutil.ReadFile("gbt.conf")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
if err = json.Unmarshal(data, &config); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Init(GbtCtx *coin.GbtContext, DbCtx *db.DbContext) {
|
||||
GbtSha3xCtx.GbtCtx = GbtCtx
|
||||
|
||||
GbtSha3xCtx.last_height = 0
|
||||
|
||||
configInit(&GbtSha3xCtx.Config)
|
||||
GbtSha3xCtx.JobGenCount = 0
|
||||
GbtSha3xCtx.Target = make([]byte, 32)
|
||||
GbtSha3xCtx.Header = make([]byte, 49)
|
||||
GbtSha3xCtx.last_time = time.Now()
|
||||
GbtSha3xCtx.Ctx = context.Background()
|
||||
logg = GbtCtx.Log
|
||||
GbtSha3xCtx.new_block_chan = make(chan int, 256)
|
||||
GbtSha3xCtx.new_block_index = 0
|
||||
logg.Info("[gbt]", zap.String("gbt_sha3x_version", GBT_SHA3X_VERSION))
|
||||
|
||||
}
|
||||
|
||||
func Start() {
|
||||
go gbt_running(&GbtSha3xCtx)
|
||||
go gbt_notify_running(&GbtSha3xCtx)
|
||||
go submit_block_running(&GbtSha3xCtx)
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
defer close(GbtSha3xCtx.new_block_chan)
|
||||
}
|
||||
|
||||
func update_block_confirm(gbt *GbtSha3xContext) {
|
||||
|
||||
}
|
||||
|
||||
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 removeJobs(gbt *GbtSha3xContext, clean bool) {
|
||||
if !clean {
|
||||
if len(gbt.JobIds) > 10 {
|
||||
end := len(gbt.JobIds) - 10
|
||||
for i := 0; i < end; i++ {
|
||||
gbt.Jobs.Delete(gbt.JobIds[i])
|
||||
}
|
||||
gbt.JobIds = gbt.JobIds[end:]
|
||||
}
|
||||
} else {
|
||||
gbt.Jobs.Range(func(key, value interface{}) bool {
|
||||
gbt.Jobs.Delete(key)
|
||||
return true
|
||||
})
|
||||
gbt.JobIds = nil
|
||||
gbt.JobGenCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
func gbt_running(gbt *GbtSha3xContext) {
|
||||
ticker := time.NewTicker(1000 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for gbt.GbtCtx.Started {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// 获取区块模板
|
||||
blockTemplate, err := gbt.GbtCtx.TariClient.GetBlockTemplate(gbt.Ctx, 1)
|
||||
if err != nil || len(blockTemplate.Header) != 638 {
|
||||
fmt.Println("任务模板中的区块头长度:", len(blockTemplate.Header))
|
||||
return
|
||||
}
|
||||
|
||||
// 初始化 ZMQ 发布通道
|
||||
if gbt.GbtCtx.PubCh == nil {
|
||||
gbt.GbtCtx.PubCh = utility.InitZmqPub(gbt.GbtCtx.Config.Zmq.Pub)
|
||||
continue
|
||||
}
|
||||
|
||||
height := blockTemplate.Height
|
||||
|
||||
if height == gbt.last_height {
|
||||
removeJobs(gbt, false)
|
||||
if gbt.JobGenCount >= 10 {
|
||||
generateJob(gbt, &blockTemplate)
|
||||
gbt.JobGenCount = 0 // 成功生成 Job 后重置计数器
|
||||
}
|
||||
} else {
|
||||
removeJobs(gbt, true)
|
||||
generateJob(gbt, &blockTemplate)
|
||||
gbt.last_height = height
|
||||
gbt.JobGenCount = 0 // 高度变化也重置计数器
|
||||
}
|
||||
// 标记存活
|
||||
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
|
||||
// 无论是否需要发布新任务,计数均+1,保持至少10秒更新一次任务
|
||||
gbt.JobGenCount += 1
|
||||
case blkIdx := <-gbt.new_block_chan:
|
||||
log.Println("new block chan", blkIdx)
|
||||
update_block_confirm(gbt)
|
||||
|
||||
case <-gbt.GbtCtx.ExitGbtChan:
|
||||
logg.Error("[gbt]", zap.String("gbt", "exit"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateJob(gbt *GbtSha3xContext, blockTemplate *tari.TariBlockTemplate) {
|
||||
for trycnt := 0; trycnt < 3; trycnt++ {
|
||||
job := msg.Sha3xJob{
|
||||
JobId: randomxJobId(),
|
||||
MiningHash: blockTemplate.MiningHash,
|
||||
Header: blockTemplate.Header,
|
||||
Body: blockTemplate.Body,
|
||||
TargetDifficulty: blockTemplate.TargetDifficulty,
|
||||
Height: blockTemplate.Height,
|
||||
}
|
||||
|
||||
sendMsg := msg.Sha3xStratumJob{
|
||||
JobId: job.JobId,
|
||||
Header: job.MiningHash,
|
||||
U64target: job.TargetDifficulty,
|
||||
Height: uint32(job.Height),
|
||||
}
|
||||
|
||||
bt, err := json.Marshal(sendMsg)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 保存 Job
|
||||
gbt.Jobs.LoadOrStore(job.JobId, job)
|
||||
gbt.JobIds = append(gbt.JobIds, job.JobId)
|
||||
|
||||
// 发布 Job
|
||||
if err := gbt.GbtCtx.PubCh.SendMessage([][]byte{[]byte("jobsha3x"), bt}); err != nil {
|
||||
logg.Warn("[gbt]", zap.String("job", err.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
logg.Warn("[gbt]", zap.String("job", "sent"))
|
||||
gbt.JobGenCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func gbt_notify_running(gbt *GbtSha3xContext) {
|
||||
for {
|
||||
|
||||
if !gbt.GbtCtx.Started {
|
||||
break
|
||||
}
|
||||
if gbt.GbtCtx.MoneroNewBlockSubCh == nil {
|
||||
gbt.GbtCtx.MoneroNewBlockSubCh = utility.InitZmqSub(gbt.GbtCtx.Config.Rpc.ZmqSub, utility.BITCOIND_ZMQ_HASHBLOCK)
|
||||
}
|
||||
if gbt.GbtCtx.MoneroNewBlockSubCh != nil {
|
||||
cmsg_sub, err := gbt.GbtCtx.MoneroNewBlockSubCh.RecvMessage()
|
||||
if err != nil {
|
||||
if !gbt.GbtCtx.Started {
|
||||
break
|
||||
}
|
||||
gbt.GbtCtx.MoneroNewBlockSubCh.SetSubscribe(utility.BITCOIND_ZMQ_HASHBLOCK)
|
||||
gbt.GbtCtx.MoneroNewBlockSubCh.Connect(gbt.GbtCtx.Config.Rpc.ZmqSub)
|
||||
continue
|
||||
}
|
||||
if len(cmsg_sub) >= 2 {
|
||||
if string(cmsg_sub[0]) == "hashblock" {
|
||||
GbtSha3xCtx.new_block_index = GbtSha3xCtx.new_block_index + 1
|
||||
//log.Println("gbt_notify_running", hex.EncodeToString(cmsg_sub[1]), GbtNexaCtx.new_block_index)
|
||||
gbt.new_block_chan <- GbtSha3xCtx.new_block_index
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logg.Error("[gbt]", zap.String("notify", "NodeSubCh fail!"))
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ServerSubmitBlock struct {
|
||||
JobId string
|
||||
Header []byte
|
||||
Body []byte
|
||||
Nonce string
|
||||
}
|
||||
|
||||
func submit_block_running(block *GbtSha3xContext) {
|
||||
logg.Info("[block]", zap.String("submit_block_running", "Start."))
|
||||
for {
|
||||
if !block.GbtCtx.Started {
|
||||
break
|
||||
}
|
||||
if block.GbtCtx.SubCh == nil {
|
||||
block.GbtCtx.SubCh = utility.InitZmqSub(block.GbtCtx.Config.Zmq.Sub, "blk"+block.GbtCtx.Coin)
|
||||
}
|
||||
if block.GbtCtx.SubCh != nil {
|
||||
cmsg_sub, err := block.GbtCtx.SubCh.RecvMessage()
|
||||
if err != nil {
|
||||
if !block.GbtCtx.Started {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
block.GbtCtx.SubCh.SetSubscribe("blk" + block.GbtCtx.Coin)
|
||||
block.GbtCtx.SubCh.Connect(block.GbtCtx.Config.Zmq.Sub)
|
||||
continue
|
||||
}
|
||||
if len(cmsg_sub) >= 2 {
|
||||
if string(cmsg_sub[0]) == "blksha3x" {
|
||||
cmsg := cmsg_sub[1]
|
||||
//block data
|
||||
msgb := make([]byte, len(cmsg)-16)
|
||||
copy(msgb, cmsg)
|
||||
var submitMsg msg.BlockSha3xMsg
|
||||
if err := json.Unmarshal(msgb, &submitMsg); err != nil {
|
||||
//block.Consumer.MarkOffset(cmsg, "")
|
||||
logg.Error("[block]", zap.String("failed to Unmarshal job", err.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
var height = submitMsg.Height
|
||||
logg.Warn("[block]", zap.Uint64("height", height))
|
||||
|
||||
if height <= block.last_height {
|
||||
continue
|
||||
}
|
||||
block.last_height = height
|
||||
|
||||
indexb, err1 := hex.DecodeString(string(cmsg[len(msgb)+8:]))
|
||||
if err1 != nil {
|
||||
logg.Error("[block]", zap.String("failed to decode index", err1.Error()))
|
||||
continue
|
||||
}
|
||||
var index uint32 = utility.ByteToUint32(indexb)
|
||||
logg.Warn("[block]", zap.Uint32("index", index))
|
||||
logg.Debug("[block]", zap.String("msg", string(cmsg)), zap.String("blk", string(msgb)))
|
||||
if v, ok := block.Jobs.Load(submitMsg.Id); ok {
|
||||
job := v.(*msg.Sha3xJob) // 类型断言
|
||||
headerStr, bodyStr := job.Header, job.Body
|
||||
header, err := hex.DecodeString(headerStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
body, err := hex.DecodeString(bodyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
nonceByte := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(nonceByte, submitMsg.Nonce)
|
||||
copy(header[311:319], nonceByte)
|
||||
blockHash_byte, _ := block.GbtCtx.TariClient.SubmitBlock(block.Ctx, header, body)
|
||||
nonceStr := fmt.Sprintf("%x", submitMsg.Nonce)
|
||||
blockHash := hex.EncodeToString(blockHash_byte)
|
||||
dbif.NotifyPoolBlkStatsSubmitResult(block.GbtCtx, int64(height), blockHash, "OK", nonceStr, submitMsg.SubIdx)
|
||||
block.Submits += 1
|
||||
logg.Warn("[block]", zap.Float64("total submits", block.Submits), zap.Int64("SubIdx", submitMsg.SubIdx))
|
||||
new_block_into_db(block, submitMsg.User, submitMsg.Miner, submitMsg.Index, int64(height), nonceStr, blockHash, submitMsg.SubIdx)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logg.Error("[block]", zap.String("block", "SubCh failed! retry"))
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func new_block_into_db(block *GbtSha3xContext, user string, miner string, minerid string, height int64, nonce string, hash string, subidx int64) bool {
|
||||
db, err := sql.Open("sqlite3", "./blocks.db")
|
||||
if err != nil {
|
||||
log.Printf("Error opening database: %v", err)
|
||||
return false
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
createTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS blocks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user TEXT NOT NULL,
|
||||
miner TEXT NOT NULL,
|
||||
minerid TEXT NOT NULL,
|
||||
height INTEGER,
|
||||
nonce TEXT NOT NULL,
|
||||
hash TEXT NOT NULL,
|
||||
subidx INTEGER,
|
||||
checked INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);`
|
||||
_, err = db.Exec(createTableSQL)
|
||||
if err != nil {
|
||||
log.Printf("Error creating table: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
insertSQL := `INSERT INTO blocks (user, miner, minerid, height, nonce, checked, hash, subidx) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
_, err = db.Exec(insertSQL, user, miner, minerid, height, nonce, 0, hash, subidx)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting data from blocks %s: %v", fmt.Sprint(height), err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user