github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/www/templates/home.html (about) 1 <script type="text/javascript" src="webui/jquery.min.js"></script> 2 <script type="text/javascript" src="webui/jquery.flot.min.js"></script> 3 <script type="text/javascript" src="webui/jquery.flot.time.min.js"></script> 4 <script type="text/javascript" src="webui/jquery.flot.crosshair.js"></script> 5 6 <!-- include fees_chart.html --> 7 8 <table width="100%" border="0"> 9 <colgroup> 10 <col width="50%"> 11 <col width="50%"> 12 </colgroup> 13 14 <tr><td colspan="2" style="padding:0px"> 15 <div align="right" style="float:right;font-size:12px;padding-top:0px"> 16 WebUI Settings: <input style="display:none" type="file" id="import_file" accept=".json"> 17 <a href="javascript:export_settings()"><img valign="bottom" title="Backup" src="webui/backup.png"> Export</a> 18 19 <a href="javascript:import_file_click()"><img valign="bottom" title="Restore" src="webui/restore.png"> Import</a> 20 </div> 21 <h2 style="margin-top:10px;margin-bottom:5px">Recent Blocks</h2> 22 23 <table width="100%"><tr style="border:0"> 24 <td style="padding:0"> 25 <span onclick="chart_type_spb.click()" class="hand"> 26 <input type="radio" name="chart_type" id="chart_type_spb" onchange="draw_chart()" onclick="event.stopPropagation()"> Average SPB 27 </span> 28 • 29 <span onclick="chart_type_siz.click()" class="hand"> 30 <input type="radio" name="chart_type" id="chart_type_siz" onchange="draw_chart()" onclick="event.stopPropagation()"> Blocksize KB 31 </span> 32 • 33 <span onclick="chart_type_txs.click()" class="hand"> 34 <input type="radio" name="chart_type" id="chart_type_txs" onchange="draw_chart()" onclick="event.stopPropagation()"> Transactions 35 </span> 36 • 37 <span onclick="chart_type_wgt.click()" class="hand"> 38 <input type="radio" name="chart_type" id="chart_type_wgt" onchange="draw_chart()" onclick="event.stopPropagation()"> Weight 39 </span> 40 • 41 42 <span onclick="chart_type_avg.click()" class="hand" style="display:inline"> 43 <input type="checkbox" id="chart_type_avg" onchange="draw_chart()" onclick="event.stopPropagation()"> 44 <span id="avg_size">.</span> bl. avg 45 </span> 46 47 <span onclick="chart_type_ord.click()" class="hand" title="See how much is used by Ordinal Inscriptions" id="ord_label"> 48 <input type="checkbox" id="chart_type_ord" onchange="draw_chart()" onclick="event.stopPropagation()"> 49 Ord. % 50 </span> 51 52 </td> 53 <td align="right" style="padding:0px"> 54 <table> 55 <tr style="border:0"><td style="padding-top:0px;padding-bottom:0px;" >Average: 56 <td align="right" style="padding-top:0px;padding-bottom:0px;" nowrap="nowrap" title="Mining hash rate"><a href="http://bitcoin.sipa.be/" target="_blank"><span id="si_network_hashrate"></span></a> 57 <td align="right" style="padding-top:0px;padding-bottom:0px;" title="Transaction fee"><a href="https://mempool.space/" target="_blank"><span id="si_avg_fee_spb"></span> SPB</a> 58 <td align="right" style="padding-top:0px;padding-bottom:0px;" title="Block size"><a href="https://www.blockchain.com/explorer/charts/avg-block-size" target="_blank" id="si_avg_block_size"></a> 59 </table> 60 61 </td> 62 </tr></table> 63 <div id="chart" style="height:200px;margin-left:0px;margin-right:5px;margin-top:0px;margin-bottom:5px"></div> 64 65 66 <tr><td colspan="2"> 67 <h2>Last Block</h2> 68 <table border="0" width="100%"> 69 <tr><td align="right" class="nw">Block Hash:<td colspan="7"><b id="last_block_hash"></b> 70 <td align="right" class="nw">Last Header: 71 <td><b title="Last known header" id="si_last_hdr_height"></b> 72 73 <tr> 74 <td align="right" colspan="1">Version: 75 <td colspan="1"><b id="last_block_version"></b> 76 <td align="right">Received: 77 <td><b id="last_block_received"></b> 78 <td align="right">Timestamp: 79 <td class="nw"><b id="last_block_timestamp"></b> 80 <td align="right">Median: 81 <td class="nw"><b id="last_block_median"></b> 82 <td align="right">Difficulty: 83 <td><b id="last_block_difficulty"></b> 84 </table> 85 </td> 86 </tr> 87 88 <tr><td valign="top"> 89 <h2>Network</h2> 90 <table width="90%"> 91 <colgroup> 92 <col width="25%"> 93 <col width="20%"> 94 <col width="25%"> 95 <col width="30%"> 96 </colgroup> 97 <tr><td nowrap="nowrap">Connections: 98 <td align="right"><b id="bw_open_conns_total"></b> 99 <td align="right" class="nw"><img title="outgoing" src="webui/outgoing.png"> <b id="bw_open_conns_out"></b> 100 <td align="right" class="nw"><img title="incoming" src="webui/incoming.png"> <b id="bw_open_conns_in"></b> 101 <tr><td>Downloading: 102 <td align="right" class="nw"><b id="bw_dl_speed_now"></b> KB/s 103 <td align="right" class="nw"><b id="bw_dl_speed_max"></b> KB/s max 104 <td align="right" class="nw"><b id="bw_dl_total"></b> tot 105 <tr><td>Uploading: 106 <td align="right" class="nw"><b id="bw_ul_speed_now"></b> KB/s 107 <td align="right" class="nw"><b id="bw_ul_speed_max"></b> KB/s max 108 <td align="right" class="nw"><b id="bw_ul_total"></b> tot 109 <tr><td>External IP: 110 <td colspan="1" align="right" id="external_ip0" style="font-weight:bold"> 111 <td colspan="2" align="right" id="external_others" style="font-size:70%;font-style:italic;overlay-x:"> 112 <tr title="Public Authorization Key"><td>Auth Key: 113 <td colspan="3" align="left" class="small"><b>@<!--PUB_AUTH_KEY--></b> 114 </table> 115 <td valign="top"> 116 <h2>Node</h2> 117 <table width="100%"> 118 <tr><td>Uptime:<td> 119 <b id="si_node_uptime"></b> 120 • 121 <b title="Known peers" id="si_known_peers"></b> peers 122 • 123 <b title="ECDSA verify operations" id="si_ecdsa_verify_cnt"></b> ECs 124 <tr><td>Mem Used MB:<td> 125 <b title="Total memory used" id="si_total_mem"></b> 126 (<b title="Native Go heap" id="si_heap_size"></b> + 127 <b title="QDB UTXO records" id="si_qdb_extramem"></b>) 128 <span id="el_free_mem" style="display:none">[<a href="javascript:config('freemem')">FREE</a>]</span> 129 130 <tr><td nowrap="nowrap">TX Mempool:<td aligh="left"> 131 <b title="Accepted" id="ts_t2s_size"></b> 132 • 133 <b title="Rejected" id="ts_tre_size" style="font-weight:bold"></b> 134 • 135 <b title="UTXOs spent in memory" id="outspent" style="font-weight:bold"></b> 136 • 137 <b title="Minimum SPB" id="min_spb" style="font-weight:bold"></b> spb 138 <tr><td>Pending Data:<td><b id="si_net_tx_qsize"></b> txs, 139 <b id="si_net_block_qsize"></b> blocks, 140 <b id="si_blocks_cached"></b> <span id="si_blocks_cached_label"></span>, 141 <b id="si_blocks_to_get"></b> to get 142 <img id="si_saving" src="webui/saving.png" style="float:right;display:none" title="Saving UTXO.db in progress"> 143 </tr> 144 <tr id="showcfg" style="display:none"> 145 146 <form method="post" style="margin:0" action="cfg" onsubmit="return confirm('Are you sure that you want to shut down this node?');"> 147 <td align="left" style="padding:0px"><input type="button" value="Edit configuration" onclick="shwcfg()"> 148 <td align="right" style="padding:0px"> 149 <input type="button" value="Save configuration" onclick="savecfg()"> 150 <input type="hidden" name="shutdown" value="1"> 151 <input type="submit" value="Shutdown Node"> 152 </form> 153 </td> 154 155 </tr> 156 </table> 157 </table> 158 159 <div id="formcfg" style="display:none"> 160 <h2>Configuration</h2> 161 <table width="100%"> 162 <form method="post" action="cfg"> 163 <tr><td colspan="2"align="left">See <a href="http://gocoin.pl/gocoin_manual_client_config.html" target="_blank">help page</a> for the format of the configuration data. 164 <tr><td colspan="2"> 165 <textarea name="configjson" id="configjson" style="width:100%" rows="26">{CONFIG_FILE}</textarea> 166 <tr><td align="center"> 167 <i>Please note that some config values require a restart in order to be applied. 168 <td align="right"> 169 <input type="button" value="Cancel" onclick="location.reload()"> 170 <input type="submit" name="apply" value="Apply"> 171 <input type="submit" name="save" value="Apply & Save"> 172 </form> 173 </table> 174 </div> 175 176 <script> 177 if (!server_mode) { 178 showcfg.style.display='table-row' 179 el_free_mem.style.display='inline' 180 } 181 182 183 var previousPoint = null; 184 var cs 185 186 const blks_average = 6 187 188 function restore_chart_setting() { 189 var mod = localStorage.getItem("home_chart") 190 if ((mod+'').length!=3) mod = "spb" 191 document.getElementById('chart_type_'+mod).checked = true 192 chart_type_avg.checked = localStorage.getItem("home_chart_avg") === 'true' 193 chart_type_ord.checked = localStorage.getItem("home_chart_ord") === 'true' 194 avg_size.innerText = blks_average 195 } 196 restore_chart_setting() 197 var chart_type_ord_prev = chart_type_ord.checked 198 199 function showTooltip(x, y, contents) { 200 $('<div id="tooltip">' + contents + '</div>').css( { 201 position: 'absolute', 202 display: 'none', 203 top: y - 30, 204 left: x + 5, 205 border: '2px solid #fdd', 206 padding: '5px', 207 'font-size' : '14px', 208 'background-color': '#fee', 209 opacity: 1 210 }).appendTo("body").fadeIn(200); 211 } 212 213 function handlehover(event, pos, item) { 214 if (item) { 215 if (previousPoint != item.dataIndex) { 216 previousPoint = item.dataIndex; 217 $("#tooltip").remove(); 218 219 var rec = cs[item.dataIndex] 220 var str = 'Block #'+rec.Height+', Version 0x'+rec.Version.toString(16)+'<br>' 221 str += tim2str(rec.Received)+' ... '+tim2str(rec.Timestamp, true)+'<br>' 222 str += ''+rec.TxCnt+' transactions / '+rec.Size+' bytes<br>' 223 str += val2str(rec.Reward)+' BTC / ' + rec.FeeSPB.toFixed(2)+' SPB<br>' 224 str += 'Block Weight: ' + rec.Weight + "<br>" 225 str += 'Mined by ' + rec.Miner + '<br>' 226 str += 'Ords use ' + (100*rec.OrdCnt/rec.TxCnt).toFixed(0) + '% txs, ' 227 str += (100*rec.OrdSize/rec.Size).toFixed(0) + '% size, ' 228 str += (100*rec.OrdWeight/rec.Weight).toFixed(0) + '% of weight' 229 //if (rec.Weight > 0) 230 231 showTooltip(item.pageX, item.pageY, str) 232 } 233 } else { 234 $("#tooltip").remove(); 235 previousPoint = null; 236 } 237 } 238 239 240 function handleclick(event, pos, item) { 241 if (item) { 242 var rec = cs[item.dataIndex] 243 if (rec.HaveFeeStats) { 244 show_block_fees(rec.Height,rec.Size,rec.Miner.substr(0,12)) 245 } else { 246 //alert("No fee stats for this block") 247 } 248 } 249 } 250 251 function draw_chart() { 252 // check if would have both SPB and Ord... 253 if (chart_type_spb.checked && chart_type_ord.checked) { 254 if (!chart_type_ord_prev) { 255 chart_type_wgt.checked = true // if clicking Ord: switch to Weigth 256 } else { 257 chart_type_ord.checked = false // clicking SPB: disable Ord 258 } 259 } 260 chart_type_ord_prev = chart_type_ord.checked 261 262 var aj = ajax() 263 aj.onerror=function() { 264 setTimeout(draw_chart, 1000) 265 } 266 267 aj.onload=function() { 268 try { 269 _cs = JSON.parse(aj.responseText) 270 var plot_data = [ { data : [], points: { show:true, fill:true }, lines: {show:true, fill:true}}, 271 { data : [], bars:{ show:true, fill:true, lineWidth:5}, color:"black" } ]; 272 var plot_options = { 273 grid: { hoverable: true, clickable: true, markings:[] }, 274 xaxis: { mode: 'time', timeformat: "%H:%M", timezone: "browser" }, 275 yaxis : {labelWidth : 30, min : 0, position : "right"} 276 } 277 278 if (chart_type_siz.checked) { 279 plot_data[0].color = 3 280 //plot_options.yaxis.max = 1100 281 //plot_options.yaxis.tickFormatter = function(a,b) {return (a/1e3).toFixed(1)+'M'} 282 localStorage.setItem("home_chart", "siz") 283 } else if (chart_type_spb.checked) { 284 plot_data[0].color = 2 285 localStorage.setItem("home_chart", "spb") 286 } else if (chart_type_wgt.checked) { 287 plot_data[0].color = 5 288 localStorage.setItem("home_chart", "wgt") 289 } else { 290 plot_data[0].color = 4 291 localStorage.setItem("home_chart", "txs") 292 } 293 localStorage.setItem("home_chart_avg", chart_type_avg.checked) 294 localStorage.setItem("home_chart_ord", chart_type_ord.checked) 295 296 //console.log(_cs) 297 cs = new Array() 298 for (var i=0; i<_cs.length; i++) { 299 if (_cs[i].TxCnt==1) { 300 //plot_data[1].data.push([_cs[i].Timestamp*1000, 100]) 301 var tt = _cs[i].Timestamp*1000 302 plot_options.grid.markings.push({ xaxis: { from: tt, to: tt+240000 }, color: "#f0f0d0" }) 303 continue // ignore blocks with a single conbase tx 304 } 305 cs.push(_cs[i]) 306 if (chart_type_avg.checked) { 307 var sum=0, avgcnt=0, sumord=0; 308 for (var ii=i; avgcnt<blks_average && ii>=0; ii--) { 309 if (_cs[i].TxCnt==1) { 310 continue 311 } 312 avgcnt++; 313 if (chart_type_siz.checked) { 314 sum += _cs[ii].Size/1000.0 315 sumord += _cs[ii].OrdSize/1000.0 316 } else if (chart_type_spb.checked) { 317 sum += _cs[ii].FeeSPB 318 } else if (chart_type_wgt.checked) { 319 sum += _cs[ii].Weight 320 sumord += _cs[ii].OrdWeight 321 } else { 322 sum += _cs[ii].TxCnt 323 sumord += _cs[ii].OrdCnt 324 } 325 } 326 if (avgcnt>0) { 327 if (chart_type_spb.checked || !chart_type_ord.checked) { 328 plot_data[0].data.push([_cs[i].Timestamp*1000, sum/avgcnt]) 329 } else { 330 plot_data[0].data.push([_cs[i].Timestamp*1000, 100*sumord/sum]) 331 } 332 } 333 } else { 334 if (chart_type_siz.checked) { 335 if (chart_type_ord.checked) { 336 plot_data[0].data.push([_cs[i].Timestamp*1000, 100*_cs[i].OrdSize/_cs[i].Size]) 337 } else { 338 plot_data[0].data.push([_cs[i].Timestamp*1000, _cs[i].Size/1000.0]) 339 } 340 } else if (chart_type_spb.checked) { 341 plot_data[0].data.push([_cs[i].Timestamp*1000, _cs[i].FeeSPB]) 342 } else if (chart_type_wgt.checked) { 343 if (chart_type_ord.checked) { 344 plot_data[0].data.push([_cs[i].Timestamp*1000, 100*_cs[i].OrdWeight/_cs[i].Weight]) 345 } else { 346 plot_data[0].data.push([_cs[i].Timestamp*1000, _cs[i].Weight]) 347 } 348 } else { 349 if (chart_type_ord.checked) { 350 plot_data[0].data.push([_cs[i].Timestamp*1000, 100*_cs[i].OrdCnt/_cs[i].TxCnt]) 351 } else { 352 plot_data[0].data.push([_cs[i].Timestamp*1000, _cs[i].TxCnt]) 353 } 354 } 355 } 356 } 357 358 plot_options.yaxis.max = chart_type_ord.checked ? 100 : null 359 $("#chart").bind("plothover", handlehover) 360 $("#chart").bind("plotclick", handleclick) 361 $.plot($("#chart"), plot_data, plot_options) 362 363 } catch(e) { 364 console.log(e) 365 } 366 } 367 aj.open("GET","blocks.json",true) 368 aj.send(null) 369 } 370 371 372 var last_block_height = -1 373 374 function shwcfg() { 375 showcfg.style.display='none' 376 formcfg.style.display='block' 377 window.scrollTo(0,document.body.scrollHeight) 378 } 379 380 blno.addEventListener("lastblock", function(e) { 381 var stat = e.block 382 if (last_block_height != stat.Height) { 383 last_block_hash.innerText = stat.Hash 384 last_block_timestamp.innerText = tim2str(stat.Timestamp) 385 last_block_height = stat.Height 386 last_block_difficulty.innerText = bignum(stat.Diff) 387 last_block_median.innerText = tim2str(stat.Median) 388 last_block_version.innerText = '0x' + leftpad(stat.Version.toString(16), '0', 8) 389 draw_chart() 390 } 391 var ago = stat.Time_now - stat.Received 392 if (ago<120) { 393 last_block_received.innerText = ago + ' sec ago' 394 } else if (ago<2*3600) { 395 last_block_received.innerText = (ago/60.0).toFixed(1) + ' min ago' 396 } 397 }) 398 399 </script> 400 401 <script> 402 function refreshsysinfo() { 403 var aj = ajax() 404 aj.onerror=function() { 405 setTimeout(refreshsysinfo, 5000) 406 } 407 aj.onload=function() { 408 try { 409 var si = JSON.parse(aj.responseText) 410 si_known_peers.innerText = si.Known_peers 411 si_total_mem.innerText = ((si.Heap_size+si.Qdb_extramem)/0x100000).toFixed(0) 412 el_free_mem.title = (si.Heap_sysmem/0x100000).toFixed(0) 413 si_heap_size.innerText = (si.Heap_size/0x100000).toFixed(0) 414 si_qdb_extramem.innerText = (si.Qdb_extramem/0x100000).toFixed(0) 415 si_net_block_qsize.innerText = si.Net_block_qsize 416 si_net_tx_qsize.innerText = si.Net_tx_qsize 417 si_ecdsa_verify_cnt.innerText = si.Ecdsa_verify_cnt 418 si_avg_block_size.innerText = (si.Average_block_size/1000).toFixed(1) + ' kB' 419 si_avg_fee_spb.innerText = si.Average_fee.toFixed(0) 420 if (si.Blocks_on_disk>0) { 421 si_blocks_cached.innerText = si.Blocks_on_disk 422 si_blocks_cached_label.innerText = "on disk" 423 } else { 424 si_blocks_cached.innerText = si.Blocks_cached 425 si_blocks_cached_label.innerText = "cached" 426 } 427 si_blocks_to_get.innerText = si.BlocksToGet 428 si_node_uptime.innerText = period2str(si.Node_uptime) 429 si_last_hdr_height.innerText = si.LastHeaderHeight 430 si_network_hashrate.innerText = bignum(si.NetworkHashRate) +'H/s' 431 si_saving.style.display = si.SavingUTXO ? "block" : "none" 432 } catch(e) { 433 console.log(e) 434 } 435 setTimeout(refreshsysinfo, 1000) 436 } 437 aj.open("GET","system.json",true) 438 aj.send(null) 439 } 440 refreshsysinfo() 441 </script> 442 443 <script> 444 function refreshbwinfo() { 445 var aj = ajax() 446 aj.onerror=function() { 447 setTimeout(refreshbwinfo, 5000) 448 } 449 aj.onload=function() { 450 try { 451 var bw = JSON.parse(aj.responseText) 452 bw_open_conns_total.innerText = bw.Open_conns_total 453 bw_open_conns_out.innerText = bw.Open_conns_out 454 bw_open_conns_in.innerText = bw.Open_conns_in 455 bw_dl_speed_now.innerText = bw.Dl_speed_now >> 10 456 bw_dl_speed_max.innerText = bw.Dl_speed_max >> 10 457 bw_dl_total.innerText = bignum(bw.Dl_total)+'B' 458 bw_ul_speed_now.innerText = bw.Ul_speed_now >> 10 459 bw_ul_speed_max.innerText = bw.Ul_speed_max >> 10 460 bw_ul_total.innerText = bignum(bw.Ul_total)+'B' 461 462 // external IP 463 if (bw.ExternalIP.length==0) { 464 external_ip0.innerText = '?' 465 external_ip0.title = '' 466 external_others.innerHTML = '' 467 } else { 468 var ht='' 469 if (bw.ExternalIP.length>1) { 470 for (var i=1; i<bw.ExternalIP.length; i++) { 471 if (i>1) ht += ', ' 472 ht += '<span title="'+bw.ExternalIP[i].Count+' times, last at '+tim2str(bw.ExternalIP[i].Timestamp)+ 473 '">' + bw.ExternalIP[i].Ip + '</span>' 474 if (i==2 && bw.ExternalIP.length>=3) { 475 ht+= ', <span title="' + (bw.ExternalIP.length-3) + ' more">...</span>' 476 break 477 } 478 } 479 } 480 external_ip0.innerText = bw.ExternalIP[0].Ip 481 external_ip0.title = bw.ExternalIP[0].Count+' times, last at '+tim2str(bw.ExternalIP[0].Timestamp) 482 external_others.innerHTML = ht 483 } 484 } catch(e) { 485 console.log(e) 486 } 487 setTimeout(refreshbwinfo, 1000) 488 } 489 aj.open("GET","bwidth.json",true) 490 aj.send(null) 491 } 492 refreshbwinfo() 493 494 495 function refreshtxstat() { 496 var aj = ajax() 497 aj.onerror=function() { 498 setTimeout(refreshtxstat, 5000) 499 } 500 aj.onload=function() { 501 try { 502 var ts = JSON.parse(aj.responseText) 503 ts_t2s_size.innerText = bignum(ts.t2s_size)+'B' 504 ts_t2s_size.title = ts.t2s_cnt + ' transactions accepted' 505 outspent.innerText = ts.spent_outs_cnt 506 ts_tre_size.innerText = bignum(ts.tre_size)+'B' 507 ts_tre_size.title = ts.tre_cnt + ' transactions rejected' 508 min_spb.innerText = (ts.min_fee_per_kb/1000.0).toFixed(1) 509 } catch(e) { 510 console.log(e) 511 } 512 setTimeout(refreshtxstat, 1000) 513 } 514 aj.open("GET","txstat.json",true) 515 aj.send(null) 516 } 517 refreshtxstat() 518 519 function export_settings() { 520 var jsonstring = JSON.stringify(localStorage) 521 var a = document.createElement("a") 522 var file = new Blob([jsonstring], {type: 'application/json'}) 523 a.href = URL.createObjectURL(file) 524 a.download = "gocoin-config.json" 525 a.click() 526 } 527 528 function import_file_click() { 529 import_file.click() 530 } 531 532 function import_file_selected(e) { 533 var f = import_file.files[0] 534 var reader = new FileReader() 535 reader.onload = function(e) { 536 //console.log('aac', e, e.target.result) 537 try { 538 var sets = JSON.parse(e.target.result) 539 var wlts = '' 540 localStorage.clear() 541 for (var nam in sets) { 542 localStorage.setItem(nam, sets[nam]) 543 } 544 location.reload() 545 } catch (ex) { 546 console.log(ex) 547 } 548 }; 549 reader.readAsText(f) 550 } 551 552 document.addEventListener('DOMContentLoaded', function() { 553 window.onkeyup = function (event) { 554 if(event.keyCode == 27) closepopup() 555 } 556 import_file.addEventListener('change', import_file_selected, false); 557 }) 558 559 </script>