github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/usif/webui/txs.go (about)

     1  package webui
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/piotrnar/gocoin/client/common"
    16  	"github.com/piotrnar/gocoin/client/network"
    17  	"github.com/piotrnar/gocoin/client/usif"
    18  	"github.com/piotrnar/gocoin/lib/btc"
    19  	"github.com/piotrnar/gocoin/lib/script"
    20  )
    21  
    22  func p_txs(w http.ResponseWriter, r *http.Request) {
    23  	if !ipchecker(r) {
    24  		return
    25  	}
    26  
    27  	var txloadresult string
    28  	var wg sync.WaitGroup
    29  	var tx2in []byte
    30  
    31  	// Check if there is a tx upload request
    32  	r.ParseMultipartForm(2e6)
    33  	fil, _, _ := r.FormFile("txfile")
    34  	if fil != nil {
    35  		tx2in, _ = ioutil.ReadAll(fil)
    36  	} else if len(r.Form["rawtx"]) == 1 {
    37  		tx2in, _ = hex.DecodeString(r.Form["rawtx"][0])
    38  	}
    39  
    40  	if len(tx2in) > 0 {
    41  		wg.Add(1)
    42  		req := &usif.OneUiReq{Param: string(tx2in)}
    43  		req.Done.Add(1)
    44  		req.Handler = func(dat string) {
    45  			txloadresult = usif.LoadRawTx([]byte(dat))
    46  			wg.Done()
    47  		}
    48  		usif.UiChannel <- req
    49  	}
    50  
    51  	s := load_template("txs.html")
    52  
    53  	wg.Wait()
    54  	if txloadresult != "" {
    55  		ld := load_template("txs_load.html")
    56  		ld = strings.Replace(ld, "{TX_RAW_DATA}", txloadresult, 1)
    57  		s = strings.Replace(s, "<!--TX_LOAD-->", ld, 1)
    58  	}
    59  
    60  	if common.CFG.TXPool.Enabled {
    61  		s = strings.Replace(s, "<!--MEM_POOL_ENABLED-->", "Enabled", 1)
    62  	} else {
    63  		s = strings.Replace(s, "<!--MEM_POOL_ENABLED-->", "Disabled", 1)
    64  	}
    65  
    66  	if common.CFG.TXRoute.Enabled {
    67  		s = strings.Replace(s, "<!--TX_ROUTE_ENABLED-->", "Enabled", 1)
    68  	} else {
    69  		s = strings.Replace(s, "<!--TX_ROUTE_ENABLED-->", "Disabled", 1)
    70  	}
    71  
    72  	write_html_head(w, r)
    73  	w.Write([]byte(s))
    74  	write_html_tail(w)
    75  }
    76  
    77  func output_tx_xml(w http.ResponseWriter, tx *btc.Tx) {
    78  	tx.Spent_outputs = make([]*btc.TxOut, len(tx.TxIn))
    79  	for i := range tx.TxIn {
    80  		var po *btc.TxOut
    81  		inpid := btc.NewUint256(tx.TxIn[i].Input.Hash[:])
    82  		if txinmem, ok := network.TransactionsToSend[inpid.BIdx()]; ok {
    83  			if int(tx.TxIn[i].Input.Vout) < len(txinmem.TxOut) {
    84  				po = txinmem.TxOut[tx.TxIn[i].Input.Vout]
    85  			}
    86  		} else {
    87  			po = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input)
    88  		}
    89  		tx.Spent_outputs[i] = po
    90  	}
    91  	w.Write([]byte("<input_list>"))
    92  	ver_flags := common.CurrentScriptFlags()
    93  	for i := range tx.TxIn {
    94  		w.Write([]byte("<input>"))
    95  		w.Write([]byte("<script_sig>"))
    96  		w.Write([]byte(hex.EncodeToString(tx.TxIn[i].ScriptSig)))
    97  		w.Write([]byte("</script_sig>"))
    98  		fmt.Fprint(w, "<txid-vout>", tx.TxIn[i].Input.String(), "</txid-vout>")
    99  		po := tx.Spent_outputs[i]
   100  		if po != nil {
   101  			ok := script.VerifyTxScript(po.Pk_script, &script.SigChecker{Amount: po.Value, Idx: i, Tx: tx}, ver_flags)
   102  			if !ok {
   103  				w.Write([]byte("<status>Script FAILED</status>"))
   104  			} else {
   105  				w.Write([]byte("<status>OK</status>"))
   106  			}
   107  			fmt.Fprint(w, "<value>", po.Value, "</value>")
   108  			fmt.Fprint(w, "<pkscript>", hex.EncodeToString(po.Pk_script), "</pkscript>")
   109  			if ad := btc.NewAddrFromPkScript(po.Pk_script, common.Testnet); ad != nil {
   110  				fmt.Fprint(w, "<addr>", ad.String(), "</addr>")
   111  			}
   112  			fmt.Fprint(w, "<block>", po.BlockHeight, "</block>")
   113  
   114  			if btc.IsP2SH(po.Pk_script) {
   115  				fmt.Fprint(w, "<input_sigops>", btc.WITNESS_SCALE_FACTOR*btc.GetP2SHSigOpCount(tx.TxIn[i].ScriptSig), "</input_sigops>")
   116  			}
   117  			fmt.Fprint(w, "<witness_sigops>", tx.CountWitnessSigOps(i, po.Pk_script), "</witness_sigops>")
   118  		} else {
   119  			w.Write([]byte("<status>Unknown input</status>"))
   120  		}
   121  		fmt.Fprint(w, "<sequence>", tx.TxIn[i].Sequence, "</sequence>")
   122  
   123  		if tx.SegWit != nil {
   124  			w.Write([]byte("<segwit>"))
   125  			for _, wit := range tx.SegWit[i] {
   126  				w.Write([]byte("<witness>"))
   127  				w.Write([]byte(hex.EncodeToString(wit)))
   128  				w.Write([]byte("</witness>"))
   129  			}
   130  			w.Write([]byte("</segwit>"))
   131  		}
   132  		w.Write([]byte("</input>"))
   133  	}
   134  	w.Write([]byte("</input_list>"))
   135  
   136  	w.Write([]byte("<output_list>"))
   137  	for i := range tx.TxOut {
   138  		w.Write([]byte("<output>"))
   139  		fmt.Fprint(w, "<value>", tx.TxOut[i].Value, "</value>")
   140  		adr := btc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, common.Testnet)
   141  		if adr != nil {
   142  			fmt.Fprint(w, "<addr>", adr.String(), "</addr>")
   143  		} else {
   144  			fmt.Fprint(w, "<addr>", "scr:"+hex.EncodeToString(tx.TxOut[i].Pk_script), "</addr>")
   145  		}
   146  		w.Write([]byte("</output>"))
   147  	}
   148  	w.Write([]byte("</output_list>"))
   149  }
   150  
   151  func tx_xml(w http.ResponseWriter, v *network.OneTxToSend, verbose bool) {
   152  	w.Write([]byte("<tx><status>OK</status>"))
   153  	fmt.Fprint(w, "<id>", v.Tx.Hash.String(), "</id>")
   154  	fmt.Fprint(w, "<version>", v.Tx.Version, "</version>")
   155  	fmt.Fprint(w, "<time>", v.Firstseen.Unix(), "</time>")
   156  	if int(v.Size) != len(v.Raw) {
   157  		panic("TX size does not match data length")
   158  	}
   159  
   160  	fmt.Fprint(w, "<size>", v.Size, "</size>")
   161  	fmt.Fprint(w, "<nwsize>", v.NoWitSize, "</nwsize>")
   162  	fmt.Fprint(w, "<weight>", v.Weight(), "</weight>")
   163  	fmt.Fprint(w, "<sw_compress>", 1000*(int(v.Size)-int(v.NoWitSize))/int(v.Size), "</sw_compress>")
   164  	fmt.Fprint(w, "<inputs>", len(v.TxIn), "</inputs>")
   165  	fmt.Fprint(w, "<outputs>", len(v.TxOut), "</outputs>")
   166  	fmt.Fprint(w, "<lock_time>", v.Lock_time, "</lock_time>")
   167  	fmt.Fprint(w, "<witness_cnt>", len(v.SegWit), "</witness_cnt>")
   168  	if verbose {
   169  		output_tx_xml(w, v.Tx)
   170  	}
   171  	fmt.Fprint(w, "<own>", v.Local, "</own>")
   172  	fmt.Fprint(w, "<firstseen>", v.Firstseen.Unix(), "</firstseen>")
   173  	fmt.Fprint(w, "<invsentcnt>", v.Invsentcnt, "</invsentcnt>")
   174  	fmt.Fprint(w, "<sigops>", v.SigopsCost, "</sigops>")
   175  	fmt.Fprint(w, "<sentcnt>", v.SentCnt, "</sentcnt>")
   176  	fmt.Fprint(w, "<sentlast>", v.Lastsent.Unix(), "</sentlast>")
   177  	fmt.Fprint(w, "<volume>", v.Volume, "</volume>")
   178  	fmt.Fprint(w, "<fee>", v.Fee, "</fee>")
   179  	fmt.Fprint(w, "<blocked>", network.ReasonToString(v.Blocked), "</blocked>")
   180  	fmt.Fprint(w, "<final>", v.Final, "</final>")
   181  	fmt.Fprint(w, "<verify_us>", uint(v.VerifyTime/time.Microsecond), "</verify_us>")
   182  	w.Write([]byte("</tx>"))
   183  }
   184  
   185  func output_utxo_tx_xml(w http.ResponseWriter, minedid, minedat string) {
   186  	txid := btc.NewUint256FromString(minedid)
   187  	if txid == nil {
   188  		return
   189  	}
   190  
   191  	block_number, er := strconv.ParseUint(minedat, 10, 32)
   192  	if er != nil {
   193  		return
   194  	}
   195  
   196  	lck := new(usif.OneLock)
   197  	lck.In.Add(1)
   198  	lck.Out.Add(1)
   199  	usif.LocksChan <- lck
   200  	lck.In.Wait()
   201  
   202  	w.Write([]byte("<tx>"))
   203  	fmt.Fprint(w, "<id>", minedid, "</id>")
   204  	if dat, er := common.GetRawTx(uint32(block_number), txid); er == nil {
   205  		w.Write([]byte("<status>OK</status>"))
   206  		w.Write([]byte(fmt.Sprint("<size>", len(dat), "</size>")))
   207  		tx, _ := btc.NewTx(dat)
   208  		output_tx_xml(w, tx)
   209  	} else {
   210  		w.Write([]byte("<status>Not found</status>"))
   211  	}
   212  	w.Write([]byte("</tx>"))
   213  
   214  	lck.Out.Done()
   215  
   216  }
   217  
   218  /* memory pool transaction sorting stuff */
   219  type sortedTxList []*network.OneTxToSend
   220  
   221  func (tl sortedTxList) Len() int      { return len(tl) }
   222  func (tl sortedTxList) Swap(i, j int) { tl[i], tl[j] = tl[j], tl[i] }
   223  func (tl sortedTxList) Less(i, j int) bool {
   224  	var res bool
   225  	switch txs2s_sort {
   226  	case "age":
   227  		res = tl[j].Firstseen.UnixNano() > tl[i].Firstseen.UnixNano()
   228  	case "siz":
   229  		res = tl[j].Size < tl[i].Size
   230  	case "nws":
   231  		res = tl[j].NoWitSize < tl[i].NoWitSize
   232  	case "wgh":
   233  		res = tl[j].Weight() < tl[i].Weight()
   234  	case "inp":
   235  		res = len(tl[j].TxIn) < len(tl[i].TxIn)
   236  	case "out":
   237  		res = len(tl[j].TxOut) < len(tl[i].TxOut)
   238  	case "btc":
   239  		res = tl[j].Volume < tl[i].Volume
   240  	case "fee":
   241  		res = tl[j].Fee < tl[i].Fee
   242  	case "ops":
   243  		res = tl[j].SigopsCost < tl[i].SigopsCost
   244  	case "rbf":
   245  		res = !tl[j].Final && tl[i].Final
   246  	case "ver":
   247  		res = int(tl[j].VerifyTime) < int(tl[i].VerifyTime)
   248  	case "swc":
   249  		sw_compr_i := float64(int(tl[i].Size)-int(tl[i].NoWitSize)) / float64(tl[i].Size)
   250  		sw_compr_j := float64(int(tl[j].Size)-int(tl[j].NoWitSize)) / float64(tl[j].Size)
   251  		res = sw_compr_i > sw_compr_j
   252  	default: /*spb*/
   253  		spb_i := float64(tl[i].Fee) / float64(tl[i].Weight())
   254  		spb_j := float64(tl[j].Fee) / float64(tl[j].Weight())
   255  		res = spb_j < spb_i
   256  	}
   257  	if txs2s_sort_desc {
   258  		return res
   259  	} else {
   260  		return !res
   261  	}
   262  }
   263  
   264  var txs2s_count int = 1000
   265  var txs2s_sort string = "spb"
   266  var txs2s_sort_desc bool = true
   267  
   268  func xml_txs2s(w http.ResponseWriter, r *http.Request) {
   269  	if !ipchecker(r) {
   270  		return
   271  	}
   272  
   273  	w.Header()["Content-Type"] = []string{"text/xml"}
   274  
   275  	if len(r.Form["minedid"]) > 0 && len(r.Form["minedat"]) > 0 {
   276  		output_utxo_tx_xml(w, r.Form["minedid"][0], r.Form["minedat"][0])
   277  		return
   278  	}
   279  
   280  	if len(r.Form["id"]) > 0 {
   281  		txid := btc.NewUint256FromString(r.Form["id"][0])
   282  		if txid == nil {
   283  			return
   284  		}
   285  		network.TxMutex.Lock()
   286  		defer network.TxMutex.Unlock()
   287  		if t2s, ok := network.TransactionsToSend[txid.BIdx()]; ok {
   288  			tx_xml(w, t2s, true)
   289  		} else {
   290  			w.Write([]byte("<tx>"))
   291  			fmt.Fprint(w, "<id>", txid.String(), "</id>")
   292  			w.Write([]byte("<status>Not found</status>"))
   293  			w.Write([]byte("</tx>"))
   294  		}
   295  		return
   296  	}
   297  
   298  	if checksid(r) {
   299  		if len(r.Form["del"]) > 0 {
   300  			tid := btc.NewUint256FromString(r.Form["del"][0])
   301  			if tid != nil {
   302  				network.TxMutex.Lock()
   303  				if tts, ok := network.TransactionsToSend[tid.BIdx()]; ok {
   304  					tts.Delete(true, 0)
   305  				}
   306  				network.TxMutex.Unlock()
   307  			}
   308  		}
   309  
   310  		if len(r.Form["send"]) > 0 {
   311  			tid := btc.NewUint256FromString(r.Form["send"][0])
   312  			if tid != nil {
   313  				network.TxMutex.Lock()
   314  				if ptx, ok := network.TransactionsToSend[tid.BIdx()]; ok {
   315  					network.TxMutex.Unlock()
   316  					cnt := network.NetRouteInv(1, tid, nil)
   317  					if cnt == 0 {
   318  						usif.SendInvToRandomPeer(1, tid)
   319  					} else {
   320  						ptx.Invsentcnt += cnt
   321  					}
   322  				} else {
   323  					network.TxMutex.Unlock()
   324  				}
   325  			}
   326  		}
   327  
   328  		if len(r.Form["sendone"]) > 0 {
   329  			tid := btc.NewUint256FromString(r.Form["sendone"][0])
   330  			if tid != nil {
   331  				network.TxMutex.Lock()
   332  				if ptx, ok := network.TransactionsToSend[tid.BIdx()]; ok {
   333  					network.TxMutex.Unlock()
   334  					usif.SendInvToRandomPeer(1, tid)
   335  					ptx.Invsentcnt++
   336  				} else {
   337  					network.TxMutex.Unlock()
   338  				}
   339  			}
   340  		}
   341  
   342  		if len(r.Form["quiet"]) > 0 {
   343  			return
   344  		}
   345  
   346  		if len(r.Form["cnt"]) > 0 {
   347  			u, e := strconv.ParseUint(r.Form["cnt"][0], 10, 32)
   348  			if e == nil && u > 0 && u < 10e3 {
   349  				txs2s_count = int(u)
   350  			}
   351  		}
   352  
   353  		if len(r.Form["sort"]) > 0 && len(r.Form["sort"][0]) == 3 {
   354  			txs2s_sort = r.Form["sort"][0]
   355  		}
   356  
   357  		txs2s_sort_desc = len(r.Form["descending"]) > 0
   358  	}
   359  
   360  	network.TxMutex.Lock()
   361  	defer network.TxMutex.Unlock()
   362  
   363  	sorted := make(sortedTxList, len(network.TransactionsToSend))
   364  	var cnt int
   365  	for _, v := range network.TransactionsToSend {
   366  		if len(r.Form["ownonly"]) > 0 && !v.Local {
   367  			continue
   368  		}
   369  		sorted[cnt] = v
   370  		cnt++
   371  	}
   372  	sorted = sorted[:cnt]
   373  	sort.Sort(sorted)
   374  
   375  	w.Write([]byte("<txpool>"))
   376  	for cnt = 0; cnt < len(sorted) && cnt < txs2s_count; cnt++ {
   377  		v := sorted[cnt]
   378  		tx_xml(w, v, false)
   379  	}
   380  	w.Write([]byte("</txpool>"))
   381  }
   382  
   383  func xml_txsre(w http.ResponseWriter, r *http.Request) {
   384  	if !ipchecker(r) {
   385  		return
   386  	}
   387  
   388  	w.Header()["Content-Type"] = []string{"text/xml"}
   389  	w.Write([]byte("<txbanned>"))
   390  	network.TxMutex.Lock()
   391  	for _, v := range network.TransactionsRejected {
   392  		w.Write([]byte("<tx>"))
   393  		fmt.Fprint(w, "<id>", v.Id.String(), "</id>")
   394  		fmt.Fprint(w, "<time>", v.Time.Unix(), "</time>")
   395  		fmt.Fprint(w, "<size>", v.Size, "</size>")
   396  		fmt.Fprint(w, "<reason>", network.ReasonToString(v.Reason), "</reason>")
   397  		w.Write([]byte("</tx>"))
   398  	}
   399  	network.TxMutex.Unlock()
   400  	w.Write([]byte("</txbanned>"))
   401  }
   402  
   403  func xml_txw4i(w http.ResponseWriter, r *http.Request) {
   404  	if !ipchecker(r) {
   405  		return
   406  	}
   407  
   408  	w.Header()["Content-Type"] = []string{"text/xml"}
   409  	w.Write([]byte("<pending>"))
   410  	network.TxMutex.Lock()
   411  	for _, v := range network.WaitingForInputs {
   412  		w.Write([]byte("<wait4>"))
   413  		fmt.Fprint(w, "<id>", v.TxID.String(), "</id>")
   414  		for x, t := range v.Ids {
   415  			w.Write([]byte("<tx>"))
   416  			if v, ok := network.TransactionsRejected[x]; ok {
   417  				fmt.Fprint(w, "<id>", v.Id.String(), "</id>")
   418  				fmt.Fprint(w, "<time>", t.Unix(), "</time>")
   419  			} else {
   420  				fmt.Fprint(w, "<id>FATAL ERROR!!! This should not happen! Please report</id>")
   421  				fmt.Fprint(w, "<time>", time.Now().Unix(), "</time>")
   422  			}
   423  			w.Write([]byte("</tx>"))
   424  		}
   425  		w.Write([]byte("</wait4>"))
   426  	}
   427  	network.TxMutex.Unlock()
   428  	w.Write([]byte("</pending>"))
   429  }
   430  
   431  func raw_tx(w http.ResponseWriter, r *http.Request) {
   432  	if !ipchecker(r) {
   433  		return
   434  	}
   435  
   436  	defer func() {
   437  		if r := recover(); r != nil {
   438  			fmt.Fprintln(w, "Error")
   439  			if err, ok := r.(error); ok {
   440  				fmt.Fprintln(w, err.Error())
   441  			}
   442  		}
   443  	}()
   444  
   445  	if len(r.Form["id"]) == 0 {
   446  		fmt.Println("No id given")
   447  		return
   448  	}
   449  
   450  	txid := btc.NewUint256FromString(r.Form["id"][0])
   451  	fmt.Fprintln(w, "TxID:", txid.String())
   452  	if tx, ok := network.TransactionsToSend[txid.BIdx()]; ok {
   453  		s, _, _, _, _ := usif.DecodeTx(tx.Tx)
   454  		w.Write([]byte(s))
   455  	} else {
   456  		fmt.Fprintln(w, "Not found")
   457  	}
   458  }
   459  
   460  func json_txstat(w http.ResponseWriter, r *http.Request) {
   461  	if !ipchecker(r) {
   462  		return
   463  	}
   464  	w.Header()["Content-Type"] = []string{"application/json"}
   465  	w.Write([]byte("{"))
   466  
   467  	network.TxMutex.Lock()
   468  
   469  	w.Write([]byte(fmt.Sprint("\"t2s_cnt\":", len(network.TransactionsToSend), ",")))
   470  	w.Write([]byte(fmt.Sprint("\"t2s_size\":", network.TransactionsToSendSize, ",")))
   471  	w.Write([]byte(fmt.Sprint("\"tre_cnt\":", len(network.TransactionsRejected), ",")))
   472  	w.Write([]byte(fmt.Sprint("\"tre_size\":", network.TransactionsRejectedSize, ",")))
   473  	w.Write([]byte(fmt.Sprint("\"ptr1_cnt\":", len(network.TransactionsPending), ",")))
   474  	w.Write([]byte(fmt.Sprint("\"ptr2_cnt\":", len(network.NetTxs), ",")))
   475  	w.Write([]byte(fmt.Sprint("\"spent_outs_cnt\":", len(network.SpentOutputs), ",")))
   476  	w.Write([]byte(fmt.Sprint("\"awaiting_inputs\":", len(network.WaitingForInputs), ",")))
   477  	w.Write([]byte(fmt.Sprint("\"awaiting_inputs_size\":", network.WaitingForInputsSize, ",")))
   478  	w.Write([]byte(fmt.Sprint("\"min_fee_per_kb\":", common.MinFeePerKB(), "")))
   479  
   480  	network.TxMutex.Unlock()
   481  
   482  	w.Write([]byte("}\n"))
   483  }
   484  
   485  func txt_mempool_fees(w http.ResponseWriter, r *http.Request) {
   486  	if !ipchecker(r) {
   487  		return
   488  	}
   489  	w.Header()["Content-Type"] = []string{"text/plain"}
   490  	w.Write([]byte(usif.MemoryPoolFees()))
   491  }
   492  
   493  func json_mempool_stats(w http.ResponseWriter, r *http.Request) {
   494  	var division, maxweight uint64
   495  	var e error
   496  
   497  	if !ipchecker(r) {
   498  		return
   499  	}
   500  
   501  	network.TxMutex.Lock()
   502  	defer network.TxMutex.Unlock()
   503  
   504  	if len(r.Form["max"]) > 0 {
   505  		maxweight, e = strconv.ParseUint(r.Form["max"][0], 10, 64)
   506  		if e != nil {
   507  			maxweight = network.TransactionsToSendWeight
   508  		}
   509  	} else {
   510  		maxweight = network.TransactionsToSendWeight
   511  	}
   512  
   513  	if maxweight > network.TransactionsToSendWeight {
   514  		maxweight = network.TransactionsToSendWeight
   515  	}
   516  
   517  	if len(r.Form["div"]) > 0 {
   518  		division, e = strconv.ParseUint(r.Form["div"][0], 10, 64)
   519  		if e != nil {
   520  			division = maxweight / 100
   521  		}
   522  	} else {
   523  		division = maxweight / 100
   524  	}
   525  
   526  	if division < 100 {
   527  		division = 100
   528  	}
   529  
   530  	var sorted []*network.OneTxToSend
   531  	if len(r.Form["new"]) > 0 {
   532  		sorted = network.GetSortedMempoolNew()
   533  	} else {
   534  		sorted = network.GetSortedMempool()
   535  	}
   536  
   537  	type one_stat_row struct {
   538  		Txs_so_far        uint
   539  		Real_len_so_far   uint
   540  		Weight_so_far     uint
   541  		Current_tx_weight uint
   542  		Current_tx_spb    float64
   543  		Current_tx_id     string
   544  		Time_received     uint
   545  		Fees_so_far       uint64
   546  		Ord_weight_so_far uint
   547  		Ord_fees_so_far   uint64
   548  	}
   549  	var mempool_stats []one_stat_row
   550  
   551  	var totweight, reallen, totfee, ordweight, ordfees uint64
   552  	for cnt := 0; cnt < len(sorted); cnt++ {
   553  		v := sorted[cnt]
   554  		newtotweight := totweight + uint64(v.Weight())
   555  		reallen += uint64(len(v.Raw))
   556  		totfee += v.Fee
   557  		if yes, _ := v.ContainsOrdFile(true); yes {
   558  			ordweight += uint64(v.Weight())
   559  			ordfees += v.Fee
   560  		}
   561  
   562  		if cnt == 0 || cnt+1 == len(sorted) || (newtotweight/division) != (totweight/division) {
   563  			cur_spb := float64(v.Fee) / (float64(v.Weight() / 4.0))
   564  			mempool_stats = append(mempool_stats, one_stat_row{
   565  				Txs_so_far:        uint(cnt),
   566  				Real_len_so_far:   uint(reallen),
   567  				Weight_so_far:     uint(totweight),
   568  				Current_tx_weight: uint(v.Weight()),
   569  				Current_tx_spb:    cur_spb,
   570  				Current_tx_id:     v.Hash.String(),
   571  				Fees_so_far:       totfee,
   572  				Time_received:     uint(v.Firstseen.Unix()),
   573  				Ord_weight_so_far: uint(ordweight),
   574  				Ord_fees_so_far:   ordfees,
   575  			})
   576  		}
   577  		totweight = newtotweight
   578  		if totweight >= maxweight {
   579  			break
   580  		}
   581  	}
   582  
   583  	bx, er := json.Marshal(mempool_stats)
   584  	if er == nil {
   585  		w.Header()["Content-Type"] = []string{"application/json"}
   586  		w.Write(bx)
   587  	} else {
   588  		println(er.Error())
   589  	}
   590  }
   591  
   592  func json_mempool_fees(w http.ResponseWriter, r *http.Request) {
   593  	var division, maxweight uint64
   594  	var e error
   595  
   596  	if !ipchecker(r) {
   597  		return
   598  	}
   599  
   600  	network.TxMutex.Lock()
   601  	defer network.TxMutex.Unlock()
   602  
   603  	if len(r.Form["max"]) > 0 {
   604  		maxweight, e = strconv.ParseUint(r.Form["max"][0], 10, 64)
   605  		if e != nil {
   606  			maxweight = network.TransactionsToSendWeight
   607  		}
   608  	} else {
   609  		maxweight = network.TransactionsToSendWeight
   610  	}
   611  
   612  	if maxweight > network.TransactionsToSendWeight {
   613  		maxweight = network.TransactionsToSendWeight
   614  	}
   615  
   616  	if len(r.Form["div"]) > 0 {
   617  		division, e = strconv.ParseUint(r.Form["div"][0], 10, 64)
   618  		if e != nil {
   619  			division = maxweight / 100
   620  		}
   621  	} else {
   622  		division = maxweight / 100
   623  	}
   624  
   625  	if division < 1 {
   626  		division = 1
   627  	}
   628  
   629  	sorted := network.GetMempoolFees(maxweight)
   630  
   631  	var mempool_stats [][3]uint64
   632  	var totweight uint64
   633  	var totfeessofar uint64
   634  	for cnt := range sorted {
   635  		wgh := sorted[cnt][0]
   636  		fee := sorted[cnt][1]
   637  		totfeessofar += fee
   638  		newtotweight := totweight + wgh
   639  
   640  		if cnt == 0 || cnt+1 == len(sorted) || (newtotweight/division) != (totweight/division) {
   641  			mempool_stats = append(mempool_stats, [3]uint64{newtotweight, 4000 * fee / wgh, totfeessofar})
   642  		}
   643  		totweight = newtotweight
   644  	}
   645  
   646  	bx, er := json.Marshal(mempool_stats)
   647  	if er == nil {
   648  		w.Header()["Content-Type"] = []string{"application/json"}
   649  		w.Write(bx)
   650  	} else {
   651  		println(er.Error())
   652  	}
   653  }