|
|
@@ -4,13 +4,19 @@ |
|
|
|
|
|
|
|
const API_PREFIX = "https://blockstream.info/api"; |
|
|
|
|
|
|
|
let nextHalvingHeight = 630000; |
|
|
|
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) { |
|
|
@@ -41,70 +47,153 @@ function doGetBlockCoinBaseTx(blockhash, cb) { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
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 elem = document.createElement("div"); |
|
|
|
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.innerHTML = block.height.toString(); |
|
|
|
heightElem.classList.add("blbheight"); |
|
|
|
elem.appendChild(heightElem); |
|
|
|
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"); |
|
|
|
elem.appendChild(hashElem); |
|
|
|
topElem.appendChild(hashElem); |
|
|
|
|
|
|
|
// Make the reward breakdown element. |
|
|
|
let rewardElem = document.createElement("span"); |
|
|
|
rewardElem.innerHTML = "Reward: ? sat"; |
|
|
|
rewardElem.classList.add("blbreward"); |
|
|
|
elem.appendChild(rewardElem); |
|
|
|
/* ==== Detail row data ===== */ |
|
|
|
|
|
|
|
// Invoke the thing to populate the reward. |
|
|
|
doGetBlockCoinBaseTx(block.id, function(resp) { |
|
|
|
let reward = resp.vout[0].value; |
|
|
|
// Details |
|
|
|
let detailElem = document.createElement("div"); |
|
|
|
detailElem.classList.add("entrydetail"); |
|
|
|
|
|
|
|
let rewardSubBtc = reward % 100000000; |
|
|
|
let rewardBtc = (reward - rewardSubBtc) / 100000000; |
|
|
|
let rewardBtcStr = rewardBtc.toString() + "." + rewardSubBtc.toString(); |
|
|
|
// 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"; |
|
|
|
|
|
|
|
rewardElem.innerHTML = "Reward: " + rewardBtcStr + " BTC"; |
|
|
|
}); |
|
|
|
// Number of transactions |
|
|
|
let txsElem = document.createElement("div"); |
|
|
|
detailElem.appendChild(txsElem); |
|
|
|
txsElem.innerHTML = "Tx count: " + block.tx_count; |
|
|
|
|
|
|
|
// Make the duration element. |
|
|
|
let timeElem = document.createElement("span"); |
|
|
|
if (prevBlock != null) { |
|
|
|
let timeElem = document.createElement("div"); |
|
|
|
let blocktime = block.timestamp - prevBlock.timestamp; |
|
|
|
timeElem.innerHTML = blocktime.toString() + " sec"; |
|
|
|
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); |
|
|
|
} |
|
|
|
timeElem.classList.add("blbtime"); |
|
|
|
elem.appendChild(timeElem); |
|
|
|
|
|
|
|
// Add the class. |
|
|
|
elem.classList.add("blocklistblock"); |
|
|
|
/* ===== Finalize ===== */ |
|
|
|
|
|
|
|
return elem; |
|
|
|
// 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"); |
|
|
|
} |
|
|
|
|
|
|
|
curHeight = block.height; |
|
|
|
blocksleftElem.innerHTML = (nextHalvingHeight - curTipBlock.height).toString() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
function populateRecentBlocks(blocks) { |
|
|
|
|
|
|
|
console.log(blocks); |
|
|
|
|
|
|
|
let blocklist = document.getElementById("blocklist"); |
|
|
|
let loadingElem = document.getElementById("blocklistloading"); |
|
|
|
|
|
|
@@ -112,7 +201,6 @@ function populateRecentBlocks(blocks) { |
|
|
|
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); |
|
|
@@ -120,21 +208,29 @@ function populateRecentBlocks(blocks) { |
|
|
|
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. |
|
|
|
}) |
|
|
|
|
|
|
|
}); |
|
|
|
} |