github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/rpcapi/mining.go (about) 1 package rpcapi 2 3 import ( 4 "sort" 5 "time" 6 "encoding/hex" 7 "fmt" 8 "github.com/piotrnar/gocoin/lib/btc" 9 "github.com/piotrnar/gocoin/client/common" 10 "github.com/piotrnar/gocoin/client/network" 11 ) 12 13 const MAX_TXS_LEN = 999e3 // 999KB, with 1KB margin to not exceed 1MB with conibase 14 15 type OneTransaction struct { 16 Data string `json:"data"` 17 Hash string `json:"hash"` 18 Depends []uint `json:"depends"` 19 Fee uint64 `json:"fee"` 20 Sigops uint64 `json:"sigops"` 21 } 22 23 type GetBlockTemplateResp struct { 24 Capabilities []string `json:"capabilities"` 25 Version uint32 `json:"version"` 26 PreviousBlockHash string `json:"previousblockhash"` 27 Transactions []OneTransaction `json:"transactions"` 28 Coinbaseaux struct { 29 Flags string `json:"flags"` 30 } `json:"coinbaseaux"` 31 Coinbasevalue uint64 `json:"coinbasevalue"` 32 Longpollid string `json:"longpollid"` 33 Target string `json:"target"` 34 Mintime uint `json:"mintime"` 35 Mutable []string `json:"mutable"` 36 Noncerange string `json:"noncerange"` 37 Sigoplimit uint `json:"sigoplimit"` 38 Sizelimit uint `json:"sizelimit"` 39 Curtime uint `json:"curtime"` 40 Bits string `json:"bits"` 41 Height uint `json:"height"` 42 } 43 44 type RpcGetBlockTemplateResp struct { 45 Id interface{} `json:"id"` 46 Result GetBlockTemplateResp `json:"result"` 47 Error interface{} `json:"error"` 48 } 49 50 func GetNextBlockTemplate(r *GetBlockTemplateResp) { 51 var zer [32]byte 52 53 common.Last.Mutex.Lock() 54 55 r.Curtime = uint(time.Now().Unix()) 56 r.Mintime = uint(common.Last.Block.GetMedianTimePast()) + 1 57 if r.Curtime < r.Mintime { 58 r.Curtime = r.Mintime 59 } 60 height := common.Last.Block.Height+1 61 bits := common.BlockChain.GetNextWorkRequired(common.Last.Block, uint32(r.Curtime)) 62 target := btc.SetCompact(bits).Bytes() 63 64 r.Capabilities = []string{"proposal"} 65 r.Version = 4 66 r.PreviousBlockHash = common.Last.Block.BlockHash.String() 67 r.Transactions, r.Coinbasevalue = GetTransactions(height, uint32(r.Mintime)) 68 r.Coinbasevalue += btc.GetBlockReward(height) 69 r.Coinbaseaux.Flags = "" 70 r.Longpollid = r.PreviousBlockHash 71 r.Target = hex.EncodeToString(append(zer[:32-len(target)], target...)) 72 r.Mutable = []string{"time","transactions","prevblock"} 73 r.Noncerange = "00000000ffffffff" 74 r.Sigoplimit = btc.MAX_BLOCK_SIGOPS_COST / btc.WITNESS_SCALE_FACTOR 75 r.Sizelimit = 1e6 76 r.Bits = fmt.Sprintf("%08x", bits) 77 r.Height = uint(height) 78 79 last_given_time = uint32(r.Curtime) 80 last_given_mintime = uint32(r.Mintime) 81 82 common.Last.Mutex.Unlock() 83 } 84 85 86 87 /* memory pool transaction sorting stuff */ 88 type one_mining_tx struct { 89 *network.OneTxToSend 90 depends []uint 91 startat int 92 } 93 94 type sortedTxList []*one_mining_tx 95 func (tl sortedTxList) Len() int {return len(tl)} 96 func (tl sortedTxList) Swap(i, j int) { tl[i], tl[j] = tl[j], tl[i] } 97 func (tl sortedTxList) Less(i, j int) bool { return tl[j].Fee < tl[i].Fee } 98 99 100 var txs_so_far map[[32]byte] uint 101 var totlen int 102 var sigops uint64 103 104 func get_next_tranche_of_txs(height, timestamp uint32) (res sortedTxList) { 105 var unsp *btc.TxOut 106 var all_inputs_found bool 107 for _, v := range network.TransactionsToSend { 108 tx := v.Tx 109 110 if _, ok := txs_so_far[tx.Hash.Hash]; ok { 111 continue 112 } 113 114 if !tx.IsFinal(height, timestamp) { 115 continue 116 } 117 118 if totlen+len(v.Raw) > 1e6 { 119 //println("Too many txs - limit to 999000 bytes") 120 return 121 } 122 totlen += len(v.Raw) 123 124 if sigops + v.SigopsCost > btc.MAX_BLOCK_SIGOPS_COST { 125 //println("Too many sigops - limit to 999000 bytes") 126 return 127 } 128 sigops += v.SigopsCost 129 130 all_inputs_found = true 131 var depends []uint 132 for i := range tx.TxIn { 133 unsp = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) 134 if unsp==nil { 135 // not found in the confirmed blocks 136 // check if txid is in txs_so_far 137 if idx, ok := txs_so_far[tx.TxIn[i].Input.Hash]; !ok { 138 // also not in txs_so_far 139 all_inputs_found = false 140 break 141 } else { 142 depends = append(depends, idx) 143 } 144 } 145 } 146 147 if all_inputs_found { 148 res = append(res, &one_mining_tx{OneTxToSend:v, depends:depends, startat:1+len(txs_so_far)}) 149 } 150 } 151 return 152 } 153 154 func GetTransactions(height, timestamp uint32) (res []OneTransaction, totfees uint64) { 155 156 network.TxMutex.Lock() 157 defer network.TxMutex.Unlock() 158 159 var cnt int 160 var sorted sortedTxList 161 txs_so_far = make(map[[32]byte]uint) 162 totlen = 0 163 sigops = 0 164 //println("\ngetting txs from the pool of", len(network.TransactionsToSend), "...") 165 for { 166 new_piece := get_next_tranche_of_txs(height, timestamp) 167 if new_piece.Len()==0 { 168 break 169 } 170 //println("adding another", len(new_piece)) 171 sort.Sort(new_piece) 172 173 for i:=0; i<len(new_piece); i++ { 174 txs_so_far[new_piece[i].Tx.Hash.Hash] = uint(1+len(sorted)+i) 175 } 176 177 sorted = append(sorted, new_piece...) 178 } 179 /*if len(txs_so_far)!=len(network.TransactionsToSend) { 180 println("ERROR: txs_so_far len", len(txs_so_far), " - please report!") 181 }*/ 182 txs_so_far = nil // leave it for the garbage collector 183 184 res = make([]OneTransaction, len(sorted)) 185 for cnt=0; cnt<len(sorted); cnt++ { 186 v := sorted[cnt] 187 res[cnt].Data = hex.EncodeToString(v.Raw) 188 res[cnt].Hash = v.Tx.Hash.String() 189 res[cnt].Fee = v.Fee 190 res[cnt].Sigops = v.SigopsCost 191 res[cnt].Depends = v.depends 192 totfees += v.Fee 193 //println("", cnt+1, v.Tx.Hash.String(), " turn:", v.startat, " spb:", int(v.Fee)/len(v.Data), " depend:", fmt.Sprint(v.depends)) 194 } 195 196 //println("returning transacitons:", totlen, len(res)) 197 return 198 }