|
|
@@ -0,0 +1,140 @@ |
|
|
|
/* |
|
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later |
|
|
|
*/ |
|
|
|
|
|
|
|
const API_PREFIX = "https://blockstream.info/api"; |
|
|
|
|
|
|
|
let nextHalvingHeight = 630000; |
|
|
|
|
|
|
|
var curTipBlock = null; |
|
|
|
|
|
|
|
function doApiReq(endpoint, cb) { |
|
|
|
let xhr = new XMLHttpRequest(); |
|
|
|
|
|
|
|
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 makeBlockElem(block, prevBlock) { |
|
|
|
let elem = document.createElement("div"); |
|
|
|
|
|
|
|
// Make the height element. |
|
|
|
let heightElem = document.createElement("span"); |
|
|
|
heightElem.innerHTML = block.height.toString(); |
|
|
|
heightElem.classList.add("blbheight"); |
|
|
|
elem.appendChild(heightElem); |
|
|
|
|
|
|
|
// Make the hash element. |
|
|
|
let hashElem = document.createElement("span"); |
|
|
|
hashElem.innerHTML = block.id; |
|
|
|
hashElem.classList.add("blbhash"); |
|
|
|
elem.appendChild(hashElem); |
|
|
|
|
|
|
|
// Make the reward breakdown element. |
|
|
|
let rewardElem = document.createElement("span"); |
|
|
|
rewardElem.innerHTML = "Reward: ? sat"; |
|
|
|
rewardElem.classList.add("blbreward"); |
|
|
|
elem.appendChild(rewardElem); |
|
|
|
|
|
|
|
// Invoke the thing to populate the reward. |
|
|
|
doGetBlockCoinBaseTx(block.id, function(resp) { |
|
|
|
let reward = resp.vout[0].value; |
|
|
|
|
|
|
|
let rewardSubBtc = reward % 100000000; |
|
|
|
let rewardBtc = (reward - rewardSubBtc) / 100000000; |
|
|
|
let rewardBtcStr = rewardBtc.toString() + "." + rewardSubBtc.toString(); |
|
|
|
|
|
|
|
rewardElem.innerHTML = "Reward: " + rewardBtcStr + " BTC"; |
|
|
|
}); |
|
|
|
|
|
|
|
// Make the duration element. |
|
|
|
let timeElem = document.createElement("span"); |
|
|
|
if (prevBlock != null) { |
|
|
|
let blocktime = block.timestamp - prevBlock.timestamp; |
|
|
|
timeElem.innerHTML = blocktime.toString() + " sec"; |
|
|
|
} |
|
|
|
timeElem.classList.add("blbtime"); |
|
|
|
elem.appendChild(timeElem); |
|
|
|
|
|
|
|
// Add the class. |
|
|
|
elem.classList.add("blocklistblock"); |
|
|
|
|
|
|
|
return elem; |
|
|
|
} |
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
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!"); |
|
|
|
|
|
|
|
doGetRecentBlocks(function(blocks) { |
|
|
|
populateRecentBlocks(blocks); |
|
|
|
|
|
|
|
// TODO Set up more stuff. |
|
|
|
}) |
|
|
|
|
|
|
|
} |