123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- /*
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
- const API_PREFIX = "https://blockstream.info/api";
-
- const IRON_BLOCK = "https://gamepedia.cursecdn.com/minecraft_gamepedia/7/7e/Block_of_Iron_JE4_BE3.png?version=692673bafa1e94785ab6012d7a3c8dc4";
- const GOLD_BLOCK = "https://gamepedia.cursecdn.com/minecraft_gamepedia/7/72/Block_of_Gold_JE6_BE3.png?version=9d1a63c717df80feffa9ec93ebceb014";
-
- var nextHalvingHeight = 630000;
-
- var curTipBlock = null;
- var recentBlocks = null;
-
- function doApiReq(endpoint, cb) {
- let xhr = new XMLHttpRequest();
-
- xhr.timeout = 5000;
-
- xhr.onreadystatechange = function() {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
- let json = JSON.parse(xhr.responseText);
- cb(json);
- } else {
- // TODO Make this better?
- cb(null);
- }
- }
- };
-
- xhr.open("GET", API_PREFIX + endpoint);
- xhr.send()
- }
-
- function doGetRecentBlocks(cb) {
- doApiReq("/blocks/tip", cb);
- }
-
- function doGetBlockCoinBaseTx(blockhash, cb) {
- doApiReq("/block/" + blockhash + "/txs", function(resp) {
- if (resp != null) {
- cb(resp[0]);
- } else {
- cb(null);
- }
- });
- }
-
- function calcRenderedSatQty(sats, decimals) {
- let subBtc = sats % 100000000;
- let btc = (sats - subBtc) / 100000000;
-
- let maskModulus = 10 ** (8 - decimals);
- let subDecimalRem = subBtc % maskModulus;
- let decimal = (subBtc - subDecimalRem) / maskModulus;
-
- if (decimal != 0) {
- return btc.toString() + "." + decimal.toString();
- } else {
- return btc.toString();
- }
- }
-
- const HALVING_PERIOD = 210000;
-
- function calcRewardAtHeight(height) {
- let epoch = Math.floor(height / HALVING_PERIOD);
- return 5000000000 / (2 ** epoch);
- }
-
- function calcRewardBreakdown(height, sats) {
- let subsidy = calcRewardAtHeight(height);
- let fees = sats - subsidy;
-
- let rSubsidy = calcRenderedSatQty(subsidy, 2);
- let rFees = calcRenderedSatQty(fees, 3);
- return rSubsidy + " subsidy + " + rFees + " fees"
- }
-
- function toggleElemSelected(elem) {
- if (elem.classList.contains("blbselected")) {
- elem.classList.remove("blbselected");
- } else {
- elem.classList.add("blbselected");
- }
- }
-
- function makeBlockElem(block, prevBlock) {
- let entry = document.createElement("div");
-
- let innerElem = document.createElement("div");
- innerElem.classList.add("entryinner");
- entry.appendChild(innerElem);
-
- /* ===== Top row data ===== */
-
- let topElem = document.createElement("div");
- topElem.classList.add("entrytop");
- innerElem.appendChild(topElem);
-
- // Make the icon.
- let iconElem = document.createElement("img");
- iconElem.classList.add("blbicon");
- topElem.appendChild(iconElem);
- if (block.height % HALVING_PERIOD == 0) {
- iconElem.src = GOLD_BLOCK;
- } else {
- iconElem.src = IRON_BLOCK;
- }
-
- // Element for all the data.
- let dataElem = document.createElement("div");
- dataElem.classList.add("blbdata");
- topElem.appendChild(dataElem);
-
- // Make the height element.
- let heightElem = document.createElement("span");
- heightElem.classList.add("blbheight");
- heightElem.innerHTML = block.height.toString();
- dataElem.appendChild(heightElem);
-
- // Make the reward breakdown element.
- let rewardElem = document.createElement("span");
- rewardElem.innerHTML = "Reward: ? sat";
- dataElem.appendChild(rewardElem);
- doGetBlockCoinBaseTx(block.id, function(resp) {
- let reward = resp.vout[0].value;
- let rewardStr = calcRewardBreakdown(block.height, reward);
- rewardElem.innerHTML = rewardStr;
- });
-
- // Make the hash element.
- let hashElem = document.createElement("span");
- hashElem.innerHTML = block.id;
- hashElem.classList.add("blbhash");
- topElem.appendChild(hashElem);
-
- /* ==== Detail row data ===== */
-
- // Details
- let detailElem = document.createElement("div");
- detailElem.classList.add("entrydetail");
-
- // Weight and height
- let sizeElem = document.createElement("div");
- detailElem.appendChild(sizeElem);
- let blockKB = Math.floor(block.size / 1024);
- let blockkWU = Math.floor(block.weight / 1024);
- detailElem.innerHTML = "Weight/Size: " + blockKB + " KiB / " + blockkWU + " kSipa";
-
- // Number of transactions
- let txsElem = document.createElement("div");
- detailElem.appendChild(txsElem);
- txsElem.innerHTML = "Tx count: " + block.tx_count;
-
- // Make the duration element.
- if (prevBlock != null) {
- let timeElem = document.createElement("div");
- let blocktime = block.timestamp - prevBlock.timestamp;
- if (blocktime < 0) {
- blocktime = 0; // ehhhhhhhh
- }
- timeElem.innerHTML = "Since last block: " + blocktime.toString() + " sec";
- detailElem.appendChild(timeElem);
- } else {
- let oopsElem = document.createElement("div");
- oopsElem.innerHTML = "<br/>(I don't feel like making the request to get the previous block to find the blocktime)";
- detailElem.appendChild(oopsElem);
- }
-
- /* ===== Finalize ===== */
-
- // Put it together.
- innerElem.appendChild(detailElem);
- entry.appendChild(innerElem);
- entry.classList.add("blocklistentry");
- entry.onclick = function() {
- toggleElemSelected(entry);
- };
-
- return entry;
- }
-
- var blocksleftElem = null;
-
- function updateCurrentTip(block) {
- if (blocksleftElem == null) {
- blocksleftElem = document.getElementById("blocksleft");
- }
-
- blocksleftElem.innerHTML = (nextHalvingHeight - curTipBlock.height).toString()
- }
-
- function populateRecentBlocks(blocks) {
-
- let blocklist = document.getElementById("blocklist");
- let loadingElem = document.getElementById("blocklistloading");
-
- curTipBlock = blocks[0];
- updateCurrentTip(curTipBlock.height);
-
- for (let i = 0; i < blocks.length; i++) {
- if (i < blocks.length - 1) {
- let elem = makeBlockElem(blocks[i], blocks[i + 1]);
- blocklist.appendChild(elem);
- } else {
- let elem = makeBlockElem(blocks[i], null);
- blocklist.appendChild(elem);
- }
- }
-
- loadingElem.style.display = "none";
- }
-
- function init() {
- console.log("Hello!");
-
- let checkHeights = [0, 1, 2, 209999, 210000, 210001, 629999, 630000, 630001];
- for (let i = 0; i < checkHeights.length; i++) {
- console.log("Reward at " + checkHeights[i] + " is " + calcRewardAtHeight(checkHeights[i]));
- }
-
- doGetRecentBlocks(function(blocks) {
- if (blocks == null) {
- alert("Error loading blocks, please refresh the page.");
- return;
- }
-
- recentBlocks = blocks;
-
- populateRecentBlocks(blocks);
-
- // TODO Set up more stuff.
- });
- }
|