m2pool_core/internal/gbt/mona/mona.go

859 lines
25 KiB
Go

// mona.go
package mona
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_MONA_VERSION string = "mona v3.0l"
type MonaAddrConfig struct {
Addr string `json:"addr"`
}
type MonaConfig struct {
Mona MonaAddrConfig `json:"mona"`
}
type GbtMonaContext struct {
Config MonaConfig
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 GbtMonaCtx GbtMonaContext
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 *GbtMonaContext) {
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 *GbtMonaContext) []byte {
rawmsgs := make([]json.RawMessage, 1)
//rawmsgs[0] = json.RawMessage(`{"capabilities": ["coinbasetxn", "workid", "coinbase/append"],"rules":["segwit"]}`)
rawmsgs[0] = json.RawMessage(`{"rules":["segwit"]}`)
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
}
//logg.Debug("[gbt]", zap.String("ts.Hash", ts.Hash))
}
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 *GbtMonaContext) {
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" {
GbtMonaCtx.new_block_index = GbtMonaCtx.new_block_index + 1
//log.Println("gbt_notify_running", hex.EncodeToString(cmsg_sub[1]), GbtMonaCtx.new_block_index)
gbt.new_block_chan <- GbtMonaCtx.new_block_index
}
}
} else {
logg.Error("[gbt]", zap.String("notify", "NodeSubCh fail!"))
time.Sleep(time.Duration(1) * time.Second)
}
}
}
func gbt_running(gbt *GbtMonaContext) {
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("jobmona"), 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("jobmona"), 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"))
}
atomic.StoreInt32(&(gbt.GbtCtx.FlagAliving), 1)
} 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("jobmona"), gbtmsg})
if err != nil {
logg.Warn("[gbt]", zap.String("job ", err.Error()))
continue
} else {
//gbt.GbtCtx.PubCh.SendChan <- [][]byte{[]byte("jobmona"), 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 monaInit(config *MonaConfig) {
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) {
GbtMonaCtx.GbtCtx = GbtCtx
GbtMonaCtx.last_height = 0
monaInit(&GbtMonaCtx.Config)
GbtMonaCtx.last_time = time.Now()
logg = GbtCtx.Log
GbtMonaCtx.new_block_chan = make(chan int, 256)
GbtMonaCtx.new_block_index = 0
logg.Info("[gbt]", zap.String("gbt_mona_version", GBT_MONA_VERSION))
}
func Start() {
go gbt_running(&GbtMonaCtx)
go gbt_notify_running(&GbtMonaCtx)
go submit_block_running(&GbtMonaCtx)
}
func Stop() {
defer close(GbtMonaCtx.new_block_chan)
}
func new_block_into_db(block *GbtMonaContext, 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 *GbtMonaContext) {
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
}
/*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]) == "blkmona" {
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 monablock msg.BlockGrsMsg
if err := json.Unmarshal(msgb, &monablock); err != nil {
//block.Consumer.MarkOffset(cmsg, "")
logg.Error("[block]", zap.String("failed to Unmarshal job", err.Error()))
continue
}
blk := hex.EncodeToString(monablock.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, monablock.User, monablock.Miner, monablock.Index, int64(height), monablock.Pow, string(result), monablock.Nonce, monablock.SubIdx)
dbif.NotifyUsersBlkStatsSubmitResult(block.GbtCtx, monablock.User, int64(height), monablock.Pow, string(result), monablock.Nonce, monablock.SubIdx)*/
dbif.NotifyPoolBlkStatsSubmitResult(block.GbtCtx, int64(height), monablock.Pow, string(result), monablock.Nonce, monablock.SubIdx)
block.Submits += 1
//log.Printf("[block] height %d subidx %d nonce %s\n", height, monablock.SubIdx, monablock.Nonce)
logg.Warn("[block]", zap.Float64("total submits", block.Submits), zap.Int64("SubIdx", monablock.SubIdx))
if string(result) == "null" {
new_block_into_db(block, monablock.User, monablock.Miner, monablock.Index, int64(height), monablock.Nonce, monablock.Pow, monablock.SubIdx)
}
}
}
} else {
logg.Error("[block]", zap.String("block", "SubCh failed! retry"))
time.Sleep(time.Duration(1) * time.Second)
}
}
}
func new_job_from_gbt(gbt *GbtMonaContext, 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
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
}
if ts.Hash == ts.Txid {
} else {
}
}
if nowit {
rxmsg.Coinbasevalue = 1250000000
logg.Info("[job]", zap.String("nowit", "1"))
}
payaddr := gbt.Config.Mona.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
tslen := len(*rxmsg.Transactions)
logg.Info("[job]", zap.Int("tslen", tslen))
if nowit {
tslen = 0
logg.Info("[job]", zap.String("nowit", "1"))
}
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
}
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_pubkeytxn2(in string) string {
//log.Println("[job]", in)
decoded, _, err := base58.CheckDecode(in)
if err != nil {
logg.Info("[job]", zap.String("CheckDecode", err.Error()))
}
//log.Println("[job]base58", decoded)
pubkeytxn := "a914"
var pubkeytxnb string
pubkeytxnb = hex.EncodeToString(decoded)
/*if len(decoded) > 21 {
var db []byte = decoded[1:21]
pubkeytxnb = hex.EncodeToString(db)
} else {
var db []byte = decoded[1:]
pubkeytxnb = hex.EncodeToString(db)
}*/
pubkeytxn += pubkeytxnb
pubkeytxn += "87"
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 += "0000000002"
coinbase1 += "1F"
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 += hex.EncodeToString(heightb)[:6]
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 += "17"
//coinbase2 += address_to_pubkeytxn22(poolpayoutaddr)
coinbase2 += address_to_pubkeytxn2(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
}