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  }