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 }