868 lines
25 KiB
Go
868 lines
25 KiB
Go
|
// dgb.go
|
||
|
package dgb
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"math"
|
||
|
"sync/atomic"
|
||
|
|
||
|
//"pool/internal/cache"
|
||
|
"pool/internal/db"
|
||
|
"pool/internal/gbt/coin"
|
||
|
"pool/internal/gbt/dbif"
|
||
|
"pool/internal/msg"
|
||
|
"pool/internal/utility"
|
||
|
"time"
|
||
|
|
||
|
"database/sql"
|
||
|
|
||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||
|
"github.com/btcsuite/btcutil/base58"
|
||
|
|
||
|
//"github.com/btcsuite/btcd/rpcclient"
|
||
|
_ "github.com/mattn/go-sqlite3"
|
||
|
|
||
|
"go.uber.org/zap"
|
||
|
)
|
||
|
|
||
|
const GBT_DGB_VERSION string = "dgb v3.0l"
|
||
|
|
||
|
type DgbAddrConfig struct {
|
||
|
Addr string `json:"addr"`
|
||
|
}
|
||
|
|
||
|
type DgbConfig struct {
|
||
|
Dgb DgbAddrConfig `json:"dgb"`
|
||
|
}
|
||
|
|
||
|
type GbtDgbContext struct {
|
||
|
Config DgbConfig
|
||
|
GbtCtx *coin.GbtContext
|
||
|
|
||
|
last_time time.Time
|
||
|
last_gbt msg.GbtMsg
|
||
|
|
||
|
Submits float64
|
||
|
|
||
|
addressIndex int
|
||
|
|
||
|
new_block_chan chan int
|
||
|
new_block_index int
|
||
|
|
||
|
last_height uint32
|
||
|
}
|
||
|
|
||
|
var logg *zap.Logger
|
||
|
var GbtDgbCtx GbtDgbContext
|
||
|
|
||
|
type BlockCheckData struct {
|
||
|
Height int
|
||
|
Nonce string
|
||
|
User string
|
||
|
Miner string
|
||
|
MinerId string
|
||
|
Hash string
|
||
|
SubIdx int
|
||
|
}
|
||
|
|
||
|
type PushBlkNewMsg struct {
|
||
|
Coin string `json:"coin"`
|
||
|
Height int `json:"height"`
|
||
|
Nonce string `json:"nonce"`
|
||
|
}
|
||
|
|
||
|
func update_block_confirm(gbt *GbtDgbContext) {
|
||
|
db, err := sql.Open("sqlite3", "./blocks.db")
|
||
|
if err != nil {
|
||
|
//log.Printf("Error opening database: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Error opening database", err.Error()))
|
||
|
return
|
||
|
}
|
||
|
defer db.Close()
|
||
|
|
||
|
query := "SELECT user,miner,minerid,height,nonce,hash,subidx FROM blocks WHERE checked=0 AND created_at >= datetime('now', '-30 minutes') order by id desc limit 2"
|
||
|
rows, err := db.Query(query)
|
||
|
if err != nil {
|
||
|
//log.Printf("Error executing query from blocks: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Error executing query from blocks:", err.Error()))
|
||
|
return
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
|
||
|
var blocks []BlockCheckData
|
||
|
for rows.Next() {
|
||
|
var height int
|
||
|
var nonce string
|
||
|
var user string
|
||
|
var miner string
|
||
|
var minerid string
|
||
|
var hash string
|
||
|
var subidx int
|
||
|
if err := rows.Scan(&user, &miner, &minerid, &height, &nonce, &hash, &subidx); err != nil {
|
||
|
//log.Printf("Error scanning row in blocks: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Error scanning row in blocks:", err.Error()))
|
||
|
return
|
||
|
}
|
||
|
var blockdata BlockCheckData
|
||
|
blockdata.Height = height
|
||
|
blockdata.Nonce = nonce
|
||
|
blockdata.User = user
|
||
|
blockdata.Miner = miner
|
||
|
blockdata.MinerId = minerid
|
||
|
blockdata.Hash = hash
|
||
|
blockdata.SubIdx = subidx
|
||
|
|
||
|
blocks = append(blocks, blockdata)
|
||
|
//fmt.Printf("blocks - Height: %d, Nonce: %d\n", height, nonce)
|
||
|
//log.Printf("update block height %d nonce %s, subidx %d, user %s", height, nonce, subidx, user+"."+miner+"_"+minerid)
|
||
|
}
|
||
|
for _, block := range blocks {
|
||
|
block_hash, err := gbt.GbtCtx.Client.GetBlockHash(int64(block.Height))
|
||
|
if err != nil {
|
||
|
logg.Info("[gbt]", zap.String("GetBlockHash ", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
rawmsgs := make([]json.RawMessage, 1)
|
||
|
param_str := `"` + block_hash.String() + `"`
|
||
|
rawmsgs[0] = json.RawMessage(param_str)
|
||
|
result, err := gbt.GbtCtx.Client.RawRequest("getblockheader", rawmsgs)
|
||
|
if err != nil {
|
||
|
//log.Printf("getblockheader %s", err.Error())
|
||
|
logg.Error("[gbt]", zap.String("getblockheader", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//log.Printf("getblockheader %d %s:%v", block.Height, block_hash, result)
|
||
|
var blockHeader msg.GetBlockHeaderMsgInt
|
||
|
err = json.Unmarshal(result, &blockHeader)
|
||
|
if err != nil {
|
||
|
//log.Printf("getblockheader Unmarshal %s", err.Error())
|
||
|
logg.Error("[gbt]", zap.String("getblockheader Unmarshal ", fmt.Sprint(block.Height)+" "+err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
rawmsgs_stats := make([]json.RawMessage, 1)
|
||
|
rawmsgs_stats[0] = json.RawMessage(param_str)
|
||
|
result_stats, err := gbt.GbtCtx.Client.RawRequest("getblockstats", rawmsgs_stats)
|
||
|
if err != nil {
|
||
|
//log.Printf("getblockstats %s", err.Error())
|
||
|
logg.Error("[gbt]", zap.String("getblockstats", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//log.Printf("getblockheader %d %s:%v", block.Height, block_hash, result)
|
||
|
var blockStats msg.GetBlockStatsMsg
|
||
|
err = json.Unmarshal(result_stats, &blockStats)
|
||
|
if err != nil {
|
||
|
//log.Printf("getblockstats Unmarshal %s", err.Error())
|
||
|
logg.Error("[gbt]", zap.String("getblockstats Unmarshal ", fmt.Sprint(block.Height)+" "+err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if blockHeader.Confirmations > 3 {
|
||
|
//log.Printf("cmp block: %d %s vs %s %s", block.Height, block.Nonce, string(result), blockHeader.Nonce)
|
||
|
//log.Printf("cmp block: %d %s %s_%s_%s vs %s\n", block.Height, block.Nonce, block.User, block.Miner, block.MinerId, blockHeader.Nonce)
|
||
|
nonceb := utility.Uint32ToByteBig(blockHeader.Nonce)
|
||
|
nonces := hex.EncodeToString(nonceb)
|
||
|
if nonces == block.Nonce {
|
||
|
|
||
|
block_height := int64(block.Height)
|
||
|
/*dbif.NotifyMinerSuccess(gbt.GbtCtx, block.User, block.Miner, block.MinerId, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)
|
||
|
dbif.NotifyUsersBlkStatsSuccess(gbt.GbtCtx, block.User, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)*/
|
||
|
dbif.NotifyPoolBlkStatsSuccess(gbt.GbtCtx, block_height, "", block.Nonce, int64(block.SubIdx), blockStats.Subsidy, blockStats.Totalfee)
|
||
|
dbif.NotifyBlkDetailSuccess(gbt.GbtCtx, block_height, "", block.Nonce, int64(block.SubIdx))
|
||
|
/*dbif.NotifyBlockStat(gbt.GbtCtx, block.User, block.Miner, block.MinerId, blockStats.Subsidy, blockStats.Totalfee)*/
|
||
|
//dbif.NotifyBlkNewSuccess(gbt.GbtCtx, block_height, "", block.Nonce, -1)
|
||
|
dbif.NotifyBlkNewDb(gbt.GbtCtx, block_height, block.Hash, true, block.Nonce, int64(block.SubIdx))
|
||
|
|
||
|
//gbt.GbtCtx.Blocks += 1
|
||
|
//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "blocks", gbt.GbtCtx.Blocks)
|
||
|
|
||
|
//gbt.GbtCtx.Reward += blockStats.Subsidy
|
||
|
//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "reward", gbt.GbtCtx.Reward)
|
||
|
|
||
|
//gbt.GbtCtx.Fee += blockStats.Totalfee
|
||
|
//cache.StorePoolCache(gbt.GbtCtx.RedisClient, gbt.GbtCtx.Coin, "fee", gbt.GbtCtx.Fee)
|
||
|
|
||
|
/*var pushmsg PushBlkNewMsg
|
||
|
pushmsg.Coin = gbt.GbtCtx.Coin
|
||
|
pushmsg.Height = block.Height
|
||
|
pushmsg.Nonce = block.Nonce
|
||
|
|
||
|
jsonData, err := json.Marshal(pushmsg)
|
||
|
if err != nil {
|
||
|
//log.Printf("Failed to marshal JSON: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Failed to marshal JSON:", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
if gbt.GbtCtx.PushCh == nil {
|
||
|
gbt.GbtCtx.PushCh = utility.InitZmqPush(gbt.GbtCtx.Config.Profit.Push)
|
||
|
}
|
||
|
if gbt.GbtCtx.PushCh != nil {
|
||
|
err = gbt.GbtCtx.PushCh.SendMessage([][]byte{[]byte("blk_new"), jsonData})
|
||
|
if err != nil {
|
||
|
//log.Printf("Failed to send data: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Failed to SendMessage:", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
updateSQL := `UPDATE blocks SET checked = 1 WHERE height = ? AND nonce = ? AND checked = 0`
|
||
|
_, err = db.Exec(updateSQL, block.Height, block.Nonce)
|
||
|
if err != nil {
|
||
|
//log.Printf("Error updating blk_new: %v", err)
|
||
|
logg.Error("[gbt]", zap.String("Error updating blk_new:", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//log.Printf("update block success: %d %s", block.Height, block.Nonce)
|
||
|
logg.Warn("[gbt]", zap.String("update block success:", fmt.Sprint(block.Height)+" "+block.Nonce))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func get_gbt_msg(gbt *GbtDgbContext) []byte {
|
||
|
rawmsgs := make([]json.RawMessage, 2)
|
||
|
//rawmsgs[0] = json.RawMessage(`{"capabilities": ["coinbasetxn", "workid", "coinbase/append"],"rules":["segwit"]}`)
|
||
|
rawmsgs[0] = json.RawMessage(`{"capabilities": ["coinbasetxn", "workid", "coinbase/append"]}`)
|
||
|
param_str2 := `"` + "skein" + `"`
|
||
|
if gbt.GbtCtx.Coin == "dgbq" {
|
||
|
param_str2 = `"` + "qubit" + `"`
|
||
|
} else if gbt.GbtCtx.Coin == "dgbo" {
|
||
|
param_str2 = `"` + "odo" + `"`
|
||
|
} else {
|
||
|
}
|
||
|
rawmsgs[1] = json.RawMessage(param_str2)
|
||
|
result, err := gbt.GbtCtx.Client.RawRequest("getblocktemplate", rawmsgs)
|
||
|
if err != nil {
|
||
|
logg.Error("[gbt]", zap.String("getblocktemplate", err.Error()))
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var rxmsg msg.GbtMsg
|
||
|
err = json.Unmarshal(result, &rxmsg)
|
||
|
if err != nil {
|
||
|
logg.Error("[gbt]", zap.String("getblocktemplate", err.Error()))
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
for i := 0; i < len(*rxmsg.Transactions); i++ {
|
||
|
var ts msg.GbtTransaction
|
||
|
err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
|
||
|
if err != nil {
|
||
|
logg.Error("[gbt]", zap.String("Unmarshal Transactions", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
if i >= 500 {
|
||
|
rxmsg.Coinbasevalue = rxmsg.Coinbasevalue - uint64(ts.Fee)
|
||
|
}
|
||
|
//logg.Debug("[gbt]", zap.String("ts.Hash", ts.Hash))
|
||
|
}
|
||
|
if len(*rxmsg.Transactions) >= 500 {
|
||
|
tss := (*rxmsg.Transactions)[:500]
|
||
|
rxmsg.Transactions = &tss
|
||
|
}
|
||
|
|
||
|
if rxmsg.Previousblockhash == gbt.last_gbt.Previousblockhash {
|
||
|
if time.Now().Sub(gbt.last_time) < time.Duration(gbt.GbtCtx.Config.Rpc.Timeout)*time.Millisecond {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gbtstr := fmt.Sprintf("version %x, prev %s, coinbase %x, longpoolid %s, target %s time %x, bits %s, height %d", rxmsg.Version, rxmsg.Previousblockhash, rxmsg.Coinbasevalue, rxmsg.Longpollid, rxmsg.Target, rxmsg.Curtime, rxmsg.Bits, rxmsg.Height)
|
||
|
logg.Debug("[gbt]", zap.String(" ", gbtstr))
|
||
|
|
||
|
body := new_job_from_gbt(gbt, &rxmsg)
|
||
|
|
||
|
gbt.last_gbt = rxmsg
|
||
|
|
||
|
gbt.last_time = time.Now()
|
||
|
|
||
|
return body
|
||
|
}
|
||
|
|
||
|
func gbt_notify_running(gbt *GbtDgbContext) {
|
||
|
for {
|
||
|
if !gbt.GbtCtx.Started {
|
||
|
break
|
||
|
}
|
||
|
if gbt.GbtCtx.NodeSubCh == nil {
|
||
|
gbt.GbtCtx.NodeSubCh = utility.InitZmqSub(gbt.GbtCtx.Config.Rpc.ZmqSub, utility.BITCOIND_ZMQ_HASHBLOCK)
|
||
|
}
|
||
|
if gbt.GbtCtx.NodeSubCh != nil {
|
||
|
cmsg_sub, err := gbt.GbtCtx.NodeSubCh.RecvMessage()
|
||
|
if err != nil {
|
||
|
if !gbt.GbtCtx.Started {
|
||
|
break
|
||
|
}
|
||
|
gbt.GbtCtx.NodeSubCh.SetSubscribe(utility.BITCOIND_ZMQ_HASHBLOCK)
|
||
|
gbt.GbtCtx.NodeSubCh.Connect(gbt.GbtCtx.Config.Rpc.ZmqSub)
|
||
|
continue
|
||
|
}
|
||
|
if len(cmsg_sub) >= 2 {
|
||
|
if string(cmsg_sub[0]) == "hashblock" {
|
||
|
GbtDgbCtx.new_block_index = GbtDgbCtx.new_block_index + 1
|
||
|
//log.Println("gbt_notify_running", hex.EncodeToString(cmsg_sub[1]), GbtDgbCtx.new_block_index)
|
||
|
gbt.new_block_chan <- GbtDgbCtx.new_block_index
|
||
|
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
logg.Error("[gbt]", zap.String("notify", "NodeSubCh fail!"))
|
||
|
time.Sleep(time.Duration(1) * time.Second)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func gbt_running(gbt *GbtDgbContext) {
|
||
|
jobtopic := "job" + gbt.GbtCtx.Coin
|
||
|
gbtmsg := get_gbt_msg(gbt)
|
||
|
if gbtmsg != nil {
|
||
|
if gbt.GbtCtx.PubCh == nil {
|
||
|
gbt.GbtCtx.PubCh = utility.InitZmqPub(gbt.GbtCtx.Config.Zmq.Pub)
|
||
|
}
|
||
|
if gbt.GbtCtx.PubCh != nil {
|
||
|
for trycnt := 0; trycnt < 3; trycnt++ {
|
||
|
err := gbt.GbtCtx.PubCh.SendMessage([][]byte{[]byte(jobtopic), gbtmsg})
|
||
|
if err != nil {
|
||
|
if !gbt.GbtCtx.Started {
|
||
|
return
|
||
|
}
|
||
|
//gbt.GbtCtx.PubCh.Bind(gbt.GbtCtx.Config.Zmq.Pub)
|
||
|
logg.Warn("[gbt]", zap.String("job ", err.Error()))
|
||
|
} else {
|
||
|
//gbt.GbtCtx.PubCh.SendChan <- [][]byte{[]byte("jobdgb"), gbtmsg}
|
||
|
logg.Warn("[gbt]", zap.String("job ", "sent"))
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
//gbt.GbtCtx.AlivingChan <- true
|
||
|
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
|
||
|
} else {
|
||
|
logg.Warn("[gbt]", zap.String("job ", "sent failed! PubCh nil"))
|
||
|
}
|
||
|
} else {
|
||
|
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
|
||
|
}
|
||
|
|
||
|
timer := time.NewTimer(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
|
||
|
defer timer.Stop()
|
||
|
|
||
|
for {
|
||
|
if !gbt.GbtCtx.Started {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
new_block_notify := false
|
||
|
|
||
|
select {
|
||
|
case blk_idx := <-gbt.new_block_chan:
|
||
|
log.Println("new block chan", blk_idx)
|
||
|
new_block_notify = true
|
||
|
if !timer.Stop() {
|
||
|
<-timer.C
|
||
|
}
|
||
|
timer.Reset(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
|
||
|
case <-gbt.GbtCtx.ExitGbtChan:
|
||
|
logg.Error("[gbt]", zap.String("gbt", "exit"))
|
||
|
return
|
||
|
case <-time.After(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond):
|
||
|
log.Println("poll gbt timeout")
|
||
|
timer.Reset(time.Duration(gbt.GbtCtx.Config.Rpc.Timeout) * time.Millisecond)
|
||
|
}
|
||
|
/*if check_bestblockhash(gbt) {*/
|
||
|
gbtmsg := get_gbt_msg(gbt)
|
||
|
if gbtmsg != nil {
|
||
|
//check_preblock(gbt, DbCtx)
|
||
|
if gbt.GbtCtx.PubCh == nil {
|
||
|
gbt.GbtCtx.PubCh = utility.InitZmqPub(gbt.GbtCtx.Config.Zmq.Pub)
|
||
|
}
|
||
|
if gbt.GbtCtx.PubCh != nil {
|
||
|
for trycnt := 0; trycnt < 3; trycnt++ {
|
||
|
err := gbt.GbtCtx.PubCh.SendMessage([][]byte{[]byte(jobtopic), gbtmsg})
|
||
|
if err != nil {
|
||
|
logg.Warn("[gbt]", zap.String("job ", err.Error()))
|
||
|
continue
|
||
|
} else {
|
||
|
//gbt.GbtCtx.PubCh.SendChan <- [][]byte{[]byte("jobdgb"), gbtmsg}
|
||
|
logg.Warn("[gbt]", zap.String("job ", "sent"))
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
//gbt.GbtCtx.AlivingChan <- true
|
||
|
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
|
||
|
} else {
|
||
|
logg.Warn("[gbt]", zap.String("job ", "sent failed! PubCh nil"))
|
||
|
}
|
||
|
} else {
|
||
|
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
|
||
|
}
|
||
|
|
||
|
if new_block_notify {
|
||
|
update_block_confirm(gbt)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func dgbInit(config *DgbConfig) {
|
||
|
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) {
|
||
|
GbtDgbCtx.GbtCtx = GbtCtx
|
||
|
GbtDgbCtx.last_height = 0
|
||
|
|
||
|
dgbInit(&GbtDgbCtx.Config)
|
||
|
|
||
|
GbtDgbCtx.last_time = time.Now()
|
||
|
logg = GbtCtx.Log
|
||
|
GbtDgbCtx.new_block_chan = make(chan int, 256)
|
||
|
GbtDgbCtx.new_block_index = 0
|
||
|
logg.Info("[gbt]", zap.String("gbt_dgb_version", GBT_DGB_VERSION))
|
||
|
}
|
||
|
|
||
|
func Start() {
|
||
|
go gbt_running(&GbtDgbCtx)
|
||
|
go gbt_notify_running(&GbtDgbCtx)
|
||
|
go submit_block_running(&GbtDgbCtx)
|
||
|
}
|
||
|
|
||
|
func Stop() {
|
||
|
defer close(GbtDgbCtx.new_block_chan)
|
||
|
}
|
||
|
|
||
|
func new_block_into_db(block *GbtDgbContext, 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
|
||
|
}
|
||
|
|
||
|
func submit_block_running(block *GbtDgbContext) {
|
||
|
logg.Info("[block]", zap.String("submit_block_running", "Start."))
|
||
|
blktopic := "blk" + block.GbtCtx.Coin
|
||
|
for {
|
||
|
if !block.GbtCtx.Started {
|
||
|
break
|
||
|
}
|
||
|
if block.GbtCtx.SubCh == nil {
|
||
|
block.GbtCtx.SubCh = utility.InitZmqSub(block.GbtCtx.Config.Zmq.Sub, blktopic)
|
||
|
}
|
||
|
if block.GbtCtx.SubCh != nil {
|
||
|
cmsg_sub, err := block.GbtCtx.SubCh.RecvMessage()
|
||
|
if err != nil {
|
||
|
if !block.GbtCtx.Started {
|
||
|
break
|
||
|
}
|
||
|
/*block.GbtCtx.SubCh.Destroy()
|
||
|
block.GbtCtx.SubCh = nil*/
|
||
|
time.Sleep(time.Duration(1) * time.Second)
|
||
|
block.GbtCtx.SubCh.SetSubscribe("blk" + block.GbtCtx.Coin)
|
||
|
block.GbtCtx.SubCh.Connect(block.GbtCtx.Config.Zmq.Sub)
|
||
|
//block.GbtCtx.SubCh.SetMaxmsgsize(1024 * 1024 * 8)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
//log.Println(cmsg_sub, len(cmsg_sub), block.GbtCtx.SubCh)
|
||
|
if len(cmsg_sub) >= 2 {
|
||
|
if string(cmsg_sub[0]) == blktopic {
|
||
|
cmsg := cmsg_sub[1]
|
||
|
//block data
|
||
|
msgb := make([]byte, len(cmsg)-16)
|
||
|
copy(msgb, cmsg)
|
||
|
|
||
|
//height
|
||
|
//heightb := make([]byte, 4)
|
||
|
heightb, err := hex.DecodeString(string(cmsg[len(msgb) : len(msgb)+8]))
|
||
|
if err != nil {
|
||
|
//block.Consumer.MarkOffset(cmsg, "")
|
||
|
logg.Error("[block]", zap.String("failed to decode height", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
var height uint32 = utility.ByteToUint32(heightb)
|
||
|
logg.Warn("[block]", zap.Uint32("height", height))
|
||
|
|
||
|
if height <= block.last_height {
|
||
|
continue
|
||
|
}
|
||
|
block.last_height = height
|
||
|
|
||
|
//index
|
||
|
//indexb := make([]byte, 4)
|
||
|
indexb, err1 := hex.DecodeString(string(cmsg[len(msgb)+8:]))
|
||
|
if err1 != nil {
|
||
|
//block.Consumer.MarkOffset(cmsg, "")
|
||
|
logg.Error("[block]", zap.String("failed to decode index", err1.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//copy(indexb, cmsg.Value[len(msgb)+4:])
|
||
|
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)))
|
||
|
var dgbblock msg.BlockGrsMsg
|
||
|
if err := json.Unmarshal(msgb, &dgbblock); err != nil {
|
||
|
//block.Consumer.MarkOffset(cmsg, "")
|
||
|
logg.Error("[block]", zap.String("failed to Unmarshal job", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
blk := hex.EncodeToString(dgbblock.Data)
|
||
|
rawmsgs := make([]json.RawMessage, 1)
|
||
|
logg.Info("[block]", zap.String("blk", blk))
|
||
|
rawmsgs[0] = json.RawMessage(`"` + blk + `"`)
|
||
|
//var last_result json.RawMessage
|
||
|
|
||
|
result, err := block.GbtCtx.Client.RawRequest("submitblock", rawmsgs)
|
||
|
if err != nil {
|
||
|
logg.Error("[block]", zap.String("submitblock", err.Error()))
|
||
|
} else {
|
||
|
//last_result = result
|
||
|
}
|
||
|
logg.Info("[block]", zap.String("result", string(result)))
|
||
|
|
||
|
/*dbif.NotifyMinerSubmitResult(block.GbtCtx, dgbblock.User, dgbblock.Miner, dgbblock.Index, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)
|
||
|
dbif.NotifyUsersBlkStatsSubmitResult(block.GbtCtx, dgbblock.User, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)*/
|
||
|
dbif.NotifyPoolBlkStatsSubmitResult(block.GbtCtx, int64(height), dgbblock.Pow, string(result), dgbblock.Nonce, dgbblock.SubIdx)
|
||
|
|
||
|
block.Submits += 1
|
||
|
//log.Printf("[block] height %d subidx %d nonce %s\n", height, dgbblock.SubIdx, dgbblock.Nonce)
|
||
|
logg.Warn("[block]", zap.Float64("total submits", block.Submits), zap.Int64("SubIdx", dgbblock.SubIdx))
|
||
|
if string(result) == "null" {
|
||
|
new_block_into_db(block, dgbblock.User, dgbblock.Miner, dgbblock.Index, int64(height), dgbblock.Nonce, dgbblock.Pow, dgbblock.SubIdx)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
logg.Error("[block]", zap.String("block", "SubCh failed! retry"))
|
||
|
time.Sleep(time.Duration(1) * time.Second)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func new_job_from_gbt(gbt *GbtDgbContext, rxmsg *msg.GbtMsg) []byte {
|
||
|
var sjob msg.StratumJob
|
||
|
|
||
|
sjob.Job_id = ""
|
||
|
sjob.IsClean = false
|
||
|
if rxmsg.Height != gbt.last_gbt.Height {
|
||
|
sjob.IsClean = true
|
||
|
}
|
||
|
vb := make([]byte, 4)
|
||
|
binary.LittleEndian.PutUint32(vb, uint32(rxmsg.Version))
|
||
|
sjob.Version = int32(binary.BigEndian.Uint32(vb))
|
||
|
sjob.PrevblockS = rxmsg.Previousblockhash
|
||
|
pb, err := hex.DecodeString(rxmsg.Previousblockhash)
|
||
|
sjob.PrevblockBig = hex.EncodeToString(utility.Reverse(utility.Convert_big_endian(pb)))
|
||
|
bb, _ := hex.DecodeString(rxmsg.Bits)
|
||
|
sjob.Bits = binary.LittleEndian.Uint32(bb)
|
||
|
sjob.BitsS = rxmsg.Bits
|
||
|
tb := make([]byte, 4)
|
||
|
binary.BigEndian.PutUint32(tb, rxmsg.Curtime)
|
||
|
t := binary.LittleEndian.Uint32(tb)
|
||
|
sjob.Timestamp = time.Unix(int64(t), 0)
|
||
|
sjob.Mintime = rxmsg.Mintime
|
||
|
sjob.Extranonce2_size = 4
|
||
|
sjob.Target = rxmsg.Target
|
||
|
sjob.Height = rxmsg.Height
|
||
|
sjob.Segwit = rxmsg.Segwit
|
||
|
cbvalue := rxmsg.Coinbasevalue
|
||
|
var nowit bool = false
|
||
|
tslen_nowit := len(*rxmsg.Transactions)
|
||
|
for i := 0; i < tslen_nowit; i++ {
|
||
|
var ts msg.GbtTransaction
|
||
|
err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
|
||
|
if err != nil {
|
||
|
logg.Error("[job]", zap.String("getblocktemplate", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//logg.Info("[job]", zap.String("transaction", ts.Hash))
|
||
|
if ts.Hash == ts.Txid {
|
||
|
} else {
|
||
|
nowit = true
|
||
|
}
|
||
|
cbvalue -= uint64(ts.Fee)
|
||
|
}
|
||
|
if nowit {
|
||
|
rxmsg.Coinbasevalue = cbvalue
|
||
|
}
|
||
|
|
||
|
payaddr := gbt.Config.Dgb.Addr
|
||
|
if len(gbt.GbtCtx.MinerAddrs) > 0 {
|
||
|
payaddr = gbt.GbtCtx.MinerAddrs[gbt.GbtCtx.MinerAddrIndex]
|
||
|
gbt.GbtCtx.MinerAddrIndex = gbt.GbtCtx.MinerAddrIndex + 1
|
||
|
if gbt.GbtCtx.MinerAddrIndex >= len(gbt.GbtCtx.MinerAddrs) {
|
||
|
gbt.GbtCtx.MinerAddrIndex = 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c1, c2, err := build_coinbase(rxmsg, payaddr)
|
||
|
|
||
|
if err != nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
sjob.Coinbase1 = c1
|
||
|
sjob.Coinbase2 = c2
|
||
|
//log.Println("coinbase1", c1, "coinbase2", c2)
|
||
|
tslen := len(*rxmsg.Transactions)
|
||
|
if nowit {
|
||
|
tslen = 0
|
||
|
}
|
||
|
|
||
|
trans := make([]string, tslen)
|
||
|
transdata := make([]string, tslen)
|
||
|
for i := 0; i < tslen; i++ {
|
||
|
var ts msg.GbtTransaction
|
||
|
err = json.Unmarshal((*rxmsg.Transactions)[i], &ts)
|
||
|
if err != nil {
|
||
|
logg.Error("[job]", zap.String("getblocktemplate", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
//logg.Info("[job]", zap.String("transaction", ts.Hash))
|
||
|
if ts.Hash == ts.Txid {
|
||
|
trans[i] = ts.Hash
|
||
|
} else {
|
||
|
trans[i] = ts.Txid
|
||
|
}
|
||
|
transdata[i] = ts.Data
|
||
|
}
|
||
|
if tslen > 1 {
|
||
|
sjob.Transactions = BuildMerkleTreeStore(&trans)
|
||
|
} else {
|
||
|
if tslen == 1 {
|
||
|
trans[0], err = utility.ReverseS(trans[0])
|
||
|
}
|
||
|
sjob.Transactions = &trans
|
||
|
}
|
||
|
sjob.TransData = &transdata
|
||
|
|
||
|
body, err := json.Marshal(sjob)
|
||
|
if err != nil {
|
||
|
logg.Error("[job]", zap.String("Marshal", err.Error()))
|
||
|
return nil
|
||
|
}
|
||
|
return body
|
||
|
|
||
|
}
|
||
|
|
||
|
func address_to_pubkeytxn3(in string) string {
|
||
|
//log.Println("[job]payee", in)
|
||
|
decoded, _, err := base58.CheckDecode(in)
|
||
|
if err != nil {
|
||
|
logg.Info("[job]", zap.String("CheckDecode", err.Error()))
|
||
|
}
|
||
|
//log.Println("[job]base58", encoded)
|
||
|
pubkeytxn := "76a914"
|
||
|
pubkeytxn += hex.EncodeToString(decoded)
|
||
|
pubkeytxn += "88ac"
|
||
|
return pubkeytxn
|
||
|
}
|
||
|
|
||
|
func build_coinbase(gbt *msg.GbtMsg, poolpayoutaddr string) (string, string, error) {
|
||
|
|
||
|
var coinbase1 string
|
||
|
coinbase1 = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff"
|
||
|
|
||
|
signheader := "/m2pool.com/"
|
||
|
var coinbase2 string = "0c" + hex.EncodeToString([]byte(signheader))
|
||
|
|
||
|
coinbase2 += "0000000001"
|
||
|
|
||
|
coinbase1 += "20"
|
||
|
heightb := make([]byte, 4)
|
||
|
binary.LittleEndian.PutUint32(heightb, gbt.Height)
|
||
|
/*if gbt.Height < 0x100 {
|
||
|
coinbase1 += "01"
|
||
|
} else {
|
||
|
if gbt.Height < 0x10000 {
|
||
|
coinbase1 += "02"
|
||
|
} else {
|
||
|
if gbt.Height < 0x1000000 {
|
||
|
coinbase1 += "03"
|
||
|
} else {
|
||
|
coinbase1 += "04"
|
||
|
}
|
||
|
}
|
||
|
}*/
|
||
|
coinbase1 += "04"
|
||
|
|
||
|
coinbase1 += hex.EncodeToString(heightb)[:8]
|
||
|
timeb := make([]byte, 4)
|
||
|
binary.LittleEndian.PutUint32(timeb, uint32(time.Now().Unix()))
|
||
|
coinbase1 += "04"
|
||
|
coinbase1 += hex.EncodeToString(timeb)
|
||
|
coinbase1 += "08"
|
||
|
|
||
|
logg.Info("[job]", zap.String("coinbase1", coinbase1))
|
||
|
|
||
|
coinbasevalueb := make([]byte, 8)
|
||
|
binary.LittleEndian.PutUint64(coinbasevalueb, gbt.Coinbasevalue)
|
||
|
coinbase2 += hex.EncodeToString(coinbasevalueb)
|
||
|
coinbase2 += "19"
|
||
|
//coinbase2 += address_to_pubkeytxn22(poolpayoutaddr)
|
||
|
//coinbase2 += address_to_pubkeytxn2(poolpayoutaddr)
|
||
|
coinbase2 += address_to_pubkeytxn3(poolpayoutaddr)
|
||
|
//coinbase2 += "0000000000000000"
|
||
|
//witlen := fmt.Sprintf("%02x", len(gbt.Segwit)/2)
|
||
|
//coinbase2 += witlen
|
||
|
//coinbase2 += gbt.Segwit
|
||
|
|
||
|
coinbase2 += "00000000"
|
||
|
logg.Info("[job]", zap.String("coinbase2", coinbase2))
|
||
|
return coinbase1, coinbase2, nil
|
||
|
}
|
||
|
|
||
|
func nextPowerOfTwo(n int) int {
|
||
|
// Return the number if it's already a power of 2.
|
||
|
if n&(n-1) == 0 {
|
||
|
return int(math.Log2(float64(n)))
|
||
|
}
|
||
|
|
||
|
// Figure out and return the next power of two.
|
||
|
exponent := int(math.Log2(float64(n))) + 1
|
||
|
return exponent // 2^exponent
|
||
|
}
|
||
|
|
||
|
func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
|
||
|
// Check for nil parameters
|
||
|
if left == nil || right == nil {
|
||
|
return nil // or handle error as appropriate
|
||
|
}
|
||
|
// Concatenate the left and right nodes.
|
||
|
var hash [chainhash.HashSize * 2]byte
|
||
|
copy(hash[:chainhash.HashSize], left[:])
|
||
|
copy(hash[chainhash.HashSize:], right[:])
|
||
|
|
||
|
newHash := chainhash.DoubleHashH(hash[:])
|
||
|
return &newHash
|
||
|
}
|
||
|
|
||
|
func BuildMerkleTreeStore(transactions *[]string) *[]string {
|
||
|
// Calculate how many entries are required to hold the binary merkle
|
||
|
// tree as a linear array and create an array of that size.
|
||
|
ts_size := len(*transactions)
|
||
|
if ts_size == 1 {
|
||
|
ts_string := make([]string, 1)
|
||
|
ts_string[0], _ = utility.ReverseS((*transactions)[0])
|
||
|
return &ts_string
|
||
|
}
|
||
|
exp := nextPowerOfTwo(ts_size + 1)
|
||
|
|
||
|
var all_size int = 0
|
||
|
var temp_size = ts_size - 1
|
||
|
var size int = 0
|
||
|
for i := 0; i < exp-1; i++ {
|
||
|
size = 1 + i + temp_size/2 + temp_size%2
|
||
|
|
||
|
if size < exp {
|
||
|
size = exp
|
||
|
}
|
||
|
|
||
|
all_size += size
|
||
|
temp_size = size - i - 2
|
||
|
|
||
|
}
|
||
|
arraySize := ts_size + all_size
|
||
|
|
||
|
merkles := make([]*chainhash.Hash, arraySize)
|
||
|
|
||
|
// Create the base transaction hashes and populate the array with them.
|
||
|
|
||
|
for i := range *transactions {
|
||
|
// If we're computing a witness merkle root, instead of the
|
||
|
// regular txid, we use the modified wtxid which includes a
|
||
|
// transaction's witness data within the digest. Additionally,
|
||
|
// the coinbase's wtxid is all zeroes.
|
||
|
|
||
|
merkles[i], _ = chainhash.NewHashFromStr((*transactions)[i])
|
||
|
}
|
||
|
|
||
|
// Start the array offset after the last transaction and adjusted to the
|
||
|
// next power of two.
|
||
|
offset := ts_size
|
||
|
|
||
|
all_size = ts_size
|
||
|
size = ts_size
|
||
|
var start int = 0
|
||
|
|
||
|
temp_size = ts_size - 1
|
||
|
for i := 0; i < exp-1; i++ {
|
||
|
for j := 0; j <= i; j++ {
|
||
|
merkles[offset] = merkles[start]
|
||
|
offset++
|
||
|
start++
|
||
|
}
|
||
|
for ; start < all_size; start += 2 {
|
||
|
if start+1 >= all_size {
|
||
|
newHash := HashMerkleBranches(merkles[start], merkles[start])
|
||
|
merkles[offset] = newHash
|
||
|
offset++
|
||
|
start++
|
||
|
break
|
||
|
} else {
|
||
|
newHash := HashMerkleBranches(merkles[start], merkles[start+1])
|
||
|
merkles[offset] = newHash
|
||
|
offset++
|
||
|
}
|
||
|
}
|
||
|
size = 1 + i + temp_size/2 + temp_size%2
|
||
|
|
||
|
if size <= exp {
|
||
|
size = exp
|
||
|
}
|
||
|
all_size += size
|
||
|
temp_size = size - i - 2
|
||
|
|
||
|
}
|
||
|
|
||
|
merkles_string := make([]string, exp)
|
||
|
var j int = 0
|
||
|
for i := offset - int(exp); i < offset; i++ {
|
||
|
merkles_string[j], _ = utility.ReverseS(merkles[i].String())
|
||
|
j++
|
||
|
}
|
||
|
|
||
|
logg.Info("[job]", zap.Any("merkles bytes", merkles_string))
|
||
|
return &merkles_string
|
||
|
}
|