// 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 }